From 457e5ecae625c41327939031b7dc408ae763b836 Mon Sep 17 00:00:00 2001 From: wangr <raoli.wang@unige.ch> Date: Thu, 1 Nov 2018 17:11:30 +0100 Subject: [PATCH 01/14] feat: add shibboleth security implementation --- pom.xml | 698 +++++++++--------- solidify-auth/pom.xml | 50 ++ .../ch/unige/solidify/auth/Authenticated.java | 24 + .../solidify/auth/SsoAuthentication.java | 55 ++ .../auth/SsoAuthenticationFilter.java | 50 ++ .../unige/solidify/auth/SsoUserDetails.java | 99 +++ .../java/ch/unige/solidify/auth/User.java | 222 ++++++ .../ch/unige/solidify/auth/UserFactory.java | 31 + .../auth/config/SsoAppRoleConfig.java | 71 ++ .../solidify/auth/oidc/OidcUserFactory.java | 17 + .../shibboleth/ShibbolethHeaderConfig.java | 160 ++++ .../shibboleth/ShibbolethUserFactory.java | 262 +++++++ .../MockSsoUserSecurityContextFactory.java | 72 ++ .../ch/unige/solidify/auth/util/UserData.java | 126 ++++ .../solidify/auth/util/WithSsoMockUser.java | 53 ++ .../resources/ShibboHeaderName.properties | 15 + .../java/ch/unige/solidify/model/IUser.java | 13 + .../main/java/ch/unige/solidify/IEmpty.java | 16 + .../java/ch/unige/solidify/util/Pair.java | 33 + 19 files changed, 1723 insertions(+), 344 deletions(-) create mode 100644 solidify-auth/pom.xml create mode 100644 solidify-auth/src/main/java/ch/unige/solidify/auth/Authenticated.java create mode 100644 solidify-auth/src/main/java/ch/unige/solidify/auth/SsoAuthentication.java create mode 100644 solidify-auth/src/main/java/ch/unige/solidify/auth/SsoAuthenticationFilter.java create mode 100644 solidify-auth/src/main/java/ch/unige/solidify/auth/SsoUserDetails.java create mode 100644 solidify-auth/src/main/java/ch/unige/solidify/auth/User.java create mode 100644 solidify-auth/src/main/java/ch/unige/solidify/auth/UserFactory.java create mode 100644 solidify-auth/src/main/java/ch/unige/solidify/auth/config/SsoAppRoleConfig.java create mode 100644 solidify-auth/src/main/java/ch/unige/solidify/auth/oidc/OidcUserFactory.java create mode 100644 solidify-auth/src/main/java/ch/unige/solidify/auth/shibboleth/ShibbolethHeaderConfig.java create mode 100644 solidify-auth/src/main/java/ch/unige/solidify/auth/shibboleth/ShibbolethUserFactory.java create mode 100644 solidify-auth/src/main/java/ch/unige/solidify/auth/util/MockSsoUserSecurityContextFactory.java create mode 100644 solidify-auth/src/main/java/ch/unige/solidify/auth/util/UserData.java create mode 100644 solidify-auth/src/main/java/ch/unige/solidify/auth/util/WithSsoMockUser.java create mode 100644 solidify-auth/src/main/resources/ShibboHeaderName.properties create mode 100644 solidify-model/src/main/java/ch/unige/solidify/model/IUser.java create mode 100644 solidify-util/src/main/java/ch/unige/solidify/IEmpty.java create mode 100644 solidify-util/src/main/java/ch/unige/solidify/util/Pair.java diff --git a/pom.xml b/pom.xml index ddbc37707..23f65ac6e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,355 +1,365 @@ -<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> - <parent> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-parent</artifactId> - <version>2.0.6.RELEASE</version> - </parent> +<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> + <parent> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-parent</artifactId> + <version>2.0.6.RELEASE</version> + </parent> - <groupId>ch.unige.solidify</groupId> - <artifactId>solidify-parent</artifactId> - <name>Solidify Parent</name> - <version>0.3.0-SNAPSHOT</version> - <packaging>pom</packaging> - <description>Solidify Parent project. Manage dependencies and version for Solidify framework</description> + <groupId>ch.unige.solidify</groupId> + <artifactId>solidify-parent</artifactId> + <name>Solidify Parent</name> + <version>0.3.0-SNAPSHOT</version> + <packaging>pom</packaging> + <description>Solidify Parent project. Manage dependencies and version for Solidify framework + </description> - <modules> - <!-- Solidify Framework --> - <module>solidify-util</module> - <module>solidify-model</module> - <module>solidify-controller</module> - </modules> + <modules> + <!-- Solidify Framework --> + <module>solidify-util</module> + <module>solidify-model</module> + <module>solidify-controller</module> + <module>solidify-auth</module> + </modules> - <properties> - <!--General properties --> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> - <java.version>1.8</java.version> - <!--Solidify properties --> - <solidify.version>${project.version}</solidify.version> - <solidify.util.version>0.3.0-SNAPSHOT</solidify.util.version> - <solidify.model.version>0.3.0-SNAPSHOT</solidify.model.version> - <solidify.controller.version>0.3.0-SNAPSHOT</solidify.controller.version> - <!--Libraries properties --> - <spring.cloud.release>Dalston.SR2</spring.cloud.release> - <bagit.version>5.0.3</bagit.version> - <saxon-he.version>9.8.0-8</saxon-he.version> - <jaxb.version>2.3.0</jaxb.version> - <elasticsearch.version>5.5.3</elasticsearch.version> - <snippets.baseDir>${project.build.directory}/generated-snippets</snippets.baseDir> - <asciidoctor.plugin.version>1.5.6</asciidoctor.plugin.version> - <asciidoctorj.pdf.version>1.5.0-alpha.16</asciidoctorj.pdf.version> - <maven-release-plugin.version>2.5.3</maven-release-plugin.version> - <jaxb2-maven-plugin.version>2.4</jaxb2-maven-plugin.version> - <properties-maven-plugin.version>1.0.0</properties-maven-plugin.version> - <org.json.version>20180813</org.json.version> - <skipDocs>false</skipDocs> - </properties> + <properties> + <!--General properties --> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> + <java.version>1.8</java.version> + <!--Solidify properties --> + <solidify.version>${project.version}</solidify.version> + <solidify.auth.version>0.1.0-SNAPSHOT</solidify.auth.version> + <solidify.util.version>0.3.0-SNAPSHOT</solidify.util.version> + <solidify.model.version>0.3.0-SNAPSHOT</solidify.model.version> + <solidify.controller.version>0.3.0-SNAPSHOT</solidify.controller.version> + <!--Libraries properties --> + <spring.cloud.release>Dalston.SR2</spring.cloud.release> + <bagit.version>5.0.3</bagit.version> + <saxon-he.version>9.8.0-8</saxon-he.version> + <jaxb.version>2.3.0</jaxb.version> + <elasticsearch.version>5.5.3</elasticsearch.version> + <snippets.baseDir>${project.build.directory}/generated-snippets</snippets.baseDir> + <asciidoctor.plugin.version>1.5.6</asciidoctor.plugin.version> + <asciidoctorj.pdf.version>1.5.0-alpha.16</asciidoctorj.pdf.version> + <maven-release-plugin.version>2.5.3</maven-release-plugin.version> + <jaxb2-maven-plugin.version>2.4</jaxb2-maven-plugin.version> + <properties-maven-plugin.version>1.0.0</properties-maven-plugin.version> + <org.json.version>20180813</org.json.version> + <skipDocs>false</skipDocs> + </properties> - <dependencyManagement> - <dependencies> - <!--Remove this after migration to Spring Boot 2 --> - <dependency> - <groupId>org.springframework.security.oauth.boot</groupId> - <artifactId>spring-security-oauth2-autoconfigure</artifactId> - <version>2.0.5.RELEASE</version> - </dependency> - <!--Spring dependencies --> - <dependency> - <groupId>org.springframework.cloud</groupId> - <artifactId>spring-cloud-dependencies</artifactId> - <version>${spring.cloud.release}</version> - <type>pom</type> - <scope>import</scope> - </dependency> - <!--Solidify dependencies --> - <dependency> - <groupId>ch.unige.solidify</groupId> - <artifactId>solidify-util</artifactId> - <version>${solidify.util.version}</version> - </dependency> - <dependency> - <groupId>ch.unige.solidify</groupId> - <artifactId>solidify-model</artifactId> - <version>${solidify.model.version}</version> - </dependency> - <dependency> - <groupId>ch.unige.solidify</groupId> - <artifactId>solidify-controller</artifactId> - <version>${solidify.controller.version}</version> - </dependency> - <!-- Search dependencies --> - <dependency> - <groupId>org.elasticsearch.client</groupId> - <artifactId>transport</artifactId> - <version>${elasticsearch.version}</version> - </dependency> - <!-- BagIt --> - <dependency> - <groupId>gov.loc</groupId> - <artifactId>bagit</artifactId> - <version>${bagit.version}</version> - </dependency> - <!-- Saxon HE --> - <dependency> - <groupId>net.sf.saxon</groupId> - <artifactId>Saxon-HE</artifactId> - <version>${saxon-he.version}</version> - </dependency> - <!-- JAXB --> - <dependency> - <groupId>com.sun.xml.bind</groupId> - <artifactId>jaxb-impl</artifactId> - <version>${jaxb.version}</version> - </dependency> - <dependency> - <groupId>com.sun.xml.bind</groupId> - <artifactId>jaxb-core</artifactId> - <version>${jaxb.version}</version> - </dependency> - <!-- Json, can we use Jackson instead ? --> - <dependency> - <groupId>org.json</groupId> - <artifactId>json</artifactId> - <version>${org.json.version}</version> - </dependency> - </dependencies> - </dependencyManagement> + <dependencyManagement> + <dependencies> + <!--Remove this after migration to Spring Boot 2 --> + <dependency> + <groupId>org.springframework.security.oauth.boot</groupId> + <artifactId>spring-security-oauth2-autoconfigure</artifactId> + <version>2.0.5.RELEASE</version> + </dependency> + <!--Spring dependencies --> + <dependency> + <groupId>org.springframework.cloud</groupId> + <artifactId>spring-cloud-dependencies</artifactId> + <version>${spring.cloud.release}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + <!--Solidify dependencies --> + <dependency> + <groupId>ch.unige.solidify</groupId> + <artifactId>solidify-util</artifactId> + <version>${solidify.util.version}</version> + </dependency> + <dependency> + <groupId>ch.unige.solidify</groupId> + <artifactId>solidify-model</artifactId> + <version>${solidify.model.version}</version> + </dependency> + <dependency> + <groupId>ch.unige.solidify</groupId> + <artifactId>solidify-controller</artifactId> + <version>${solidify.controller.version}</version> + </dependency> + <dependency> + <groupId>ch.unige.solidify</groupId> + <artifactId>solidify-auth</artifactId> + <version>${solidify.auth.version}</version> + </dependency> + <!-- Search dependencies --> + <dependency> + <groupId>org.elasticsearch.client</groupId> + <artifactId>transport</artifactId> + <version>${elasticsearch.version}</version> + </dependency> + <!-- BagIt --> + <dependency> + <groupId>gov.loc</groupId> + <artifactId>bagit</artifactId> + <version>${bagit.version}</version> + </dependency> + <!-- Saxon HE --> + <dependency> + <groupId>net.sf.saxon</groupId> + <artifactId>Saxon-HE</artifactId> + <version>${saxon-he.version}</version> + </dependency> + <!-- JAXB --> + <dependency> + <groupId>com.sun.xml.bind</groupId> + <artifactId>jaxb-impl</artifactId> + <version>${jaxb.version}</version> + </dependency> + <dependency> + <groupId>com.sun.xml.bind</groupId> + <artifactId>jaxb-core</artifactId> + <version>${jaxb.version}</version> + </dependency> + <!-- Json, can we use Jackson instead ? --> + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + <version>${org.json.version}</version> + </dependency> + </dependencies> + </dependencyManagement> - <dependencies> - <!--Remove this after migration to Spring Boot 2 --> - <dependency> - <groupId>org.springframework.security.oauth.boot</groupId> - <artifactId>spring-security-oauth2-autoconfigure</artifactId> - <version>2.0.5.RELEASE</version> - </dependency> - <!--Logs dependencies --> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-api</artifactId> - </dependency> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-core</artifactId> - </dependency> - <!--Tests dependencies --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-test</artifactId> - <scope>test</scope> - <exclusions> - <exclusion> - <groupId>com.vaadin.external.google</groupId> - <artifactId>android-json</artifactId> - </exclusion> - </exclusions> - </dependency> - <dependency> - <groupId>org.springframework.restdocs</groupId> - <artifactId>spring-restdocs-mockmvc</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-jersey</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.hsqldb</groupId> - <artifactId>hsqldb</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - <build> - <pluginManagement> - <plugins> - <plugin> - <groupId>pl.project13.maven</groupId> - <artifactId>git-commit-id-plugin</artifactId> - <configuration> - <dotGitDirectory>${project.basedir}/../.git</dotGitDirectory> - </configuration> - </plugin> - <plugin> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-maven-plugin</artifactId> - <executions> - <execution> - <goals> - <goal>repackage</goal> - </goals> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <trimStackTrace>false</trimStackTrace> - <includes> - <include>**/*Test.java</include> - <include>**/*Documentation.java</include> - </includes> - </configuration> - </plugin> - <plugin> - <groupId>org.asciidoctor</groupId> - <artifactId>asciidoctor-maven-plugin</artifactId> - <version>${asciidoctor.plugin.version}</version> - <dependencies> - <dependency> - <groupId>org.asciidoctor</groupId> - <artifactId>asciidoctorj-pdf</artifactId> - <version>${asciidoctorj.pdf.version}</version> - </dependency> - </dependencies> - <executions> - <execution> - <id>generate-docs</id> - <phase>prepare-package</phase> - <goals> - <goal>process-asciidoc</goal> - </goals> - </execution> - <execution> - <id>generate-pdf-doc</id> - <phase>prepare-package</phase> - <goals> - <goal>process-asciidoc</goal> - </goals> - <configuration> - <backend>pdf</backend> - <attributes> - <icons>font</icons> - <pagenums /> - <toc /> - </attributes> - </configuration> - </execution> - </executions> - <configuration> - <skip>${skipDocs}</skip> - <sourceDirectory>${dlcm.asciidoc.baseDir}</sourceDirectory> - <backend>html</backend> - <doctype>book</doctype> - <attributes> - <snippets>${snippets.baseDir}</snippets> - </attributes> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-resources-plugin</artifactId> - <executions> - <execution> - <id>copy-resources</id> - <phase>prepare-package</phase> - <goals> - <goal>copy-resources</goal> - </goals> - <configuration> - <outputDirectory> - ${project.build.outputDirectory}/static/docs - </outputDirectory> - <resources> - <resource> - <directory> - ${project.build.directory}/generated-docs - </directory> - </resource> - </resources> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>jaxb2-maven-plugin</artifactId> - <version>${jaxb2-maven-plugin.version}</version> - </plugin> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>build-helper-maven-plugin</artifactId> - </plugin> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>properties-maven-plugin</artifactId> - <version>${properties-maven-plugin.version}</version> - </plugin> - </plugins> - </pluginManagement> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <compilerArgument> - -Xlint:all - </compilerArgument> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-release-plugin</artifactId> - <version>${maven-release-plugin.version}</version> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - </plugin> - </plugins> - </build> + <dependencies> + <!--Remove this after migration to Spring Boot 2 --> + <dependency> + <groupId>org.springframework.security.oauth.boot</groupId> + <artifactId>spring-security-oauth2-autoconfigure</artifactId> + <version>2.0.5.RELEASE</version> + </dependency> + <!--Logs dependencies --> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + </dependency> + <!--Tests dependencies --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>com.vaadin.external.google</groupId> + <artifactId>android-json</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.springframework.restdocs</groupId> + <artifactId>spring-restdocs-mockmvc</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-jersey</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hsqldb</groupId> + <artifactId>hsqldb</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>pl.project13.maven</groupId> + <artifactId>git-commit-id-plugin</artifactId> + <configuration> + <dotGitDirectory>${project.basedir}/../.git</dotGitDirectory> + </configuration> + </plugin> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <trimStackTrace>false</trimStackTrace> + <includes> + <include>**/*Test.java</include> + <include>**/*Documentation.java</include> + </includes> + </configuration> + </plugin> + <plugin> + <groupId>org.asciidoctor</groupId> + <artifactId>asciidoctor-maven-plugin</artifactId> + <version>${asciidoctor.plugin.version}</version> + <dependencies> + <dependency> + <groupId>org.asciidoctor</groupId> + <artifactId>asciidoctorj-pdf</artifactId> + <version>${asciidoctorj.pdf.version}</version> + </dependency> + </dependencies> + <executions> + <execution> + <id>generate-docs</id> + <phase>prepare-package</phase> + <goals> + <goal>process-asciidoc</goal> + </goals> + </execution> + <execution> + <id>generate-pdf-doc</id> + <phase>prepare-package</phase> + <goals> + <goal>process-asciidoc</goal> + </goals> + <configuration> + <backend>pdf</backend> + <attributes> + <icons>font</icons> + <pagenums/> + <toc/> + </attributes> + </configuration> + </execution> + </executions> + <configuration> + <skip>${skipDocs}</skip> + <sourceDirectory>${dlcm.asciidoc.baseDir}</sourceDirectory> + <backend>html</backend> + <doctype>book</doctype> + <attributes> + <snippets>${snippets.baseDir}</snippets> + </attributes> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-resources-plugin</artifactId> + <executions> + <execution> + <id>copy-resources</id> + <phase>prepare-package</phase> + <goals> + <goal>copy-resources</goal> + </goals> + <configuration> + <outputDirectory> + ${project.build.outputDirectory}/static/docs + </outputDirectory> + <resources> + <resource> + <directory> + ${project.build.directory}/generated-docs + </directory> + </resource> + </resources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>jaxb2-maven-plugin</artifactId> + <version>${jaxb2-maven-plugin.version}</version> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>properties-maven-plugin</artifactId> + <version>${properties-maven-plugin.version}</version> + </plugin> + </plugins> + </pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <compilerArgument> + -Xlint:all + </compilerArgument> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-release-plugin</artifactId> + <version>${maven-release-plugin.version}</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + </plugin> + </plugins> + </build> - <profiles> - <profile> - <id>dlcm_nexus</id> - <activation> - <activeByDefault>false</activeByDefault> - </activation> - <repositories> - <repository> - <id>nexus_central</id> - <url>https://packages.dlcm.ch/repository/maven-public/</url> - <releases> - <enabled>true</enabled> - </releases> - <snapshots> - <enabled>true</enabled> - </snapshots> - </repository> - </repositories> - <pluginRepositories> - <pluginRepository> - <id>nexus_central</id> - <url>https://packages.dlcm.ch/repository/maven-public/</url> - <releases> - <enabled>true</enabled> - </releases> - <snapshots> - <enabled>true</enabled> - </snapshots> - </pluginRepository> - </pluginRepositories> - </profile> - </profiles> + <profiles> + <profile> + <id>dlcm_nexus</id> + <activation> + <activeByDefault>false</activeByDefault> + </activation> + <repositories> + <repository> + <id>nexus_central</id> + <url>https://packages.dlcm.ch/repository/maven-public/</url> + <releases> + <enabled>true</enabled> + </releases> + <snapshots> + <enabled>true</enabled> + </snapshots> + </repository> + </repositories> + <pluginRepositories> + <pluginRepository> + <id>nexus_central</id> + <url>https://packages.dlcm.ch/repository/maven-public/</url> + <releases> + <enabled>true</enabled> + </releases> + <snapshots> + <enabled>true</enabled> + </snapshots> + </pluginRepository> + </pluginRepositories> + </profile> + </profiles> - <scm> - <connection>scm:git:git@gitlab.unige.ch:DLCM/DLCM-Solidify.git</connection> - <tag>HEAD</tag> - </scm> + <scm> + <connection>scm:git:git@gitlab.unige.ch:DLCM/DLCM-Solidify.git</connection> + <tag>HEAD</tag> + </scm> - <distributionManagement> - <snapshotRepository> - <id>nexus_deploy</id> - <name>Nexus snapshot repo</name> - <url>https://packages.dlcm.ch/repository/maven-snapshots/</url> - </snapshotRepository> - <repository> - <id>nexus_release</id> - <name>Nexus release repo</name> - <url>https://packages.dlcm.ch/repository/maven-releases/</url> - </repository> - </distributionManagement> + <distributionManagement> + <snapshotRepository> + <id>nexus_deploy</id> + <name>Nexus snapshot repo</name> + <url>https://packages.dlcm.ch/repository/maven-snapshots/</url> + </snapshotRepository> + <repository> + <id>nexus_release</id> + <name>Nexus release repo</name> + <url>https://packages.dlcm.ch/repository/maven-releases/</url> + </repository> + </distributionManagement> </project> diff --git a/solidify-auth/pom.xml b/solidify-auth/pom.xml new file mode 100644 index 000000000..ec6d49909 --- /dev/null +++ b/solidify-auth/pom.xml @@ -0,0 +1,50 @@ +<?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> + <parent> + <groupId>ch.unige.solidify</groupId> + <artifactId>solidify-parent</artifactId> + <version>0.3.0-SNAPSHOT</version> + </parent> + + <artifactId>solidify-auth</artifactId> + <name>Solidify auth</name> + <description>Solidify auth Library</description> + <dependencies> + <!--Solidify dependencies --> + <dependency> + <groupId>ch.unige.solidify</groupId> + <artifactId>solidify-model</artifactId> + </dependency> + <dependency> + <groupId>ch.unige.solidify</groupId> + <artifactId>solidify-util</artifactId> + </dependency> + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + <version>2.6</version> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-test</artifactId> + </dependency> + <dependency> + <groupId>javax.ws.rs</groupId> + <artifactId>javax.ws.rs-api</artifactId> + </dependency> + + </dependencies> + +</project> \ No newline at end of file diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/Authenticated.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/Authenticated.java new file mode 100644 index 000000000..58a92f971 --- /dev/null +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/Authenticated.java @@ -0,0 +1,24 @@ +package ch.unige.solidify.auth; + +import org.springframework.security.access.prepost.PreAuthorize; + +import javax.ws.rs.NameBinding; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Authenticated annotation. If the user is authenticated move on, otherwise stop. + * <p> + * Warning: + * --------- + * Only one security annotation per method + */ +@NameBinding +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +@PreAuthorize("isFullyAuthenticated()") +public @interface Authenticated { +} + diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoAuthentication.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoAuthentication.java new file mode 100644 index 000000000..39e6b7080 --- /dev/null +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoAuthentication.java @@ -0,0 +1,55 @@ +package ch.unige.solidify.auth; + + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +public class SsoAuthentication implements Authentication { + + private SsoUserDetails userDetails; + + public SsoAuthentication(SsoUserDetails userDetails) { + this.userDetails = userDetails; + } + + @Override + public Collection<? extends GrantedAuthority> getAuthorities() { + return userDetails.getAuthorities(); + } + + @Override + public Object getCredentials() { + return userDetails.getPassword(); + } + + @Override + public Object getDetails() { + return null; + } + + @Override + public Object getPrincipal() { + return userDetails; + } + + @Override + public boolean isAuthenticated() { + return userDetails != null; + } + + @Override + public void setAuthenticated(boolean b) { + if (!b) { + userDetails = null; + } else { + throw new IllegalStateException("setAuthenticated"); + } + } + + @Override + public String getName() { + return userDetails.getUniqueid(); + } +} diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoAuthenticationFilter.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoAuthenticationFilter.java new file mode 100644 index 000000000..c57ae852b --- /dev/null +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoAuthenticationFilter.java @@ -0,0 +1,50 @@ +package ch.unige.solidify.auth; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +@Component +public class SsoAuthenticationFilter extends OncePerRequestFilter { + + + private UserFactory userFactory; + + @Autowired + public SsoAuthenticationFilter(UserFactory userFactory) { + this.userFactory = userFactory; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + Map<String, String> headers = new HashMap<>(); + Enumeration<String> names = request.getHeaderNames(); + while (names.hasMoreElements()) { + String name = names.nextElement().toLowerCase(); + String value = request.getHeader(name); + headers.putIfAbsent(name, value); + } + + User user = userFactory.get(headers); + + if (user != null && !user.isEmpty()) { + Authentication auth = new SsoAuthentication(new SsoUserDetails(user)); + SecurityContextHolder.getContext().setAuthentication(auth); + } + filterChain.doFilter(request, response); + } + +} diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoUserDetails.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoUserDetails.java new file mode 100644 index 000000000..3d36384e4 --- /dev/null +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoUserDetails.java @@ -0,0 +1,99 @@ +package ch.unige.solidify.auth; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + + +public class SsoUserDetails extends User implements UserDetails { + + //from Spring Security, spring adds the prefix + @JsonIgnore + private String rolePrefix = "ROLE_"; + + @JsonIgnore + private Collection<GrantedAuthority> authorities = new ArrayList<>(); + + public SsoUserDetails() { + super(); + } + + public SsoUserDetails(User user) { + for (String role : user.getRoles()) { + authorities.add(new SimpleGrantedAuthority(rolePrefix + role)); + } + super.uniqueid = user.getUniqueid(); + super.systemId = user.getSystemId(); + super.firstname = user.getFirstname(); + super.lastname = user.getLastname(); + super.name = user.getName(); + super.email = user.getEmail(); + super.homeorganization = user.getHomeorganization(); + super.employeeType = Collections.unmodifiableSet(user.getEmployeeType()); + super.unigeChStudentCategory = Collections.unmodifiableSet(user.getUnigeChStudentCategory()); + super.ismemberOf = Collections.unmodifiableSet(user.getIsmemberOf()); + super.roles = Collections.unmodifiableSet(user.getRoles()); + super.homeorganizationtype = user.getHomeorganizationtype(); + super.affiliation = user.getAffiliation(); + super.unigechoucode = user.getUnigechoucode(); + super.unigechstudentoucode = user.getUnigechstudentoucode(); + super.unigechemployeeoucode = user.getUnigechemployeeoucode(); + super.orgunitdn = user.getOrgunitdn(); + super.matriculationnumber = user.getMatriculationnumber(); + super.allProperties=user.getAllProperties(); + } + + @Override + @JsonIgnore + public Collection<? extends GrantedAuthority> getAuthorities() { + return authorities; + } + + @Override + @JsonIgnore + public String getPassword() { + throw new IllegalStateException("password"); + } + + @Override + @JsonIgnore + public String getUsername() { + return super.getUniqueid(); + } + + @Override + @JsonIgnore + public boolean isAccountNonExpired() { + return true; + } + + @Override + @JsonIgnore + public boolean isAccountNonLocked() { + return true; + } + + @Override + @JsonIgnore + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + @JsonIgnore + public boolean isEnabled() { + return true; + } + + @Override + @JsonIgnore + public boolean isEmpty() { + return super.getUniqueid() == null; + } + +} diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/User.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/User.java new file mode 100644 index 000000000..072a064ce --- /dev/null +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/User.java @@ -0,0 +1,222 @@ +package ch.unige.solidify.auth; + + +import ch.unige.solidify.model.IUser; +import org.apache.commons.lang.StringUtils; + +import java.io.Serializable; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * The user of the application. + */ + +public class User implements IUser, Serializable { + + protected String oauthToken; + protected String firstname; + protected String lastname; + protected String email; + protected String homeorganization; + protected String homeorganizationtype; + protected Set<String> affiliation = Collections.synchronizedSet(new HashSet<String>()); + protected Set<String> ismemberOf = Collections.synchronizedSet(new HashSet<String>()); + protected Set<String> employeeType = Collections.synchronizedSet(new HashSet<String>()); + protected String unigechoucode; + protected String unigechstudentoucode; + protected String unigechemployeeoucode; + protected String uniqueid; + protected Set<String> unigeChStudentCategory = Collections.synchronizedSet(new HashSet<String>()); + protected String orgunitdn; + protected String matriculationnumber; + protected String name = null; + protected Set<String> roles = Collections.synchronizedSet(new HashSet<String>()); + protected long systemId = 0; + + + protected Map<String, String> allProperties; + + + public User() { + } + + public User(final String name) { + this(); + this.name = name; + } + + + public long getSystemId() { + String uid = getUniqueid(); + if (!StringUtils.isEmpty(uid) && uid.contains("@")) { + String[] words = uid.split("@"); + String id = words[0]; + try { + return Long.parseLong(id); + + } catch (Exception ex) { + return 0; + } + } + + return 0; + } + + + public String getEmail() { + return email; + } + + + public Set<String> getEmployeeType() { + return employeeType; + } + + + public String getFirstname() { + return firstname; + } + + + public String getLastname() { + return lastname; + } + + + public String getName() { + if (!StringUtils.isEmpty(name)) { + return name; + } + + if (StringUtils.isEmpty(firstname) && StringUtils.isEmpty(lastname)) { + return ""; + } + + if (StringUtils.isEmpty(firstname)) { + return lastname; + } + + if (StringUtils.isEmpty(lastname)) { + return firstname; + } + + return firstname + " " + lastname; + } + + + public String getHomeorganization() { + return homeorganization; + } + + + public String getHomeorganizationtype() { + return homeorganizationtype; + } + + + public Set<String> getAffiliation() { + return affiliation; + } + + + public Set<String> getIsmemberOf() { + return ismemberOf; + } + + + public String getUnigechoucode() { + return unigechoucode; + } + + + public String getUnigechstudentoucode() { + return unigechstudentoucode; + } + + + public String getUnigechemployeeoucode() { + return unigechemployeeoucode; + } + + + public String getUniqueid() { + return uniqueid; + } + + @Override + public String getOauthToken() { + return this.oauthToken; + } + + + public String getOrgunitdn() { + return orgunitdn; + } + + + public String getMatriculationnumber() { + return matriculationnumber; + } + + + public Set<String> getRoles() { + return roles; + } + + + public Set<String> getUnigeChStudentCategory() { + return unigeChStudentCategory; + } + + + public boolean hasRole(String role) { + return roles.contains(role); + } + + public boolean isEmployeeType(String employeeType) { + return this.employeeType.contains(employeeType); + } + + public boolean isUnigeChStudentCategory(String unigeChStudentCategory) { + return this.unigeChStudentCategory.contains(unigeChStudentCategory); + } + + public Map<String, String> getAllProperties() { + return allProperties; + } + + + public boolean isEmpty() { + return false; + } + + @Override + public String toString() { + return "User{" + + "firstname='" + firstname + '\'' + + ", lastname='" + lastname + '\'' + + ", email='" + email + '\'' + + ", homeorganization='" + homeorganization + '\'' + + ", homeorganizationtype='" + homeorganizationtype + '\'' + + ", affiliation=" + affiliation + + ", ismemberOf=" + ismemberOf + + ", employeeType=" + employeeType + + ", unigechoucode='" + unigechoucode + '\'' + + ", unigechstudentoucode='" + unigechstudentoucode + '\'' + + ", unigechemployeeoucode='" + unigechemployeeoucode + '\'' + + ", uniqueid='" + uniqueid + '\'' + + ", unigeChStudentCategory=" + unigeChStudentCategory + + ", orgunitdn='" + orgunitdn + '\'' + + ", matriculationnumber='" + matriculationnumber + '\'' + + ", name='" + name + '\'' + + ", roles=" + roles + + ", systemId=" + systemId + + ", oauthToken=" + oauthToken + + '}'; + } + + +} diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/UserFactory.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/UserFactory.java new file mode 100644 index 000000000..caa26aaf5 --- /dev/null +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/UserFactory.java @@ -0,0 +1,31 @@ +package ch.unige.solidify.auth; + + +import ch.unige.solidify.auth.User; +import ch.unige.solidify.auth.oidc.OidcUserFactory; +import ch.unige.solidify.auth.shibboleth.ShibbolethUserFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Map; + + +@Component +class UserFactory { + private static final String OIDC_ACCESS_TOKEN = "oidc_access_token"; + + @Autowired + private ShibbolethUserFactory shibbolethUserFactory; + + @Autowired + private OidcUserFactory oidcUserFactory; + + User get(Map<String, String> headers) { + if (headers.containsKey(OIDC_ACCESS_TOKEN)) { + return oidcUserFactory.get(headers); + } else { + return shibbolethUserFactory.get(headers); + } + + } +} \ No newline at end of file diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/config/SsoAppRoleConfig.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/config/SsoAppRoleConfig.java new file mode 100644 index 000000000..8dba92126 --- /dev/null +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/config/SsoAppRoleConfig.java @@ -0,0 +1,71 @@ +package ch.unige.solidify.auth.config; + +import ch.unige.solidify.IEmpty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * Defintion of mapping between Application role and Grouper group + */ +@Component +@ConfigurationProperties("sol.sso") +public class SsoAppRoleConfig implements Serializable { + private List<Group> groups = new ArrayList<>(); + + public List<Group> getGroups() { + return groups; + } + + public void setGroups(List<Group> groups) { + this.groups = groups; + } + + public static class Group implements IEmpty, Serializable { + private String role; + private String org = "unige.ch"; + private String criteria = "isMemberOf"; + private List<String> criteriaValues = new ArrayList<>(); + + public List<String> getCriteriaValues() { + return criteriaValues; + } + + public void setCriteriaValues(List<String> criteriaValues) { + this.criteriaValues = criteriaValues; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + public String getOrg() { + return org; + } + + public void setOrg(String org) { + this.org = org; + } + + public String getCriteria() { + return criteria; + } + + public void setCriteria(String criteria) { + this.criteria = criteria; + } + + + @Override + public boolean isEmpty() { + return this.role == null || this.criteriaValues == null || this.criteriaValues.isEmpty(); + } + } +} diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/oidc/OidcUserFactory.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/oidc/OidcUserFactory.java new file mode 100644 index 000000000..287436aa5 --- /dev/null +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/oidc/OidcUserFactory.java @@ -0,0 +1,17 @@ +package ch.unige.solidify.auth.oidc; + +import ch.unige.solidify.auth.User; +import ch.unige.solidify.auth.util.UserData; +import org.springframework.stereotype.Component; + +import java.util.Map; + + +@Component +public class OidcUserFactory { + public User get(Map<String, String> attributes) { + UserData userData = new UserData(); + userData.setUniqueid(attributes.get("oidc_claim_sub")); + return userData; + } +} diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/shibboleth/ShibbolethHeaderConfig.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/shibboleth/ShibbolethHeaderConfig.java new file mode 100644 index 000000000..8d026ba2d --- /dev/null +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/shibboleth/ShibbolethHeaderConfig.java @@ -0,0 +1,160 @@ +package ch.unige.solidify.auth.shibboleth; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +import java.io.Serializable; + +/** + * Shibboleth header configuration + */ +@Component +@PropertySource("classpath:ShibboHeaderName.properties") +@ConfigurationProperties("shibboleth.header.name") +public class ShibbolethHeaderConfig implements Serializable { + + /** + * Adding default values for the test client, not very good + */ + + private String lastname; + private String firstname; + private String affiliation; + private String email; + private String uniqueId; + private String isMemberOf; + private String organization; + private String organizationType; + private String employeeType; + private String unigeChOuCode; + private String unigeChStudentOuCode; + private String unigeChEmployeeOuCode; + private String unigeChStudentCategory; + private String orgunitdn; + private String matriculationnumber; + + public String getMatriculationnumber() { + return matriculationnumber; + } + + public void setMatriculationnumber(String matriculationnumber) { + this.matriculationnumber = matriculationnumber; + } + + + public String getOrgunitdn() { + return orgunitdn; + } + + public void setOrgunitdn(String orgunitdn) { + this.orgunitdn = orgunitdn; + } + + + public String getIsMemberOf() { + return isMemberOf; + } + + public void setIsMemberOf(String isMemberOf) { + this.isMemberOf = isMemberOf; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getUniqueId() { + return uniqueId; + } + + public void setUniqueId(String uniqueId) { + this.uniqueId = uniqueId; + } + + public String getOrganization() { + return organization; + } + + public void setOrganization(String organization) { + this.organization = organization; + } + + public String getOrganizationType() { + return organizationType; + } + + public void setOrganizationType(String organizationType) { + this.organizationType = organizationType; + } + + public String getAffiliation() { + return affiliation; + } + + public void setAffiliation(String affiliation) { + this.affiliation = affiliation; + } + + public String getEmployeeType() { + return employeeType; + } + + public void setEmployeeType(String employeeType) { + this.employeeType = employeeType; + } + + public String getUnigeChOuCode() { + return unigeChOuCode; + } + + public void setUnigeChOuCode(String unigeChOuCode) { + this.unigeChOuCode = unigeChOuCode; + } + + public String getUnigeChStudentOuCode() { + return unigeChStudentOuCode; + } + + public void setUnigeChStudentOuCode(String unigeChStudentOuCode) { + this.unigeChStudentOuCode = unigeChStudentOuCode; + } + + public String getUnigeChEmployeeOuCode() { + return unigeChEmployeeOuCode; + } + + public void setUnigeChEmployeeOuCode(String unigeChEmployeeOuCode) { + this.unigeChEmployeeOuCode = unigeChEmployeeOuCode; + } + + public String getUnigeChStudentCategory() { + return unigeChStudentCategory; + } + + public void setUnigeChStudentCategory(String unigeChStudentCategory) { + this.unigeChStudentCategory = unigeChStudentCategory; + } + + +} diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/shibboleth/ShibbolethUserFactory.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/shibboleth/ShibbolethUserFactory.java new file mode 100644 index 000000000..770fd460e --- /dev/null +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/shibboleth/ShibbolethUserFactory.java @@ -0,0 +1,262 @@ +package ch.unige.solidify.auth.shibboleth; + +import ch.unige.solidify.auth.config.SsoAppRoleConfig; +import ch.unige.solidify.auth.User; +import ch.unige.solidify.auth.util.UserData; +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.xml.bind.annotation.XmlRootElement; +import java.lang.reflect.Method; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Read user's attributes from HTTP headers and returns a User object from them. + * + * @link https://aai-viewer.switch.ch/aai/ + */ +@Component +public class ShibbolethUserFactory { + private static final Logger LOGGER = Logger.getLogger(ShibbolethUserFactory.class.getName()); + + private static final String SEPARATOR = ";"; + + + private ShibbolethHeaderConfig config; + private SsoAppRoleConfig ssoRoleConfig; + + @Autowired + public ShibbolethUserFactory(ShibbolethHeaderConfig config, SsoAppRoleConfig ssoRoleConfig) { + this.config = config; + this.ssoRoleConfig = ssoRoleConfig; + } + + public User get(Map<String, String> attributes) { + return new Reader(attributes, config, ssoRoleConfig); + } + + /** + * Read AAI attributes from the a (Http Header) map + */ + @XmlRootElement + public static class Reader extends UserData { + + private Map<String, String> attributes; + private ShibbolethHeaderConfig config; + private SsoAppRoleConfig ssoRoleConfig; + + Reader(final Map<String, String> attributes, ShibbolethHeaderConfig config, SsoAppRoleConfig ssoRoleConfig) { + this.attributes = attributes; + this.config = config; + this.ssoRoleConfig = ssoRoleConfig; + setUserData(); + } + + @Override + public boolean isEmpty() { + return StringUtils.isEmpty(getUniqueid()); + } + + private void setUserData() { + super.setAllProperties(this.attributes); + super.setUniqueid(calculeUniqueid()); + super.setFirstname(calculeFirstname()); + super.setLastname(calculeLastname()); + super.setEmail(calculeEmail()); + super.setHomeorganization(calculeHomeorganization()); + super.setEmployeeType(calculeEmployeeType()); + super.setUnigeChStudentCategory(calculeUnigeChStudentCategory()); + super.setIsmemberOf(calculeIsmemberOf()); + super.setHomeorganizationtype(calculeHomeorganizationtype()); + super.setAffiliation(calculeAffiliation()); + super.setUnigechoucode(calculeUnigechoucode()); + super.setUnigechstudentoucode(calculeUnigechstudentoucode()); + super.setUnigechemployeeoucode(calculeUnigechemployeeoucode()); + super.setOrgunitdn(calculeOrgunitdn()); + super.setMatriculationnumber(calculeMatriculationnumber()); + super.setName(calculeName()); + super.setRoles(calculeApplicationRoles()); + } + + + /** + * The fist item in the attribute array for the specified key + * + * @param key The key to search for + * @return The first element of the array matching the key. + */ + private String first(final String key) { + if (!attributes.containsKey(key)) { + return ""; + } + String result = attributes.get(key); + result = StringUtils.isEmpty(result) ? "" : result; + return result; + } + + + private String calculeUniqueid() { + String key = config.getUniqueId(); + return first(key); + } + + private String calculeFirstname() { + String key = config.getFirstname(); + return first(key); + } + + + private String calculeLastname() { + String key = config.getLastname(); + return first(key); + } + + + private String calculeEmail() { + String key = config.getEmail(); + return first(key); + } + + + private String calculeHomeorganization() { + String key = config.getOrganization(); + return first(key); + } + + private Set<String> calculeEmployeeType() { + String key = config.getEmployeeType(); + if (StringUtils.isEmpty(first(key))) { + return Collections.synchronizedSet(new HashSet<>()); + } + String[] items = first(key).split(SEPARATOR); + return Collections.synchronizedSet(new HashSet<>(Arrays.asList(items))); + } + + + private Set<String> calculeUnigeChStudentCategory() { + String key = config.getUnigeChStudentCategory(); + if (StringUtils.isEmpty(first(key))) { + return Collections.synchronizedSet(new HashSet<>()); + } + String[] items = first(key).split(SEPARATOR); + return Collections.synchronizedSet(new HashSet<>(Arrays.asList(items))); + } + + private Set<String> calculeIsmemberOf() { + String key = config.getIsMemberOf(); + String value = first(key); + if (StringUtils.isEmpty(value)) { + return Collections.synchronizedSet(new HashSet<>()); + } + String[] items = value.split(SEPARATOR); + return Collections.synchronizedSet(new HashSet<>(Arrays.asList(items))); + } + + private String calculeHomeorganizationtype() { + String key = config.getOrganizationType(); + return first(key); + } + + + private Set<String> calculeAffiliation() { + String key = config.getAffiliation(); + String[] items = first(key).split(SEPARATOR); + return Collections.synchronizedSet(new HashSet<>(Arrays.asList(items))); + } + + + private String calculeUnigechoucode() { + String key = config.getUnigeChOuCode(); + return first(key); + } + + private String calculeUnigechstudentoucode() { + String key = config.getUnigeChStudentOuCode(); + return first(key); + } + + private String calculeUnigechemployeeoucode() { + String key = config.getUnigeChEmployeeOuCode(); + return first(key); + } + + private String calculeOrgunitdn() { + String key = config.getOrgunitdn(); + return first(key); + } + + private String calculeMatriculationnumber() { + String key = config.getMatriculationnumber(); + return first(key); + } + + private String calculeName() { + String firstname = getFirstname(); + String lastname = getLastname(); + + if (StringUtils.isEmpty(firstname) && StringUtils.isEmpty(lastname)) { + return ""; + } + + if (StringUtils.isEmpty(firstname)) { + return lastname; + } + + if (StringUtils.isEmpty(lastname)) { + return firstname; + } + + return firstname + " " + lastname; + } + + @SuppressWarnings("unchecked") + private void setRoleWithcalculeResult(Object foundValues, Set<String> waitedValues, String applicationRoleName, Set<String> rolesList) { + + if (foundValues instanceof Set) { + Set<String> foundSetValue = (Set<String>) foundValues; + foundSetValue.retainAll(waitedValues); + if (!foundSetValue.isEmpty()) { + rolesList.add(applicationRoleName); + } + return; + } + + if (foundValues instanceof String) {//if method returns a String value + String foundStringValue = (String) foundValues; + if (waitedValues.contains(foundStringValue)) { + rolesList.add(applicationRoleName); + } + } + } + + + private Set<String> calculeApplicationRoles() { + Set<String> roleList = Collections.synchronizedSet(new HashSet<>()); + List<SsoAppRoleConfig.Group> applicationGroups = ssoRoleConfig.getGroups(); + + for (SsoAppRoleConfig.Group group : applicationGroups) { + if (group.isEmpty() || (!group.getOrg().equalsIgnoreCase(getHomeorganization()))) break; + Method[] allMethods = super.getClass().getDeclaredMethods(); + for (Method m : allMethods) { + if (m.getName().equalsIgnoreCase("calcule" + group.getCriteria())) { + try { + Object mothodeResult = m.invoke(this); + Set<String> waitedValues = new HashSet<>(group.getCriteriaValues()); + setRoleWithcalculeResult(mothodeResult, waitedValues, group.getRole(), roleList); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Application Role and grouper mapping error", e); + break; + } + } + } + } + return roleList; + } + + + } +} + diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/util/MockSsoUserSecurityContextFactory.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/util/MockSsoUserSecurityContextFactory.java new file mode 100644 index 000000000..06756bf92 --- /dev/null +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/util/MockSsoUserSecurityContextFactory.java @@ -0,0 +1,72 @@ +package ch.unige.solidify.auth.util; + + +import ch.unige.solidify.auth.SsoAuthentication; +import ch.unige.solidify.auth.SsoUserDetails; +import ch.unige.solidify.auth.User; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.context.support.WithSecurityContextFactory; + +import java.util.*; + +public class MockSsoUserSecurityContextFactory + implements WithSecurityContextFactory<WithSsoMockUser> { + + @Override + public SecurityContext createSecurityContext(WithSsoMockUser unigeShibboMockUser) { + SecurityContext context = SecurityContextHolder.createEmptyContext(); + User user = getUser(unigeShibboMockUser); + SsoUserDetails principal = new SsoUserDetails(user); + Authentication auth = new SsoAuthentication(principal); + context.setAuthentication(auth); + return context; + } + + private User getUser(WithSsoMockUser mockUser) { + UserData userData = new UserData(); + userData.setSystemId(mockUser.systemId()); + userData.setUniqueid(mockUser.uniqueid()); + + userData.setFirstname(mockUser.firstName()); + userData.setLastname(mockUser.lastname()); + userData.setName(mockUser.name()); + userData.setEmail(mockUser.email()); + userData.setHomeorganization(mockUser.organization()); + userData.setEmployeeType(string2Set(mockUser.employeeType())); + + userData.setUnigeChStudentCategory(string2Set(mockUser.unigeChStudentCategory())); + userData.setIsmemberOf(string2Set(mockUser.ismemberOf())); + userData.setRoles(string2Set(mockUser.roles())); + + userData.setHomeorganizationtype(mockUser.homeorganizationtype()); + userData.setAffiliation(string2Set(mockUser.affiliation())); + userData.setUnigechoucode(mockUser.unigechoucode()); + userData.setUnigechstudentoucode(mockUser.unigechstudentoucode()); + userData.setUnigechemployeeoucode(mockUser.unigechemployeeoucode()); + userData.setOrgunitdn(mockUser.orgunitdn()); + userData.setMatriculationnumber(mockUser.matriculationnumber()); + userData + .setAllProperties(array2Map(mockUser.allPropertiesName(), mockUser.allPropertiesValue())); + return userData; + } + + private Map<String, String> array2Map(String[] names, String[] values) { + Map<String, String> properties = new HashMap(); + if (names != null && names.length != 0 && values != null && values.length != 0) { + int index = 0; + for (String name : names) { + properties.put(name, values[index]); + index++; + } + } + return properties; + + } + + private Set<String> string2Set(String[] input) { + return new HashSet(Arrays.asList(input)); + } +} + diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/util/UserData.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/util/UserData.java new file mode 100644 index 000000000..a7ea0adf8 --- /dev/null +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/util/UserData.java @@ -0,0 +1,126 @@ +package ch.unige.solidify.auth.util; + +import ch.unige.solidify.auth.User; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import java.io.Serializable; +import java.util.Map; +import java.util.Set; + +/** + * Contains user's data. Properties are read/write, useful for testing purposes. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class UserData extends User implements Serializable { + + public void setAllProperties(Map<String, String> allProperties) { + super.allProperties = allProperties; + } + + public void setOauthToken(String oauthToken) { + super.oauthToken = oauthToken; + } + + public void setSystemId(long systemId) { + super.systemId = systemId; + } + + public void setFirstname(String firstname) { + super.firstname = firstname; + } + + public void setLastname(String lastname) { + super.lastname = lastname; + } + + + public void setAffiliation(Set<String> affiliation) { + super.affiliation = affiliation; + } + + public void setEmail(String email) { + super.email = email; + } + + public void setHomeorganization(String homeorganization) { + super.homeorganization = homeorganization; + } + + public void setHomeorganizationtype(String homeorganizationtype) { + super.homeorganizationtype = homeorganizationtype; + } + + + public void setIsmemberOf(Set<String> ismemberOf) { + super.ismemberOf = ismemberOf; + } + + public void setEmployeeType(Set<String> employeeType) { + super.employeeType = employeeType; + } + + public void setUnigechoucode(String unigechoucode) { + super.unigechoucode = unigechoucode; + } + + public void setUnigechstudentoucode(String unigechstudentoucode) { + super.unigechstudentoucode = unigechstudentoucode; + } + + public void setUnigechemployeeoucode(String unigechemployeeoucode) { + super.unigechemployeeoucode = unigechemployeeoucode; + } + + public void setUniqueid(String uniqueid) { + super.uniqueid = uniqueid; + } + + public void setUnigeChStudentCategory(Set<String> unigeChStudentCategory) { + super.unigeChStudentCategory = unigeChStudentCategory; + } + + public void setOrgunitdn(String orgunitdn) { + super.orgunitdn = orgunitdn; + } + + public void setMatriculationnumber(String matriculationnumber) { + super.matriculationnumber = matriculationnumber; + } + + public void setName(String name) { + super.name = name; + } + + public void setRoles(Set<String> roles) { + super.roles = roles; + } + + + @Override + public String toString() { + return "UserData{" + + "firstname='" + firstname + '\'' + + ", lastname='" + lastname + '\'' + + ", email='" + email + '\'' + + ", homeorganization='" + homeorganization + '\'' + + ", homeorganizationtype='" + homeorganizationtype + '\'' + + ", affiliation='" + affiliation + '\'' + + ", ismemberOf=" + ismemberOf + + ", employeeType=" + employeeType + + ", unigechoucode='" + unigechoucode + '\'' + + ", unigechstudentoucode='" + unigechstudentoucode + '\'' + + ", unigechemployeeoucode='" + unigechemployeeoucode + '\'' + + ", uniqueid='" + uniqueid + '\'' + + ", unigeChStudentCategory=" + unigeChStudentCategory + + ", orgunitdn='" + orgunitdn + '\'' + + ", matriculationnumber='" + matriculationnumber + '\'' + + ", name='" + name + '\'' + + ", roles=" + roles + + ", systemId=" + systemId + + ", oauthToken=" + oauthToken + + '}'; + } + + +} + diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/util/WithSsoMockUser.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/util/WithSsoMockUser.java new file mode 100644 index 000000000..085f655eb --- /dev/null +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/util/WithSsoMockUser.java @@ -0,0 +1,53 @@ +package ch.unige.solidify.auth.util; + + +import org.springframework.security.test.context.support.WithSecurityContext; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@WithSecurityContext(factory = MockSsoUserSecurityContextFactory.class) +public @interface WithSsoMockUser { + String uniqueid() default "0@unige.ch"; + + long systemId() default 0; + + + String firstName() default "mock firstname"; + + String lastname() default "mock Surname"; + + String name() default "mockUser"; + + String email() default "mock@unige.ch"; + + String organization() default "unige"; + + String[] employeeType() default {}; + + String[] unigeChStudentCategory() default {}; + + String[] ismemberOf() default {}; + + String[] roles() default {}; + + String homeorganizationtype() default "university"; + + String[] affiliation() default {}; + + String unigechoucode() default ""; + + String unigechstudentoucode() default ""; + + String unigechemployeeoucode() default ""; + + String orgunitdn() default ""; + + String matriculationnumber() default ""; + + String[] allPropertiesName() default {}; + String[] allPropertiesValue() default {}; + + +} diff --git a/solidify-auth/src/main/resources/ShibboHeaderName.properties b/solidify-auth/src/main/resources/ShibboHeaderName.properties new file mode 100644 index 000000000..f2941513b --- /dev/null +++ b/solidify-auth/src/main/resources/ShibboHeaderName.properties @@ -0,0 +1,15 @@ +shibboleth.header.name.isMemberOf=ismemberof +shibboleth.header.name.lastname=surname +shibboleth.header.name.firstname=givenname +shibboleth.header.name.email=mail +shibboleth.header.name.uniqueId=uniqueid +shibboleth.header.name.organization=homeorganization +shibboleth.header.name.organizationType=homeorganizationtype +shibboleth.header.name.affiliation=affiliation +shibboleth.header.name.employeeType=employeetype +shibboleth.header.name.unigeChOuCode=unigechoucode +shibboleth.header.name.unigeChStudentOuCode=unigechstudentoucode +shibboleth.header.name.unigeChEmployeeOuCode=unigechemployeeoucode +shibboleth.header.name.unigeChStudentCategory=unigechstudentcategory +shibboleth.header.name.orgunitdn=orgunit-dn +shibboleth.header.name.matriculationnumber=matriculationNumber \ No newline at end of file diff --git a/solidify-model/src/main/java/ch/unige/solidify/model/IUser.java b/solidify-model/src/main/java/ch/unige/solidify/model/IUser.java new file mode 100644 index 000000000..32262fd0f --- /dev/null +++ b/solidify-model/src/main/java/ch/unige/solidify/model/IUser.java @@ -0,0 +1,13 @@ +package ch.unige.solidify.model; + +public interface IUser { + public String getFirstname(); + + public String getLastname(); + + public String getEmail(); + + public String getUniqueid(); + + public String getOauthToken(); +} diff --git a/solidify-util/src/main/java/ch/unige/solidify/IEmpty.java b/solidify-util/src/main/java/ch/unige/solidify/IEmpty.java new file mode 100644 index 000000000..4162f67f4 --- /dev/null +++ b/solidify-util/src/main/java/ch/unige/solidify/IEmpty.java @@ -0,0 +1,16 @@ +package ch.unige.solidify; + +import javax.xml.bind.annotation.XmlTransient; + +/** + * Empty objects are placeholders for nulls. They have the benefits of avoiding scattering "if null" + * checks throughout the code. + */ +public interface IEmpty { + + /** + * @return True if object is empty false otherwise + */ + @XmlTransient + boolean isEmpty(); +} \ No newline at end of file diff --git a/solidify-util/src/main/java/ch/unige/solidify/util/Pair.java b/solidify-util/src/main/java/ch/unige/solidify/util/Pair.java new file mode 100644 index 000000000..3a6b04e05 --- /dev/null +++ b/solidify-util/src/main/java/ch/unige/solidify/util/Pair.java @@ -0,0 +1,33 @@ +package ch.unige.solidify.util; + + +public class Pair { + private String key; + private String value; + + public Pair() { + this.key = ""; + this.value = ""; + } + + public Pair(String key, String value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return this.key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return this.value; + } + + public void setValue(String value) { + this.value = value; + } +} -- GitLab From 270814c049c49185fd6c57cb5a7eee1e29cc47d9 Mon Sep 17 00:00:00 2001 From: wangr <raoli.wang@unige.ch> Date: Fri, 2 Nov 2018 16:21:28 +0100 Subject: [PATCH 02/14] fix: remove security features from controller --- .../controller/AssociationController.java | 37 ++++++++----------- .../AssociationRemoteController.java | 3 +- .../controller/CompositionController.java | 3 +- .../controller/ResourceController.java | 3 +- 4 files changed, 18 insertions(+), 28 deletions(-) diff --git a/solidify-controller/src/main/java/ch/unige/solidify/controller/AssociationController.java b/solidify-controller/src/main/java/ch/unige/solidify/controller/AssociationController.java index 09fa30ad7..4dadd91e5 100644 --- a/solidify-controller/src/main/java/ch/unige/solidify/controller/AssociationController.java +++ b/solidify-controller/src/main/java/ch/unige/solidify/controller/AssociationController.java @@ -1,31 +1,24 @@ package ch.unige.solidify.controller; -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; - -import java.util.ArrayList; -import java.util.List; - +import ch.unige.solidify.rest.Resource; +import ch.unige.solidify.rest.SubResourceContainer; +import ch.unige.solidify.service.CompositeResourceService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.http.HttpEntity; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; -import ch.unige.solidify.rest.Resource; -import ch.unige.solidify.rest.SubResourceContainer; -import ch.unige.solidify.security.UserPermissions; -import ch.unige.solidify.service.CompositeResourceService; -@UserPermissions public abstract class AssociationController<T extends Resource<T> & SubResourceContainer, V extends Resource<V>> - extends SubResourceController<T, V> { + extends SubResourceController<T, V> { @Autowired protected CompositeResourceService<V> childItemService; @@ -51,9 +44,9 @@ public abstract class AssociationController<T extends Resource<T> & SubResourceC return new ResponseEntity<>(list, HttpStatus.CREATED); } - @RequestMapping(value = "/{id}", method = { RequestMethod.PUT, RequestMethod.PATCH }) + @RequestMapping(value = "/{id}", method = {RequestMethod.PUT, RequestMethod.PATCH}) public HttpEntity<V> update(@PathVariable String parentid, @PathVariable String id, - @RequestBody T t2) { + @RequestBody T t2) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } @@ -62,7 +55,7 @@ public abstract class AssociationController<T extends Resource<T> & SubResourceC T parentItem = this.itemService.findOne(parentid); V item = this.itemService.findByParentIdAndChildId(parentid, getMainResourceClass(), - getSubResourceClass(), id); + getSubResourceClass(), id); if (item == null) { return HttpStatus.NOT_FOUND; } @@ -78,7 +71,7 @@ public abstract class AssociationController<T extends Resource<T> & SubResourceC T parentItem = this.itemService.findOne(parentid); Page<V> list = this.itemService.findByParentId(parentid, getMainResourceClass(), - getSubResourceClass(), null); + getSubResourceClass(), null); if (list.getTotalElements() > 0) { for (V v : list) { parentItem.removeItem(v); // TODO to optimize removeall diff --git a/solidify-controller/src/main/java/ch/unige/solidify/controller/AssociationRemoteController.java b/solidify-controller/src/main/java/ch/unige/solidify/controller/AssociationRemoteController.java index 15844b729..5fe172123 100644 --- a/solidify-controller/src/main/java/ch/unige/solidify/controller/AssociationRemoteController.java +++ b/solidify-controller/src/main/java/ch/unige/solidify/controller/AssociationRemoteController.java @@ -24,10 +24,9 @@ import ch.unige.solidify.rest.Collection; import ch.unige.solidify.rest.Resource; import ch.unige.solidify.rest.SubResourceContainer; import ch.unige.solidify.rest.Tool; -import ch.unige.solidify.security.UserPermissions; import ch.unige.solidify.service.CompositeResourceService; -@UserPermissions + public abstract class AssociationRemoteController<T extends Resource<T> & SubResourceContainer, V extends Resource<V>> extends SolidifyController { diff --git a/solidify-controller/src/main/java/ch/unige/solidify/controller/CompositionController.java b/solidify-controller/src/main/java/ch/unige/solidify/controller/CompositionController.java index e2ed8b697..d397940f3 100644 --- a/solidify-controller/src/main/java/ch/unige/solidify/controller/CompositionController.java +++ b/solidify-controller/src/main/java/ch/unige/solidify/controller/CompositionController.java @@ -19,10 +19,9 @@ import org.springframework.web.bind.annotation.RequestMethod; import ch.unige.solidify.exception.SolidifyRuntimeException; import ch.unige.solidify.rest.Resource; import ch.unige.solidify.rest.SubResourceContainer; -import ch.unige.solidify.security.UserPermissions; import ch.unige.solidify.service.CompositeResourceService; -@UserPermissions + public abstract class CompositionController<T extends Resource<T> & SubResourceContainer, V extends Resource<V>> extends SubResourceController<T, V> { diff --git a/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceController.java b/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceController.java index 3844a129f..141e08b51 100644 --- a/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceController.java +++ b/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceController.java @@ -24,11 +24,10 @@ import ch.unige.solidify.rest.ActionName; import ch.unige.solidify.rest.Collection; import ch.unige.solidify.rest.Resource; import ch.unige.solidify.rest.Tool; -import ch.unige.solidify.security.UserPermissions; import ch.unige.solidify.service.CompositeResourceService; import ch.unige.solidify.service.MessageService; -@UserPermissions + public abstract class ResourceController<T extends Resource<T>> extends SolidifyController implements ControllerWithHateoasHome { -- GitLab From c373fffdbb1da9a1e3b94da5cefe06c9ffdd541e Mon Sep 17 00:00:00 2001 From: wangr <raoli.wang@unige.ch> Date: Fri, 2 Nov 2018 16:21:57 +0100 Subject: [PATCH 03/14] feat: add SSO Security implementation and UserController --- pom.xml | 2 +- solidify-auth/pom.xml | 2 + .../auth/SsoAuthenticationFilter.java | 6 +- .../solidify/auth/{User.java => SsoUser.java} | 10 +- .../unige/solidify/auth/SsoUserDetails.java | 44 ++-- .../ch/unige/solidify/auth/UserFactory.java | 6 +- .../auth/config/SsoAppRoleConfig.java | 1 - .../solidify/auth/oidc/OidcUserFactory.java | 11 +- .../shibboleth/ShibbolethUserFactory.java | 10 +- .../MockSsoUserSecurityContextFactory.java | 10 +- .../util/{UserData.java => SsoUserData.java} | 6 +- .../solidify/auth/util/WithSsoMockUser.java | 2 + .../solidify/test/auth/AuthMockToollTest.java | 44 ++++ .../solidify/test/auth/ShibboFilterTest.java | 80 ++++++++ .../test/auth/ShibbolethSsoUserTest.java | 191 ++++++++++++++++++ .../test/auth/config/MockApplication.java | 28 +++ .../auth/controllers/MockUserController.java | 27 +++ .../solidify/test/auth/data/MockData.java | 48 +++++ .../test/resources/config/application.yaml | 27 +++ solidify-controller/pom.xml | 177 +++++++++------- .../controller/SubResourceController.java | 3 +- .../solidify/controller/UserController.java | 23 +++ .../unige/solidify/service/UserService.java | 23 +++ .../test/controller/UserServiceTest.java | 42 ++++ .../UserShibboletControllerTest.java | 66 ++++++ .../controller/config/MockApplication.java | 29 +++ .../java/ch/unige/solidify/model/IUser.java | 13 -- .../java/ch/unige/solidify/model/User.java | 25 +++ .../solidify/config/SolidifyProvider.java | 2 +- 29 files changed, 811 insertions(+), 147 deletions(-) rename solidify-auth/src/main/java/ch/unige/solidify/auth/{User.java => SsoUser.java} (96%) rename solidify-auth/src/main/java/ch/unige/solidify/auth/util/{UserData.java => SsoUserData.java} (96%) create mode 100644 solidify-auth/src/test/java/ch/unige/solidify/test/auth/AuthMockToollTest.java create mode 100644 solidify-auth/src/test/java/ch/unige/solidify/test/auth/ShibboFilterTest.java create mode 100644 solidify-auth/src/test/java/ch/unige/solidify/test/auth/ShibbolethSsoUserTest.java create mode 100644 solidify-auth/src/test/java/ch/unige/solidify/test/auth/config/MockApplication.java create mode 100644 solidify-auth/src/test/java/ch/unige/solidify/test/auth/controllers/MockUserController.java create mode 100644 solidify-auth/src/test/java/ch/unige/solidify/test/auth/data/MockData.java create mode 100644 solidify-auth/src/test/resources/config/application.yaml create mode 100644 solidify-controller/src/main/java/ch/unige/solidify/controller/UserController.java create mode 100644 solidify-controller/src/main/java/ch/unige/solidify/service/UserService.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserServiceTest.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java delete mode 100644 solidify-model/src/main/java/ch/unige/solidify/model/IUser.java create mode 100644 solidify-model/src/main/java/ch/unige/solidify/model/User.java diff --git a/pom.xml b/pom.xml index 23f65ac6e..d16f4ef4e 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ <java.version>1.8</java.version> <!--Solidify properties --> <solidify.version>${project.version}</solidify.version> - <solidify.auth.version>0.1.0-SNAPSHOT</solidify.auth.version> + <solidify.auth.version>0.3.0-SNAPSHOT</solidify.auth.version> <solidify.util.version>0.3.0-SNAPSHOT</solidify.util.version> <solidify.model.version>0.3.0-SNAPSHOT</solidify.model.version> <solidify.controller.version>0.3.0-SNAPSHOT</solidify.controller.version> diff --git a/solidify-auth/pom.xml b/solidify-auth/pom.xml index ec6d49909..63d1e293e 100644 --- a/solidify-auth/pom.xml +++ b/solidify-auth/pom.xml @@ -40,9 +40,11 @@ <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> </dependency> + <dependency> <groupId>javax.ws.rs</groupId> <artifactId>javax.ws.rs-api</artifactId> + <version>2.1</version> </dependency> </dependencies> diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoAuthenticationFilter.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoAuthenticationFilter.java index c57ae852b..1cefa8325 100644 --- a/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoAuthenticationFilter.java +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoAuthenticationFilter.java @@ -38,10 +38,10 @@ public class SsoAuthenticationFilter extends OncePerRequestFilter { headers.putIfAbsent(name, value); } - User user = userFactory.get(headers); + SsoUser ssoUser = userFactory.get(headers); - if (user != null && !user.isEmpty()) { - Authentication auth = new SsoAuthentication(new SsoUserDetails(user)); + if (ssoUser != null && !ssoUser.isEmpty()) { + Authentication auth = new SsoAuthentication(new SsoUserDetails(ssoUser)); SecurityContextHolder.getContext().setAuthentication(auth); } filterChain.doFilter(request, response); diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/User.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoUser.java similarity index 96% rename from solidify-auth/src/main/java/ch/unige/solidify/auth/User.java rename to solidify-auth/src/main/java/ch/unige/solidify/auth/SsoUser.java index 072a064ce..bc96420d8 100644 --- a/solidify-auth/src/main/java/ch/unige/solidify/auth/User.java +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoUser.java @@ -1,7 +1,7 @@ package ch.unige.solidify.auth; -import ch.unige.solidify.model.IUser; +import ch.unige.solidify.model.User; import org.apache.commons.lang.StringUtils; import java.io.Serializable; @@ -14,7 +14,7 @@ import java.util.Set; * The user of the application. */ -public class User implements IUser, Serializable { +public class SsoUser implements User, Serializable { protected String oauthToken; protected String firstname; @@ -40,10 +40,10 @@ public class User implements IUser, Serializable { protected Map<String, String> allProperties; - public User() { + public SsoUser() { } - public User(final String name) { + public SsoUser(final String name) { this(); this.name = name; } @@ -195,7 +195,7 @@ public class User implements IUser, Serializable { @Override public String toString() { - return "User{" + + return "SsoUser{" + "firstname='" + firstname + '\'' + ", lastname='" + lastname + '\'' + ", email='" + email + '\'' + diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoUserDetails.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoUserDetails.java index 3d36384e4..c6490f647 100644 --- a/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoUserDetails.java +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/SsoUserDetails.java @@ -10,7 +10,7 @@ import java.util.Collection; import java.util.Collections; -public class SsoUserDetails extends User implements UserDetails { +public class SsoUserDetails extends SsoUser implements UserDetails { //from Spring Security, spring adds the prefix @JsonIgnore @@ -23,29 +23,29 @@ public class SsoUserDetails extends User implements UserDetails { super(); } - public SsoUserDetails(User user) { - for (String role : user.getRoles()) { + public SsoUserDetails(SsoUser ssoUser) { + for (String role : ssoUser.getRoles()) { authorities.add(new SimpleGrantedAuthority(rolePrefix + role)); } - super.uniqueid = user.getUniqueid(); - super.systemId = user.getSystemId(); - super.firstname = user.getFirstname(); - super.lastname = user.getLastname(); - super.name = user.getName(); - super.email = user.getEmail(); - super.homeorganization = user.getHomeorganization(); - super.employeeType = Collections.unmodifiableSet(user.getEmployeeType()); - super.unigeChStudentCategory = Collections.unmodifiableSet(user.getUnigeChStudentCategory()); - super.ismemberOf = Collections.unmodifiableSet(user.getIsmemberOf()); - super.roles = Collections.unmodifiableSet(user.getRoles()); - super.homeorganizationtype = user.getHomeorganizationtype(); - super.affiliation = user.getAffiliation(); - super.unigechoucode = user.getUnigechoucode(); - super.unigechstudentoucode = user.getUnigechstudentoucode(); - super.unigechemployeeoucode = user.getUnigechemployeeoucode(); - super.orgunitdn = user.getOrgunitdn(); - super.matriculationnumber = user.getMatriculationnumber(); - super.allProperties=user.getAllProperties(); + super.uniqueid = ssoUser.getUniqueid(); + super.systemId = ssoUser.getSystemId(); + super.firstname = ssoUser.getFirstname(); + super.lastname = ssoUser.getLastname(); + super.name = ssoUser.getName(); + super.email = ssoUser.getEmail(); + super.homeorganization = ssoUser.getHomeorganization(); + super.employeeType = Collections.unmodifiableSet(ssoUser.getEmployeeType()); + super.unigeChStudentCategory = Collections.unmodifiableSet(ssoUser.getUnigeChStudentCategory()); + super.ismemberOf = Collections.unmodifiableSet(ssoUser.getIsmemberOf()); + super.roles = Collections.unmodifiableSet(ssoUser.getRoles()); + super.homeorganizationtype = ssoUser.getHomeorganizationtype(); + super.affiliation = ssoUser.getAffiliation(); + super.unigechoucode = ssoUser.getUnigechoucode(); + super.unigechstudentoucode = ssoUser.getUnigechstudentoucode(); + super.unigechemployeeoucode = ssoUser.getUnigechemployeeoucode(); + super.orgunitdn = ssoUser.getOrgunitdn(); + super.matriculationnumber = ssoUser.getMatriculationnumber(); + super.allProperties= ssoUser.getAllProperties(); } @Override diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/UserFactory.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/UserFactory.java index caa26aaf5..47b686c9e 100644 --- a/solidify-auth/src/main/java/ch/unige/solidify/auth/UserFactory.java +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/UserFactory.java @@ -1,7 +1,6 @@ package ch.unige.solidify.auth; -import ch.unige.solidify.auth.User; import ch.unige.solidify.auth.oidc.OidcUserFactory; import ch.unige.solidify.auth.shibboleth.ShibbolethUserFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -12,7 +11,6 @@ import java.util.Map; @Component class UserFactory { - private static final String OIDC_ACCESS_TOKEN = "oidc_access_token"; @Autowired private ShibbolethUserFactory shibbolethUserFactory; @@ -20,8 +18,8 @@ class UserFactory { @Autowired private OidcUserFactory oidcUserFactory; - User get(Map<String, String> headers) { - if (headers.containsKey(OIDC_ACCESS_TOKEN)) { + SsoUser get(Map<String, String> headers) { + if (headers.containsKey(OidcUserFactory.OIDC_ACCESS_TOKEN)) { return oidcUserFactory.get(headers); } else { return shibbolethUserFactory.get(headers); diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/config/SsoAppRoleConfig.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/config/SsoAppRoleConfig.java index 8dba92126..e1dbe3e13 100644 --- a/solidify-auth/src/main/java/ch/unige/solidify/auth/config/SsoAppRoleConfig.java +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/config/SsoAppRoleConfig.java @@ -62,7 +62,6 @@ public class SsoAppRoleConfig implements Serializable { this.criteria = criteria; } - @Override public boolean isEmpty() { return this.role == null || this.criteriaValues == null || this.criteriaValues.isEmpty(); diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/oidc/OidcUserFactory.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/oidc/OidcUserFactory.java index 287436aa5..52df42c5d 100644 --- a/solidify-auth/src/main/java/ch/unige/solidify/auth/oidc/OidcUserFactory.java +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/oidc/OidcUserFactory.java @@ -1,7 +1,7 @@ package ch.unige.solidify.auth.oidc; -import ch.unige.solidify.auth.User; -import ch.unige.solidify.auth.util.UserData; +import ch.unige.solidify.auth.SsoUser; +import ch.unige.solidify.auth.util.SsoUserData; import org.springframework.stereotype.Component; import java.util.Map; @@ -9,9 +9,12 @@ import java.util.Map; @Component public class OidcUserFactory { - public User get(Map<String, String> attributes) { - UserData userData = new UserData(); + public static final String OIDC_ACCESS_TOKEN = "oidc_access_token"; + + public SsoUser get(Map<String, String> attributes) { + SsoUserData userData = new SsoUserData(); userData.setUniqueid(attributes.get("oidc_claim_sub")); + userData.setOauthToken(attributes.get(OIDC_ACCESS_TOKEN)); return userData; } } diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/shibboleth/ShibbolethUserFactory.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/shibboleth/ShibbolethUserFactory.java index 770fd460e..14f61e899 100644 --- a/solidify-auth/src/main/java/ch/unige/solidify/auth/shibboleth/ShibbolethUserFactory.java +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/shibboleth/ShibbolethUserFactory.java @@ -1,8 +1,8 @@ package ch.unige.solidify.auth.shibboleth; import ch.unige.solidify.auth.config.SsoAppRoleConfig; -import ch.unige.solidify.auth.User; -import ch.unige.solidify.auth.util.UserData; +import ch.unige.solidify.auth.SsoUser; +import ch.unige.solidify.auth.util.SsoUserData; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -14,7 +14,7 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * Read user's attributes from HTTP headers and returns a User object from them. + * Read user's attributes from HTTP headers and returns a SsoUser object from them. * * @link https://aai-viewer.switch.ch/aai/ */ @@ -34,7 +34,7 @@ public class ShibbolethUserFactory { this.ssoRoleConfig = ssoRoleConfig; } - public User get(Map<String, String> attributes) { + public SsoUser get(Map<String, String> attributes) { return new Reader(attributes, config, ssoRoleConfig); } @@ -42,7 +42,7 @@ public class ShibbolethUserFactory { * Read AAI attributes from the a (Http Header) map */ @XmlRootElement - public static class Reader extends UserData { + public static class Reader extends SsoUserData { private Map<String, String> attributes; private ShibbolethHeaderConfig config; diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/util/MockSsoUserSecurityContextFactory.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/util/MockSsoUserSecurityContextFactory.java index 06756bf92..62bd69822 100644 --- a/solidify-auth/src/main/java/ch/unige/solidify/auth/util/MockSsoUserSecurityContextFactory.java +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/util/MockSsoUserSecurityContextFactory.java @@ -2,8 +2,8 @@ package ch.unige.solidify.auth.util; import ch.unige.solidify.auth.SsoAuthentication; +import ch.unige.solidify.auth.SsoUser; import ch.unige.solidify.auth.SsoUserDetails; -import ch.unige.solidify.auth.User; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; @@ -17,15 +17,15 @@ public class MockSsoUserSecurityContextFactory @Override public SecurityContext createSecurityContext(WithSsoMockUser unigeShibboMockUser) { SecurityContext context = SecurityContextHolder.createEmptyContext(); - User user = getUser(unigeShibboMockUser); - SsoUserDetails principal = new SsoUserDetails(user); + SsoUser ssoUser = getUser(unigeShibboMockUser); + SsoUserDetails principal = new SsoUserDetails(ssoUser); Authentication auth = new SsoAuthentication(principal); context.setAuthentication(auth); return context; } - private User getUser(WithSsoMockUser mockUser) { - UserData userData = new UserData(); + private SsoUser getUser(WithSsoMockUser mockUser) { + SsoUserData userData = new SsoUserData(); userData.setSystemId(mockUser.systemId()); userData.setUniqueid(mockUser.uniqueid()); diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/util/UserData.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/util/SsoUserData.java similarity index 96% rename from solidify-auth/src/main/java/ch/unige/solidify/auth/util/UserData.java rename to solidify-auth/src/main/java/ch/unige/solidify/auth/util/SsoUserData.java index a7ea0adf8..57faeb404 100644 --- a/solidify-auth/src/main/java/ch/unige/solidify/auth/util/UserData.java +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/util/SsoUserData.java @@ -1,6 +1,6 @@ package ch.unige.solidify.auth.util; -import ch.unige.solidify.auth.User; +import ch.unige.solidify.auth.SsoUser; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import java.io.Serializable; @@ -11,7 +11,7 @@ import java.util.Set; * Contains user's data. Properties are read/write, useful for testing purposes. */ @JsonIgnoreProperties(ignoreUnknown = true) -public class UserData extends User implements Serializable { +public class SsoUserData extends SsoUser implements Serializable { public void setAllProperties(Map<String, String> allProperties) { super.allProperties = allProperties; @@ -98,7 +98,7 @@ public class UserData extends User implements Serializable { @Override public String toString() { - return "UserData{" + + return "SsoUserData{" + "firstname='" + firstname + '\'' + ", lastname='" + lastname + '\'' + ", email='" + email + '\'' + diff --git a/solidify-auth/src/main/java/ch/unige/solidify/auth/util/WithSsoMockUser.java b/solidify-auth/src/main/java/ch/unige/solidify/auth/util/WithSsoMockUser.java index 085f655eb..1c1804191 100644 --- a/solidify-auth/src/main/java/ch/unige/solidify/auth/util/WithSsoMockUser.java +++ b/solidify-auth/src/main/java/ch/unige/solidify/auth/util/WithSsoMockUser.java @@ -13,6 +13,7 @@ public @interface WithSsoMockUser { long systemId() default 0; + String oauthToken() default ""; String firstName() default "mock firstname"; @@ -47,6 +48,7 @@ public @interface WithSsoMockUser { String matriculationnumber() default ""; String[] allPropertiesName() default {}; + String[] allPropertiesValue() default {}; diff --git a/solidify-auth/src/test/java/ch/unige/solidify/test/auth/AuthMockToollTest.java b/solidify-auth/src/test/java/ch/unige/solidify/test/auth/AuthMockToollTest.java new file mode 100644 index 000000000..3844055eb --- /dev/null +++ b/solidify-auth/src/test/java/ch/unige/solidify/test/auth/AuthMockToollTest.java @@ -0,0 +1,44 @@ +package ch.unige.solidify.test.auth; + +import ch.unige.solidify.auth.util.WithSsoMockUser; +import ch.unige.solidify.test.auth.config.MockApplication; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import static org.hamcrest.CoreMatchers.is; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * this class is used to test the 2 mock tools + * WithSsoMockUser and MockSsoUserSecurityContextFactory + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = MockApplication.class) +@AutoConfigureMockMvc +@WithSsoMockUser(uniqueid = "123456@unige.ch", name = "Test", roles = {"editor"}, + allPropertiesName = {"unigechemployeeuid", "unigechstudentuid"}, + allPropertiesValue = {"wangr", "testw"}) +public class AuthMockToollTest { + + @Autowired + private MockMvc mockMvc; + + + @Test + public void testLoggedUser() throws Exception { + mockMvc.perform(get("/api/user")) + .andExpect(status().isOk()) + .andExpect(content().contentType("application/json;charset=UTF-8")) + .andExpect(jsonPath("uniqueid", is("123456@unige.ch"))) + .andDo(print()); + } + + +} diff --git a/solidify-auth/src/test/java/ch/unige/solidify/test/auth/ShibboFilterTest.java b/solidify-auth/src/test/java/ch/unige/solidify/test/auth/ShibboFilterTest.java new file mode 100644 index 000000000..c5bc3cc38 --- /dev/null +++ b/solidify-auth/src/test/java/ch/unige/solidify/test/auth/ShibboFilterTest.java @@ -0,0 +1,80 @@ +package ch.unige.solidify.test.auth; + +import ch.unige.solidify.auth.SsoAuthenticationFilter; +import ch.unige.solidify.test.auth.config.MockApplication; +import ch.unige.solidify.test.auth.data.MockData; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpHeaders; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.util.NestedServletException; + +import java.util.Map; + + +/** + * unit test for SsoAuthenticationFilter + */ + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = MockApplication.class) +public class ShibboFilterTest { + + @Autowired + private SsoAuthenticationFilter ssoAuthenticationFilter; + + @Autowired + private WebApplicationContext context; + + + private MockMvc mockMvc; + private MockHttpServletRequestBuilder requestBuilder; + + @Before + public void before() { + mockMvc = MockMvcBuilders.webAppContextSetup(context).addFilters(ssoAuthenticationFilter).build(); + } + + + @Test + public void tetShibboUser() throws Exception { + HttpHeaders shibboHeaders = new HttpHeaders(); + Map<String, String> shibboHeadersMockData = MockData.getShibboHeadersMockData(); + for (String key : shibboHeadersMockData.keySet()) { + shibboHeaders.set(key, shibboHeadersMockData.get(key)); + } + requestBuilder = MockMvcRequestBuilders.get("/api/user").headers(shibboHeaders); + mockMvc.perform(requestBuilder).andExpect(MockMvcResultMatchers.status().isOk()); + } + + /** + * this call will throw exception "access is denied" + * because mocked OIDC user don't have role "editor" + */ + @Test(expected = NestedServletException.class) + public void testOidcUser() throws Exception { + HttpHeaders oidcUeaders = new HttpHeaders(); + Map<String, String> oidcHeadersMockData = MockData.getOidcHeadersMockData(); + for (String key : oidcHeadersMockData.keySet()) { + oidcUeaders.set(key, oidcHeadersMockData.get(key)); + } + requestBuilder = MockMvcRequestBuilders.get("/api/user").headers(oidcUeaders); + mockMvc.perform(requestBuilder).andExpect(MockMvcResultMatchers.status().isOk()); + } + + @Test + public void testEmptyUser() throws Exception { + HttpHeaders emptyHeader = new HttpHeaders(); + requestBuilder = MockMvcRequestBuilders.get("/api/greet").headers(emptyHeader); + mockMvc.perform(requestBuilder).andExpect(MockMvcResultMatchers.status().isOk()); + } +} diff --git a/solidify-auth/src/test/java/ch/unige/solidify/test/auth/ShibbolethSsoUserTest.java b/solidify-auth/src/test/java/ch/unige/solidify/test/auth/ShibbolethSsoUserTest.java new file mode 100644 index 000000000..d02070673 --- /dev/null +++ b/solidify-auth/src/test/java/ch/unige/solidify/test/auth/ShibbolethSsoUserTest.java @@ -0,0 +1,191 @@ +package ch.unige.solidify.test.auth; + +import ch.unige.solidify.auth.SsoAuthentication; +import ch.unige.solidify.auth.SsoUser; +import ch.unige.solidify.auth.SsoUserDetails; +import ch.unige.solidify.auth.config.SsoAppRoleConfig; +import ch.unige.solidify.auth.oidc.OidcUserFactory; +import ch.unige.solidify.auth.shibboleth.ShibbolethHeaderConfig; +import ch.unige.solidify.auth.shibboleth.ShibbolethUserFactory; +import ch.unige.solidify.auth.util.SsoUserData; +import ch.unige.solidify.model.User; +import ch.unige.solidify.test.auth.config.MockApplication; +import ch.unige.solidify.test.auth.data.MockData; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.*; + +/** + * unit test for + * Creation of a user from HashMap who contains shibboleth headers value + * Creation of a user from HashMap who contains OID headers value + * class SsoUserdetails and SsoAuthentication + */ + +@RunWith(SpringRunner.class) +@TestConfiguration +@SpringBootTest(classes = MockApplication.class) +public class ShibbolethSsoUserTest { + + + @Autowired + private ShibbolethHeaderConfig config; + @Autowired + private SsoAppRoleConfig ssoRoleConfig; + + private Map<String, String> oidcHeaders = null; + private Map<String, String> shibboHeaders = null; + + @Before + public void setup() { + oidcHeaders = MockData.getOidcHeadersMockData(); + for (String key : oidcHeaders.keySet()) { + oidcHeaders.put(key, oidcHeaders.get(key)); + } + + shibboHeaders = MockData.getShibboHeadersMockData(); + for (String key : shibboHeaders.keySet()) { + shibboHeaders.put(key, shibboHeaders.get(key)); + } + } + + @Test + public void testConfig() { + List<SsoAppRoleConfig.Group> groups = ssoRoleConfig.getGroups(); + for (SsoAppRoleConfig.Group group : groups) { + String value = group.getRole(); + group.setRole(null); + assertTrue(group.isEmpty()); + group.setRole(value); + + + List<String> values = group.getCriteriaValues(); + group.setCriteriaValues(null); + assertTrue(group.isEmpty()); + + group.setCriteriaValues(new ArrayList<>()); + assertTrue(group.isEmpty()); + group.setCriteriaValues(values); + + } + } + + + @Test + public void testShibboUser() { + ShibbolethUserFactory userFactory = new ShibbolethUserFactory(config, ssoRoleConfig); + SsoUser ssoUser = userFactory.get(shibboHeaders); + assertTrue(ssoUser.toString().length() > 10); + assertTrue(MockData.ShibboSystemId == ssoUser.getSystemId()); + Set<String> roles = ssoUser.getRoles(); + assertEquals(MockData.ShibboUniqueId, ssoUser.getUniqueid()); + assertTrue(roles.contains("techAdmin")); + assertTrue(roles.contains("editor")); + Map<String, String> allProperties = ssoUser.getAllProperties(); + assertNotNull(allProperties); + shibboHeaders.put("uid", "test"); + assertEquals( "test", ssoUser.getAllProperties().get("uid")); + } + + @Test + public void testOcidcIdUser() { + OidcUserFactory userFactory = new OidcUserFactory(); + User user = userFactory.get(oidcHeaders); + assertTrue(user.toString().length() > 10); + assertEquals(MockData.OcidcId, user.getUniqueid()); + assertEquals(MockData.oauthAccessToken, user.getOauthToken()); + } + + + @Test(expected = IllegalStateException.class) + public void testSsoUserPassword() { + SsoUserDetails details = new SsoUserDetails(); + assertNotNull(details.getPassword()); + } + + @Test + public void testWrongValue() { + ShibbolethUserFactory userFactory = new ShibbolethUserFactory(config, ssoRoleConfig); + //unique id wrong format + String value = shibboHeaders.get("uniqueid"); + shibboHeaders.put("uniqueid", "test@unige.ch"); + SsoUser ssoUser = userFactory.get(shibboHeaders); + shibboHeaders.put("uniqueid", value); + assertEquals(0, ssoUser.getSystemId()); + + String givenname = shibboHeaders.get("givenname"); + String lastname = shibboHeaders.get("lastname"); + + //without surname + shibboHeaders.put("surname", null); + ssoUser = userFactory.get(shibboHeaders); + shibboHeaders.put("surname", lastname); + assertEquals(givenname, ssoUser.getName()); + + //without first + shibboHeaders.put("givenname", null); + ssoUser = userFactory.get(shibboHeaders); + shibboHeaders.put("givenname", givenname); + assertEquals(lastname, ssoUser.getName()); + + //without first and laist + shibboHeaders.put("givenname", null); + shibboHeaders.put("surname", null); + ssoUser = userFactory.get(shibboHeaders); + shibboHeaders.put("givenname", givenname); + shibboHeaders.put("surname", lastname); + assertEquals("", ssoUser.getName()); + + } + + @Test + public void testSsoUserDetail() { + SsoUserDetails details = new SsoUserDetails(); + assertTrue(details.isAccountNonExpired()); + assertTrue(details.isAccountNonLocked()); + assertTrue(details.isCredentialsNonExpired()); + assertTrue(details.isEnabled()); + + SsoUserData data = new SsoUserData(); + data.setUniqueid("3123@test"); + SsoUserDetails details2 = new SsoUserDetails(data); + assertTrue(!details2.isEmpty()); + assertNotNull(details2.getUsername()); + } + + + @Test(expected = IllegalStateException.class) + public void testAuthenticationCredential() { + SsoUserDetails details = new SsoUserDetails(); + SsoAuthentication aut = new SsoAuthentication(details); + assertNotNull(aut.getCredentials()); + } + + @Test(expected = IllegalStateException.class) + public void testSetAuthenticatedTrue() { + SsoUserDetails details = new SsoUserDetails(); + SsoAuthentication aut = new SsoAuthentication(details); + aut.setAuthenticated(true); + } + + @Test + public void testSetAuthenticatedFalse() { + SsoUserDetails details = new SsoUserDetails(); + SsoAuthentication aut = new SsoAuthentication(details); + aut.setAuthenticated(false); + assertNull(aut.getDetails()); + } + + +} diff --git a/solidify-auth/src/test/java/ch/unige/solidify/test/auth/config/MockApplication.java b/solidify-auth/src/test/java/ch/unige/solidify/test/auth/config/MockApplication.java new file mode 100644 index 000000000..d496c45fb --- /dev/null +++ b/solidify-auth/src/test/java/ch/unige/solidify/test/auth/config/MockApplication.java @@ -0,0 +1,28 @@ +package ch.unige.solidify.test.auth.config; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; + +@SpringBootApplication +@EnableWebSecurity +@EnableGlobalMethodSecurity(jsr250Enabled = true, proxyTargetClass = true, prePostEnabled = true) +@ComponentScan(basePackages = {"ch.unige.solidify.auth.**", "ch.unige.solidify.test.auth.**"}) +@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}) +public class MockApplication extends SpringBootServletInitializer { + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(MockApplication.class); + } + + public static void main(String[] args) { + SpringApplication.run(MockApplication.class, args); + } + +} diff --git a/solidify-auth/src/test/java/ch/unige/solidify/test/auth/controllers/MockUserController.java b/solidify-auth/src/test/java/ch/unige/solidify/test/auth/controllers/MockUserController.java new file mode 100644 index 000000000..818d84e38 --- /dev/null +++ b/solidify-auth/src/test/java/ch/unige/solidify/test/auth/controllers/MockUserController.java @@ -0,0 +1,27 @@ +package ch.unige.solidify.test.auth.controllers; + +import ch.unige.solidify.auth.SsoUserDetails; +import org.springframework.http.MediaType; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.security.RolesAllowed; + +@RestController +@RequestMapping(value = "api", produces = MediaType.APPLICATION_JSON_VALUE) +public class MockUserController { + + @GetMapping("/user") + @RolesAllowed("editor") + public SsoUserDetails currentUser() { + return (SsoUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + } + + @GetMapping("/greet") + public String greeting() { + return "hello"; + } + +} diff --git a/solidify-auth/src/test/java/ch/unige/solidify/test/auth/data/MockData.java b/solidify-auth/src/test/java/ch/unige/solidify/test/auth/data/MockData.java new file mode 100644 index 000000000..905391c81 --- /dev/null +++ b/solidify-auth/src/test/java/ch/unige/solidify/test/auth/data/MockData.java @@ -0,0 +1,48 @@ +package ch.unige.solidify.test.auth.data; + +import java.util.HashMap; +import java.util.Map; + +public class MockData { + + public static String ShibboUniqueId = "135192@unige.ch"; + public static long ShibboSystemId = 135192; + public static String OcidcId = "747918@unige.ch"; + public static String oauthAccessToken="c8ed71ac-368e-4b43-8dc5-39093a5eef84"; + + + private static Map<String, String> shibboHeadersMockData = new HashMap<>(); + + private static Map<String, String> oidcHeadersMockData = new HashMap<>(); + + + static { + oidcHeadersMockData.put("oidc_access_token",oauthAccessToken ); + oidcHeadersMockData.put("oidc_claim_sub", OcidcId); + shibboHeadersMockData.put("ismemberof", "adm:editor;adm:admin;"); + shibboHeadersMockData.put("homeorganization", "unige.ch"); + shibboHeadersMockData.put("givenname", "TEST-prénoms"); + shibboHeadersMockData.put("lastname", "TEST-nom"); + shibboHeadersMockData.put("uniqueid", ShibboUniqueId); + shibboHeadersMockData.put("mail", "test@unige.ch"); + shibboHeadersMockData.put("homeorganizationtype", "university"); + shibboHeadersMockData.put("affiliation", "staff;student"); + shibboHeadersMockData.put("employeetype", "etu;pat;ens"); + shibboHeadersMockData.put("unigechoucode", ""); + shibboHeadersMockData.put("unigechstudentoucode", ""); + shibboHeadersMockData.put("unigechemployeeoucode", ""); + shibboHeadersMockData.put("unigechstudentcategory", "master;mobile"); + shibboHeadersMockData.put("orgunit-dn", "ou=acad,ou=block,o=unige"); + shibboHeadersMockData.put("matriculationNumber", "1234567"); + shibboHeadersMockData.put("unigechemployeeuid", "wangr"); + } + + public static Map<String, String> getShibboHeadersMockData() { + return shibboHeadersMockData; + } + + public static Map<String, String> getOidcHeadersMockData() { + return oidcHeadersMockData; + } + +} diff --git a/solidify-auth/src/test/resources/config/application.yaml b/solidify-auth/src/test/resources/config/application.yaml new file mode 100644 index 000000000..22d10d0d0 --- /dev/null +++ b/solidify-auth/src/test/resources/config/application.yaml @@ -0,0 +1,27 @@ +spring: + + jersey: + type: servlet + + jackson: + date-format: com.fasterxml.jackson.databind.util.ISO8601DateFormat + +server: + servlet: + contextPath: /tech/auth +sol: + + sso: + groups: + - + role: "techAdmin" + org: "unige.ch" + criteria: "uniqueId" + criteriaValues: + - "135192@unige.ch" + - + role: "editor" + org: "unige.ch" + criteria: "isMemberOf" + criteriaValues: + - "adm:editor" diff --git a/solidify-controller/pom.xml b/solidify-controller/pom.xml index 388ef0350..12ee85761 100644 --- a/solidify-controller/pom.xml +++ b/solidify-controller/pom.xml @@ -1,86 +1,107 @@ -<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> - <parent> - <groupId>ch.unige.solidify</groupId> - <artifactId>solidify-parent</artifactId> - <version>0.3.0-SNAPSHOT</version> - </parent> +<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> + <parent> + <groupId>ch.unige.solidify</groupId> + <artifactId>solidify-parent</artifactId> + <version>0.3.0-SNAPSHOT</version> + </parent> - <artifactId>solidify-controller</artifactId> - <name>Solidify Controller</name> - <description>Solidify Controller library</description> + <artifactId>solidify-controller</artifactId> + <name>Solidify Controller</name> + <description>Solidify Controller library</description> - <dependencies> - <!--Solidify dependencies --> - <dependency> - <groupId>ch.unige.solidify</groupId> - <artifactId>solidify-model</artifactId> - </dependency> - <!--Rest dependencies --> - <dependency> - <groupId>commons-validator</groupId> - <artifactId>commons-validator</artifactId> - <version>1.6</version> - </dependency> - <dependency> - <groupId>org.json</groupId> - <artifactId>json</artifactId> - </dependency> - <dependency> - <groupId>com.fasterxml.jackson.datatype</groupId> - <artifactId>jackson-datatype-jsr310</artifactId> - </dependency> - <!-- <dependency> --> - <!-- <groupId>com.fasterxml.jackson.dataformat</groupId> --> - <!-- <artifactId>jackson-dataformat-xml</artifactId> --> - <!-- </dependency> --> - <dependency> - <groupId>org.apache.httpcomponents</groupId> - <artifactId>httpasyncclient</artifactId> - </dependency> + <dependencies> + <!--Solidify dependencies --> + <dependency> + <groupId>ch.unige.solidify</groupId> + <artifactId>solidify-model</artifactId> + </dependency> + <dependency> + <groupId>ch.unige.solidify</groupId> + <artifactId>solidify-auth</artifactId> + </dependency> + <!--Rest dependencies --> + <dependency> + <groupId>commons-validator</groupId> + <artifactId>commons-validator</artifactId> + <version>1.6</version> + </dependency> + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.datatype</groupId> + <artifactId>jackson-datatype-jsr310</artifactId> + </dependency> + <!-- <dependency> --> + <!-- <groupId>com.fasterxml.jackson.dataformat</groupId> --> + <!-- <artifactId>jackson-dataformat-xml</artifactId> --> + <!-- </dependency> --> + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpasyncclient</artifactId> + </dependency> - <!--Database dependencies --> - <dependency> - <groupId>org.hibernate</groupId> - <artifactId>hibernate-java8</artifactId> - </dependency> - <dependency> - <groupId>mysql</groupId> - <artifactId>mysql-connector-java</artifactId> - </dependency> + <!--Database dependencies --> + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-java8</artifactId> + </dependency> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + </dependency> - <!--Spring dependencies --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-web-services</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-hateoas</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-security</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-data-jpa</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-activemq</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-actuator</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.cloud</groupId> - <artifactId>spring-cloud-context</artifactId> - </dependency> - <dependency> + <!--Spring dependencies --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web-services</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-hateoas</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-data-jpa</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-activemq</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-actuator</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.cloud</groupId> + <artifactId>spring-cloud-context</artifactId> + </dependency> + <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> </dependency> - </dependencies> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-test</artifactId> + </dependency> + + </dependencies> </project> diff --git a/solidify-controller/src/main/java/ch/unige/solidify/controller/SubResourceController.java b/solidify-controller/src/main/java/ch/unige/solidify/controller/SubResourceController.java index 5fff6f2c1..1f6b8dbb2 100644 --- a/solidify-controller/src/main/java/ch/unige/solidify/controller/SubResourceController.java +++ b/solidify-controller/src/main/java/ch/unige/solidify/controller/SubResourceController.java @@ -26,11 +26,10 @@ import ch.unige.solidify.rest.Resource; import ch.unige.solidify.rest.SubResourceContainer; import ch.unige.solidify.rest.Tool; import ch.unige.solidify.security.NoPermissions; -import ch.unige.solidify.security.UserPermissions; import ch.unige.solidify.service.CompositeResourceService; import ch.unige.solidify.util.FileTool; -@UserPermissions + public abstract class SubResourceController<T extends Resource<T> & SubResourceContainer, V extends Resource<V>> extends SolidifyController { diff --git a/solidify-controller/src/main/java/ch/unige/solidify/controller/UserController.java b/solidify-controller/src/main/java/ch/unige/solidify/controller/UserController.java new file mode 100644 index 000000000..dd586ed51 --- /dev/null +++ b/solidify-controller/src/main/java/ch/unige/solidify/controller/UserController.java @@ -0,0 +1,23 @@ +package ch.unige.solidify.controller; + +import ch.unige.solidify.model.User; +import ch.unige.solidify.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class UserController { + @Autowired + private UserService userService; + + /** + * Returns user data provided by the SSO for the current user. + * + * @return User's sso data + */ + @RequestMapping("/api/user") + public User user() { + return userService.current(); + } +} \ No newline at end of file diff --git a/solidify-controller/src/main/java/ch/unige/solidify/service/UserService.java b/solidify-controller/src/main/java/ch/unige/solidify/service/UserService.java new file mode 100644 index 000000000..ead16ccb1 --- /dev/null +++ b/solidify-controller/src/main/java/ch/unige/solidify/service/UserService.java @@ -0,0 +1,23 @@ +package ch.unige.solidify.service; + +import ch.unige.solidify.model.User; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +@Service +public class UserService { + + public UserService() { + //need an empty constructor + } + + public User current() { + Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + User user = null; + if (principal instanceof User) { + user = (User) principal; + } + return user; + } +} + diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserServiceTest.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserServiceTest.java new file mode 100644 index 000000000..7909b9065 --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserServiceTest.java @@ -0,0 +1,42 @@ +package ch.unige.solidify.test.controller; + + +import ch.unige.solidify.model.User; +import ch.unige.solidify.service.UserService; +import ch.unige.solidify.test.controller.config.MockApplication; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.assertNull; + +@RunWith(SpringRunner.class) +@AutoConfigureMockMvc +@SpringBootTest(classes = MockApplication.class) +public class UserServiceTest { + private String userName = "UserTest"; + + @Autowired + private UserService userService; + + + @Before + public void setUp() { + SecurityContextHolder.getContext() + .setAuthentication(new AnonymousAuthenticationToken("KeyTest", userName, AuthorityUtils + .createAuthorityList("ROLE_ONE", "ROLE_TWO"))); + } + + @Test + public void testNonSsoUser() { + User user = userService.current(); + assertNull(user); + } +} diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java new file mode 100644 index 000000000..5c8dbe2d2 --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java @@ -0,0 +1,66 @@ +package ch.unige.solidify.test.controller; + + +import ch.unige.solidify.auth.util.SsoUserData; +import ch.unige.solidify.service.UserService; +import ch.unige.solidify.test.controller.config.MockApplication; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import static org.hamcrest.CoreMatchers.is; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + + +@RunWith(SpringRunner.class) +@AutoConfigureMockMvc +@SpringBootTest(classes = MockApplication.class) +public class UserShibboletControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private UserService userService; + + private static final String HEADER_UNIQUEID_VALUE = "123456@unige.ch"; + + @Test + @WithMockUser() + public void shibboUserTest() throws Exception { + SsoUserData uTest = new SsoUserData(); + uTest.setEmail("test@unige.ch"); + uTest.setLastname("LastnameTest"); + uTest.setFirstname("FirstnameTest"); + uTest.setUniqueid(HEADER_UNIQUEID_VALUE); + // User uTest = User.create(u); + when(userService.current()).thenReturn(uTest); + mockMvc.perform(get("/api/user")) + .andExpect(status().isOk()).andExpect(content().contentType("application/json;charset=UTF-8")) + .andExpect(jsonPath("$.email", is("test@unige.ch"))) + .andExpect(jsonPath("$.firstname", is("FirstnameTest"))) + .andExpect(jsonPath("$.lastname", is("LastnameTest"))) + .andExpect(jsonPath("$.uniqueid", is(HEADER_UNIQUEID_VALUE))) + .andDo(print()); + } + + @Test + @WithMockUser() + public void userTest() throws Exception { + SsoUserData uTest = new SsoUserData(); + // User uTest = User.create(u); + when(userService.current()).thenReturn(uTest); + mockMvc.perform(get("/api/user")) + .andDo(print()); + } +} + diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java new file mode 100644 index 000000000..62c2ed66d --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java @@ -0,0 +1,29 @@ +package ch.unige.solidify.test.controller.config; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; + +@SpringBootApplication +@EnableWebSecurity +@EnableGlobalMethodSecurity(jsr250Enabled = true, proxyTargetClass = true, prePostEnabled = true) +@ComponentScan(basePackages = {"ch.unige.solidify.controller", "ch.unige.solidify.service", + "ch.unige.solidify.test.**"}) +@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}) +public class MockApplication extends SpringBootServletInitializer { + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(MockApplication.class); + } + + public static void main(String[] args) { + SpringApplication.run(MockApplication.class, args); + } + +} diff --git a/solidify-model/src/main/java/ch/unige/solidify/model/IUser.java b/solidify-model/src/main/java/ch/unige/solidify/model/IUser.java deleted file mode 100644 index 32262fd0f..000000000 --- a/solidify-model/src/main/java/ch/unige/solidify/model/IUser.java +++ /dev/null @@ -1,13 +0,0 @@ -package ch.unige.solidify.model; - -public interface IUser { - public String getFirstname(); - - public String getLastname(); - - public String getEmail(); - - public String getUniqueid(); - - public String getOauthToken(); -} diff --git a/solidify-model/src/main/java/ch/unige/solidify/model/User.java b/solidify-model/src/main/java/ch/unige/solidify/model/User.java new file mode 100644 index 000000000..f461bc84a --- /dev/null +++ b/solidify-model/src/main/java/ch/unige/solidify/model/User.java @@ -0,0 +1,25 @@ +package ch.unige.solidify.model; + +import java.util.Map; +import java.util.Set; + +public interface User { + public String getFirstname(); + + public String getLastname(); + + public String getEmail(); + + public String getHomeorganization(); + + public String getUniqueid(); + + public String getOauthToken(); + + public String getName(); + + public Map<String, String> getAllProperties(); + + public Set<String> getRoles(); +} + diff --git a/solidify-util/src/main/java/ch/unige/solidify/config/SolidifyProvider.java b/solidify-util/src/main/java/ch/unige/solidify/config/SolidifyProvider.java index e3fdbd3c9..a988ef467 100644 --- a/solidify-util/src/main/java/ch/unige/solidify/config/SolidifyProvider.java +++ b/solidify-util/src/main/java/ch/unige/solidify/config/SolidifyProvider.java @@ -7,6 +7,6 @@ public class SolidifyProvider extends Provider { public SolidifyProvider() { super("SolidifyProvider", 1.0, "Solidify Provider"); - this.put("MessageDigest.CRC32", "ch.unige.solidify.util.CRC"); + this.put("MessageDigest.CRC32", "ch.unige.solidify.config.CRC"); } } -- GitLab From 22b1f118f5409b461f3bca8373432b63d564342b Mon Sep 17 00:00:00 2001 From: wangr <raoli.wang@unige.ch> Date: Tue, 6 Nov 2018 09:06:36 +0100 Subject: [PATCH 04/14] feat: add unit test for UserService et UserController --- pom.xml | 2 +- .../test/controller/UserServiceTest.java | 1 - .../UserShibboletControllerTest.java | 18 ++++++++------- .../controller/config/MockApplication.java | 6 ++--- .../src/test/resources/application.yaml | 3 +++ .../src/test/resources/git.properties | 23 +++++++++++++++++++ 6 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 solidify-controller/src/test/resources/application.yaml create mode 100644 solidify-controller/src/test/resources/git.properties diff --git a/pom.xml b/pom.xml index d16f4ef4e..7af71e9a8 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ <solidify.model.version>0.3.0-SNAPSHOT</solidify.model.version> <solidify.controller.version>0.3.0-SNAPSHOT</solidify.controller.version> <!--Libraries properties --> - <spring.cloud.release>Dalston.SR2</spring.cloud.release> + <spring.cloud.release>Finchley.SR1</spring.cloud.release> <bagit.version>5.0.3</bagit.version> <saxon-he.version>9.8.0-8</saxon-he.version> <jaxb.version>2.3.0</jaxb.version> diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserServiceTest.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserServiceTest.java index 7909b9065..319d90303 100644 --- a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserServiceTest.java +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserServiceTest.java @@ -26,7 +26,6 @@ public class UserServiceTest { @Autowired private UserService userService; - @Before public void setUp() { SecurityContextHolder.getContext() diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java index 5c8dbe2d2..d4d3156c5 100644 --- a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java @@ -35,7 +35,7 @@ public class UserShibboletControllerTest { private static final String HEADER_UNIQUEID_VALUE = "123456@unige.ch"; @Test - @WithMockUser() + @WithMockUser(username = "admin", roles = {"techAdmin"}) public void shibboUserTest() throws Exception { SsoUserData uTest = new SsoUserData(); uTest.setEmail("test@unige.ch"); @@ -45,12 +45,14 @@ public class UserShibboletControllerTest { // User uTest = User.create(u); when(userService.current()).thenReturn(uTest); mockMvc.perform(get("/api/user")) - .andExpect(status().isOk()).andExpect(content().contentType("application/json;charset=UTF-8")) - .andExpect(jsonPath("$.email", is("test@unige.ch"))) - .andExpect(jsonPath("$.firstname", is("FirstnameTest"))) - .andExpect(jsonPath("$.lastname", is("LastnameTest"))) - .andExpect(jsonPath("$.uniqueid", is(HEADER_UNIQUEID_VALUE))) - .andDo(print()); + .andExpect(status().isOk()) + .andExpect(content().contentType("application/json;charset=UTF-8")) + .andExpect(jsonPath("$.email", is("test@unige.ch"))) + .andExpect(jsonPath("$.firstname", is("FirstnameTest"))) + .andExpect(jsonPath("$.lastname", is("LastnameTest"))) + .andExpect(jsonPath("$.uniqueid", is(HEADER_UNIQUEID_VALUE))) + .andDo(print()); + } @Test @@ -60,7 +62,7 @@ public class UserShibboletControllerTest { // User uTest = User.create(u); when(userService.current()).thenReturn(uTest); mockMvc.perform(get("/api/user")) - .andDo(print()); + .andDo(print()); } } diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java index 62c2ed66d..d42cd94ba 100644 --- a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java @@ -1,5 +1,6 @@ package ch.unige.solidify.test.controller.config; +import ch.unige.solidify.service.GitInfoService; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -7,15 +8,14 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.FilterType; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @SpringBootApplication @EnableWebSecurity @EnableGlobalMethodSecurity(jsr250Enabled = true, proxyTargetClass = true, prePostEnabled = true) -@ComponentScan(basePackages = {"ch.unige.solidify.controller", "ch.unige.solidify.service", - "ch.unige.solidify.test.**"}) -@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}) +@ComponentScan(basePackages = {"ch.unige.solidify.test.controller.config", "ch.unige.solidify"}) public class MockApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { diff --git a/solidify-controller/src/test/resources/application.yaml b/solidify-controller/src/test/resources/application.yaml new file mode 100644 index 000000000..e31e505cf --- /dev/null +++ b/solidify-controller/src/test/resources/application.yaml @@ -0,0 +1,3 @@ +spring: + application: + name: test \ No newline at end of file diff --git a/solidify-controller/src/test/resources/git.properties b/solidify-controller/src/test/resources/git.properties new file mode 100644 index 000000000..1a72be9aa --- /dev/null +++ b/solidify-controller/src/test/resources/git.properties @@ -0,0 +1,23 @@ +#Generated by Git-Commit-Id-Plugin +#Mon Nov 05 16:59:42 CET 2018 +git.branch=RW_CRUD_validation +git.build.host=CD-5GMSS22 +git.build.time=2018-11-05T16\:59\:42+0100 +git.build.user.email=raoli.wang@unige.ch +git.build.user.name=wangr +git.build.version=1.0.0-SNAPSHOT +git.closest.tag.commit.count= +git.closest.tag.name= +git.commit.id=9616c13b736b38551e894a5dd2f985e2a055ca2f +git.commit.id.abbrev=9616c13 +git.commit.id.describe=9616c13-dirty +git.commit.id.describe-short=9616c13-dirty +git.commit.message.full=feat\: enable security +git.commit.message.short=feat\: enable security +git.commit.time=2018-11-05T14\:02\:04+0100 +git.commit.user.email=raoli.wang@unige.ch +git.commit.user.name=wangr +git.dirty=true +git.remote.origin.url=git@gitlab.fl.unige.ch\:solidify/logistics-domain.git +git.tags= +git.total.commit.count=42 -- GitLab From 4ae978ac3378b6ef1b6bc1d17dc884d5e0c34019 Mon Sep 17 00:00:00 2001 From: wangr <raoli.wang@unige.ch> Date: Tue, 6 Nov 2018 17:02:08 +0100 Subject: [PATCH 05/14] feat: implement search specification in Model module --- .../ResourceReadOnlyController.java | 20 ++- .../src/test/resources/git.properties | 12 +- solidify-model/pom.xml | 156 ++++++++++-------- .../java/ch/unige/solidify/rest/Resource.java | 23 +++ .../specification/SearchSpecification.java | 108 ++++++++++++ .../unige/solidify/util/SearchCriteria.java | 106 ++++++++++++ .../unige/solidify/util/SearchOperation.java | 32 ++++ .../test/config/MockUserJpaConfig.java | 71 ++++++++ .../SearchSpecificationTest.java | 100 +++++++++++ .../test/specification/dao/MockUser.java | 45 +++++ .../specification/dao/MockUserRepository.java | 7 + .../dao/MockUserSearchSpecification.java | 10 ++ .../resources/persistence-test.properties | 9 + .../solidify/config/SolidifyProvider.java | 2 +- 14 files changed, 622 insertions(+), 79 deletions(-) create mode 100644 solidify-model/src/main/java/ch/unige/solidify/specification/SearchSpecification.java create mode 100644 solidify-model/src/main/java/ch/unige/solidify/util/SearchCriteria.java create mode 100644 solidify-model/src/main/java/ch/unige/solidify/util/SearchOperation.java create mode 100644 solidify-model/src/test/java/ch/unige/solidify/test/config/MockUserJpaConfig.java create mode 100644 solidify-model/src/test/java/ch/unige/solidify/test/specification/SearchSpecificationTest.java create mode 100644 solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUser.java create mode 100644 solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUserRepository.java create mode 100644 solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUserSearchSpecification.java create mode 100644 solidify-model/src/test/resources/persistence-test.properties diff --git a/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java b/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java index c05abe14d..f6566425e 100644 --- a/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java +++ b/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java @@ -20,8 +20,7 @@ import ch.unige.solidify.rest.Resource; import ch.unige.solidify.rest.Tool; import ch.unige.solidify.service.CompositeResourceService; -public abstract class ResourceReadOnlyController<T extends Resource<T>> extends SolidifyController - implements ControllerWithHateoasHome { +public abstract class ResourceReadOnlyController<T extends Resource<T>> extends SolidifyController{ @Autowired protected CompositeResourceService<T> itemService; @@ -55,6 +54,23 @@ public abstract class ResourceReadOnlyController<T extends Resource<T>> extends return new ResponseEntity<>(HttpStatus.NOT_FOUND); } + protected Collection<T> setLinks(Page<T> listItem, Pageable pageable) { + + for (final T t : listItem) { + addLinks(t); + } + Collection<T> collection = new Collection<>(listItem, pageable); + collection.add(linkTo(this.getClass()).withSelfRel()); + collection.add(Tool.parentLink((linkTo(this.getClass())).toUriComponentsBuilder()) + .withRel(ActionName.MODULE)); + + this.addSortLinks(linkTo(this.getClass()), collection); + this.addPageLinks(linkTo(this.getClass()), collection, pageable); + addOthersLinks(collection); + return collection; + } + + protected void addLinks(T t) { t.removeLinks(); t.addLinks(linkTo(this.getClass()), true, true); diff --git a/solidify-controller/src/test/resources/git.properties b/solidify-controller/src/test/resources/git.properties index 1a72be9aa..7e4d5401e 100644 --- a/solidify-controller/src/test/resources/git.properties +++ b/solidify-controller/src/test/resources/git.properties @@ -1,10 +1,10 @@ #Generated by Git-Commit-Id-Plugin #Mon Nov 05 16:59:42 CET 2018 -git.branch=RW_CRUD_validation +git.branch=CONTROLLER_validation git.build.host=CD-5GMSS22 git.build.time=2018-11-05T16\:59\:42+0100 -git.build.user.email=raoli.wang@unige.ch -git.build.user.name=wangr +git.build.user.email=rocksolid@unige.ch +git.build.user.name=rocksolid git.build.version=1.0.0-SNAPSHOT git.closest.tag.commit.count= git.closest.tag.name= @@ -15,9 +15,9 @@ git.commit.id.describe-short=9616c13-dirty git.commit.message.full=feat\: enable security git.commit.message.short=feat\: enable security git.commit.time=2018-11-05T14\:02\:04+0100 -git.commit.user.email=raoli.wang@unige.ch -git.commit.user.name=wangr +git.commit.user.email=rocksolid@unige.ch +git.commit.user.name=rocksolidx git.dirty=true -git.remote.origin.url=git@gitlab.fl.unige.ch\:solidify/logistics-domain.git +git.remote.origin.url=git@gitlab.fl.unige.ch\:solidify/one-domain.git git.tags= git.total.commit.count=42 diff --git a/solidify-model/pom.xml b/solidify-model/pom.xml index dcc240973..74cfdbf4a 100644 --- a/solidify-model/pom.xml +++ b/solidify-model/pom.xml @@ -1,73 +1,89 @@ -<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> - <parent> - <groupId>ch.unige.solidify</groupId> - <artifactId>solidify-parent</artifactId> - <version>0.3.0-SNAPSHOT</version> - </parent> +<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> + <parent> + <groupId>ch.unige.solidify</groupId> + <artifactId>solidify-parent</artifactId> + <version>0.3.0-SNAPSHOT</version> + </parent> - <artifactId>solidify-model</artifactId> - <name>Solidify Model</name> - <description>Solidify Model Libray</description> + <artifactId>solidify-model</artifactId> + <name>Solidify Model</name> + <description>Solidify Model Libray</description> - <dependencies> - <!--Solidify dependencies --> - <dependency> - <groupId>ch.unige.solidify</groupId> - <artifactId>solidify-util</artifactId> - </dependency> - <!--Rest dependencies --> - <dependency> - <groupId>commons-validator</groupId> - <artifactId>commons-validator</artifactId> - <version>1.6</version> - </dependency> - <dependency> - <groupId>org.json</groupId> - <artifactId>json</artifactId> - </dependency> - <dependency> - <groupId>com.fasterxml.jackson.datatype</groupId> - <artifactId>jackson-datatype-jsr310</artifactId> - </dependency> - <!--Spring dependencies --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-data-jpa</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-hateoas</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-test</artifactId> - <scope>test</scope> - <exclusions> - <exclusion> - <groupId>com.vaadin.external.google</groupId> - <artifactId>android-json</artifactId> - </exclusion> - </exclusions> - </dependency> - <!-- JAXB dependencies --> - <dependency> - <groupId>com.sun.xml.bind</groupId> - <artifactId>jaxb-impl</artifactId> - </dependency> - <dependency> - <groupId>com.sun.xml.bind</groupId> - <artifactId>jaxb-core</artifactId> - </dependency> - <!--Packaging dependencies --> - <dependency> - <groupId>gov.loc</groupId> - <artifactId>bagit</artifactId> - </dependency> - <!--XLST dependencies --> - <dependency> - <groupId>net.sf.saxon</groupId> - <artifactId>Saxon-HE</artifactId> - </dependency> - </dependencies> + <dependencies> + <!--Solidify dependencies --> + <dependency> + <groupId>ch.unige.solidify</groupId> + <artifactId>solidify-util</artifactId> + </dependency> + <!--Rest dependencies --> + <dependency> + <groupId>commons-validator</groupId> + <artifactId>commons-validator</artifactId> + <version>1.6</version> + </dependency> + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.datatype</groupId> + <artifactId>jackson-datatype-jsr310</artifactId> + </dependency> + <!--Spring dependencies --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-data-jpa</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-hateoas</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>com.vaadin.external.google</groupId> + <artifactId>android-json</artifactId> + </exclusion> + </exclusions> + </dependency> + <!--dependence for test--> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <version>1.4.197</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-entitymanager</artifactId> + <version>5.3.7.Final</version> + <scope>test</scope> + + </dependency> + <!-- JAXB dependencies --> + <dependency> + <groupId>com.sun.xml.bind</groupId> + <artifactId>jaxb-impl</artifactId> + </dependency> + <dependency> + <groupId>com.sun.xml.bind</groupId> + <artifactId>jaxb-core</artifactId> + </dependency> + <!--Packaging dependencies --> + <dependency> + <groupId>gov.loc</groupId> + <artifactId>bagit</artifactId> + </dependency> + <!--XLST dependencies --> + <dependency> + <groupId>net.sf.saxon</groupId> + <artifactId>Saxon-HE</artifactId> + </dependency> + </dependencies> </project> diff --git a/solidify-model/src/main/java/ch/unige/solidify/rest/Resource.java b/solidify-model/src/main/java/ch/unige/solidify/rest/Resource.java index c1f2cf015..0885e7e8f 100644 --- a/solidify-model/src/main/java/ch/unige/solidify/rest/Resource.java +++ b/solidify-model/src/main/java/ch/unige/solidify/rest/Resource.java @@ -6,6 +6,7 @@ import java.time.OffsetDateTime; import java.util.List; import java.util.Objects; +import ch.unige.solidify.util.SearchCriteria; import org.springframework.data.jpa.domain.Specification; import org.springframework.hateoas.mvc.ControllerLinkBuilder; @@ -14,6 +15,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import ch.unige.solidify.message.CacheMessage; import ch.unige.solidify.message.ResourceCacheMessage; +import javax.persistence.Transient; + public abstract class Resource<T> extends ResourceBase { public abstract String getResId(); @@ -112,4 +115,24 @@ public abstract class Resource<T> extends ResourceBase { public CacheMessage getCacheMessage() { return new ResourceCacheMessage(this.getClass(), this.getResId()); } + + public Specification<T> getSearchSpecification(SearchCriteria criteria) { + return null; + } + + + @Transient + protected List<SearchCriteria> searchCriterias; + + @Transient + @JsonIgnore + public List<SearchCriteria> getSearchCriterias() { + return searchCriterias; + } + + @Transient + public void setSearchCriterias(List<SearchCriteria> searchCriterias) { + this.searchCriterias = searchCriterias; + } + } \ No newline at end of file diff --git a/solidify-model/src/main/java/ch/unige/solidify/specification/SearchSpecification.java b/solidify-model/src/main/java/ch/unige/solidify/specification/SearchSpecification.java new file mode 100644 index 000000000..2ab1caebf --- /dev/null +++ b/solidify-model/src/main/java/ch/unige/solidify/specification/SearchSpecification.java @@ -0,0 +1,108 @@ +package ch.unige.solidify.specification; + +import ch.unige.solidify.util.SearchCriteria; +import org.springframework.data.jpa.domain.Specification; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; + + +public abstract class SearchSpecification<T> implements Specification<T> { + + private SearchCriteria criteria; + + protected SearchSpecification(SearchCriteria criteria) { + this.criteria = criteria; + } + + private boolean isStringField(Root<T> root, String fieldName) { + try { + Class[] classlist = root.get(fieldName).type().getJavaType().getClasses(); + for (Class c : classlist) { + if (c == String.class) { + return true; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return false; + + } + + @Override + public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) { + + switch (criteria.getOperationType()) { + case EQUALITY: + if (criteria.isCaseInsensitive() && isStringField(root, criteria.getKey())) { + return builder.equal( + builder.lower(root.get(criteria.getKey())), + criteria.getValue().toString().toLowerCase() + ); + } + return builder.equal( + root.get(criteria.getKey()), criteria.getValue() + ); + + case NEGATION: + if (criteria.isCaseInsensitive() && isStringField(root, criteria.getKey())) { + return builder.notEqual( + builder.lower(root.get(criteria.getKey())), + criteria.getValue().toString().toLowerCase() + ); + } + return builder.notEqual(root.get(criteria.getKey()), criteria.getValue()); + + case GREATER_THAN: + if (criteria.isCaseInsensitive() && isStringField(root, criteria.getKey())) { + return builder.greaterThan( + builder.lower(root.get(criteria.getKey())), + criteria.getValue().toString().toLowerCase() + ); + } + return builder.greaterThan(root.<String>get( + criteria.getKey()), criteria.getValue().toString()); + + case LESS_THAN: + if (criteria.isCaseInsensitive() && isStringField(root, criteria.getKey())) { + return builder.lessThan( + builder.lower(root.get(criteria.getKey())), + criteria.getValue().toString().toLowerCase() + ); + } + return builder.lessThan(root.get( + criteria.getKey()), criteria.getValue().toString()); + case STARTS_WITH: + if (criteria.isCaseInsensitive()) { + return builder.like( + builder.lower(root.get(criteria.getKey())), + criteria.getValue().toString().toLowerCase() + "%" + ); + } + return builder.like(root.<String>get(criteria.getKey()), criteria.getValue() + "%"); + case ENDS_WITH: + if (criteria.isCaseInsensitive()) { + return builder.like( + builder.lower(root.get(criteria.getKey())), + "%" + criteria.getValue().toString().toLowerCase() + ); + } + return builder.like(root.<String>get(criteria.getKey()), "%" + criteria.getValue()); + case CONTAINS: + if (criteria.isCaseInsensitive()) { + return builder.like( + builder.lower(root.get(criteria.getKey())), + "%" + criteria.getValue().toString().toLowerCase() + "%" + ); + } + return builder.like(root.<String>get( + criteria.getKey()), "%" + criteria.getValue() + "%"); + default: + return null; + } + } +} diff --git a/solidify-model/src/main/java/ch/unige/solidify/util/SearchCriteria.java b/solidify-model/src/main/java/ch/unige/solidify/util/SearchCriteria.java new file mode 100644 index 000000000..6c9b7c57c --- /dev/null +++ b/solidify-model/src/main/java/ch/unige/solidify/util/SearchCriteria.java @@ -0,0 +1,106 @@ +package ch.unige.solidify.util; + + +import java.io.Serializable; + +public class SearchCriteria implements Serializable { + private String key; + + private SearchOperation operationType; + private String operation; + + private Object value; + + + private boolean caseInsensitive = false; + private String caseType; + + public SearchCriteria() { + super(); + } + public SearchCriteria(final String key, final String operation, final Object value) { + super(); + this.operation = operation; + this.key = key; + this.operationType = SearchOperation.getOperation(operation.charAt(0)); + this.value = value; + } + + public SearchCriteria(final String key, SearchOperation operationType, final Object value) { + super(); + this.key = key; + this.operationType = operationType; + this.value = value; + } + + + + public SearchCriteria(final String caseType, final String key, final String operation, + final Object value) { + super(); + this.caseInsensitive = + (caseType != null && caseType.equals(SearchOperation.CASE_INSENSITIVE_FLAG)); + this.key = key; + this.operation = operation; + this.operationType = SearchOperation.getOperation(operation.charAt(0)); + this.value = value; + } + + public SearchCriteria(final String caseType, final String key, final SearchOperation operationType, + final Object value) { + super(); + this.caseInsensitive =(caseType != null && caseType.equals(SearchOperation.CASE_INSENSITIVE_FLAG)); + this.key = key; + this.operationType = operationType; + this.value = value; + } + + public SearchOperation getOperationType() { + return operationType; + } + + + public String getOperation() { + return operation; + } + + public void setOperation(String operation) { + this.operation = operation; + this.operationType = SearchOperation.getOperation(operation.charAt(0)); + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + public boolean isCaseInsensitive() { + return caseInsensitive; + } + + public void setCaseInsensitive(boolean caseInsensitive) { + this.caseInsensitive = caseInsensitive; + } + + public String getCaseType() { + return caseType; + } + + public void setCaseType(String caseType) { + this.caseType = caseType; + this.caseInsensitive = + (caseType != null && caseType.equals(SearchOperation.CASE_INSENSITIVE_FLAG)); + } + +} diff --git a/solidify-model/src/main/java/ch/unige/solidify/util/SearchOperation.java b/solidify-model/src/main/java/ch/unige/solidify/util/SearchOperation.java new file mode 100644 index 000000000..c275c6434 --- /dev/null +++ b/solidify-model/src/main/java/ch/unige/solidify/util/SearchOperation.java @@ -0,0 +1,32 @@ +package ch.unige.solidify.util; + + +public enum SearchOperation { + EQUALITY, NEGATION, GREATER_THAN, LESS_THAN, CONTAINS, STARTS_WITH, ENDS_WITH; + + public static final String[] OPERATION_SET = {":", "!", ">", "<", "~", "¬", "§"}; + public static final String OR_PREDICATE_FLAG = "OR"; + public static final String CASE_INSENSITIVE_FLAG = "i"; + + + public static SearchOperation getOperation(final char input) { + switch (input) { + case ':': + return EQUALITY; + case '!': + return NEGATION; + case '>': + return GREATER_THAN; + case '<': + return LESS_THAN; + case '~': + return CONTAINS; + case '¬': + return STARTS_WITH; + case '§': + return ENDS_WITH; + default: + return null; + } + } +} diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/config/MockUserJpaConfig.java b/solidify-model/src/test/java/ch/unige/solidify/test/config/MockUserJpaConfig.java new file mode 100644 index 000000000..0388a5613 --- /dev/null +++ b/solidify-model/src/test/java/ch/unige/solidify/test/config/MockUserJpaConfig.java @@ -0,0 +1,71 @@ +package ch.unige.solidify.test.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; +import java.util.Properties; + +@Configuration +@EnableJpaRepositories(basePackages = "ch.unige.solidify.test.specification.dao") +@ComponentScan({ "ch.unige.solidify.test" }) +@PropertySource("persistence-test.properties") +@EnableTransactionManagement +public class MockUserJpaConfig { + @Autowired + private Environment env; + + @Bean + public DataSource dataSource() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName")); + dataSource.setUrl(env.getProperty("jdbc.url")); + dataSource.setUsername(env.getProperty("jdbc.user")); + dataSource.setPassword(env.getProperty("jdbc.pass")); + + return dataSource; + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(dataSource()); + em.setPackagesToScan(new String[]{"ch.unige.solidify.test"}); + em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + em.setJpaProperties(additionalProperties()); + return em; + } + + @Bean + JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + JpaTransactionManager transactionManager = new JpaTransactionManager(); + transactionManager.setEntityManagerFactory(entityManagerFactory); + return transactionManager; + } + + final Properties additionalProperties() { + final Properties hibernateProperties = new Properties(); + + hibernateProperties + .setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); + hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); + hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql")); + hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", + env.getProperty("hibernate.cache.use_second_level_cache")); + hibernateProperties.setProperty("hibernate.cache.use_query_cache", + env.getProperty("hibernate.cache.use_query_cache")); + + return hibernateProperties; + } +} diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/specification/SearchSpecificationTest.java b/solidify-model/src/test/java/ch/unige/solidify/test/specification/SearchSpecificationTest.java new file mode 100644 index 000000000..362b59690 --- /dev/null +++ b/solidify-model/src/test/java/ch/unige/solidify/test/specification/SearchSpecificationTest.java @@ -0,0 +1,100 @@ +package ch.unige.solidify.test.specification; + +import ch.unige.solidify.test.config.MockUserJpaConfig; +import ch.unige.solidify.test.specification.dao.MockUser; +import ch.unige.solidify.test.specification.dao.MockUserRepository; +import ch.unige.solidify.test.specification.dao.MockUserSearchSpecification; +import ch.unige.solidify.util.SearchCriteria; +import ch.unige.solidify.util.SearchOperation; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import javax.annotation.Resource; +import javax.transaction.Transactional; +import java.util.List; + +import static org.hamcrest.collection.IsIn.isIn; +import static org.hamcrest.core.IsNot.not; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration( + classes = {MockUserJpaConfig.class}, + loader = AnnotationConfigContextLoader.class) +@Transactional +public class SearchSpecificationTest { + + @Resource + private MockUserRepository userRepository; + + MockUser user1; + MockUser user2; + MockUser user3; + + @Before + public void setup() { + user1 = new MockUser("john", 22); + user2 = new MockUser("doe", 15); + user3 = new MockUser("jo", 40); + userRepository.save(user1); + userRepository.save(user2); + userRepository.save(user3); + } + + @Test + /** + * Test equals + */ + public void givenNameSearch_ByEqual_thenCorrect() { + SearchCriteria criteria = new SearchCriteria("name", SearchOperation.EQUALITY, "john"); + Specification<MockUser> spec = new MockUserSearchSpecification(criteria); + List<MockUser> results = userRepository.findAll(spec); + Assert.assertThat(user1, isIn(results)); + } + + /** + * Test starts with and not + */ + @Test + public void givenNamesearch_ByStartsWithNot_thenCorrect() { + SearchCriteria criteria1 = new SearchCriteria("name", SearchOperation.STARTS_WITH, "jo"); + SearchCriteria criteria2 = new SearchCriteria("name", SearchOperation.NEGATION, "jo"); + List<MockUser> results = userRepository.findAll( + Specification.where(new MockUserSearchSpecification(criteria1)) + .and(new MockUserSearchSpecification(criteria2))); + Assert.assertThat(user1, isIn(results)); + Assert.assertThat(user3, not(isIn(results))); + } + + /** + * Test contains and case insensentive + */ + @Test + public void givenNamesearch_ByContainsCaseInsensitive_thenCorrect() { + SearchCriteria criteria1 = new SearchCriteria("i", "name", SearchOperation.CONTAINS, "O"); + List<MockUser> results = userRepository.findAll( Specification.where(new MockUserSearchSpecification(criteria1))); + Assert.assertEquals(3,results.size()); + SearchCriteria criteria2 = new SearchCriteria("name", SearchOperation.CONTAINS, "O"); + List<MockUser> results2 = userRepository.findAll( + Specification.where(new MockUserSearchSpecification(criteria2))); + Assert.assertEquals(0,results2.size()); + } + + //Test Greater Than and less Than + @Test + public void ageSearch_ByGreaterLess_thenCorrect() { + SearchCriteria criteria1 = new SearchCriteria("i","age", SearchOperation.LESS_THAN, 30); + SearchCriteria criteria2 = new SearchCriteria("age", SearchOperation.GREATER_THAN, 20); + List<MockUser> results = userRepository.findAll( + Specification.where(new MockUserSearchSpecification(criteria1)) + .and(new MockUserSearchSpecification(criteria2))); + Assert.assertEquals(1,results.size()); + } + + +} diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUser.java b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUser.java new file mode 100644 index 000000000..cdb19dc2a --- /dev/null +++ b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUser.java @@ -0,0 +1,45 @@ +package ch.unige.solidify.test.specification.dao; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class MockUser { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + private String name; + private int age; + + public MockUser(String name, int age) { + this.name = name; + this.age = age; + } + + public MockUser() { + super(); + } + + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } +} diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUserRepository.java b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUserRepository.java new file mode 100644 index 000000000..c92dcf79d --- /dev/null +++ b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUserRepository.java @@ -0,0 +1,7 @@ +package ch.unige.solidify.test.specification.dao; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface MockUserRepository extends JpaRepository<MockUser, Long>,JpaSpecificationExecutor<MockUser> { +} diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUserSearchSpecification.java b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUserSearchSpecification.java new file mode 100644 index 000000000..b57aa6623 --- /dev/null +++ b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUserSearchSpecification.java @@ -0,0 +1,10 @@ +package ch.unige.solidify.test.specification.dao; + +import ch.unige.solidify.specification.SearchSpecification; +import ch.unige.solidify.util.SearchCriteria; + +public class MockUserSearchSpecification extends SearchSpecification<MockUser> { + public MockUserSearchSpecification(SearchCriteria criteria) { + super(criteria); + } +} diff --git a/solidify-model/src/test/resources/persistence-test.properties b/solidify-model/src/test/resources/persistence-test.properties new file mode 100644 index 000000000..3b6b58063 --- /dev/null +++ b/solidify-model/src/test/resources/persistence-test.properties @@ -0,0 +1,9 @@ +jdbc.driverClassName=org.h2.Driver +jdbc.url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1 + +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.show_sql=true +hibernate.hbm2ddl.auto=create + +hibernate.cache.use_second_level_cache=false +hibernate.cache.use_query_cache=false \ No newline at end of file diff --git a/solidify-util/src/main/java/ch/unige/solidify/config/SolidifyProvider.java b/solidify-util/src/main/java/ch/unige/solidify/config/SolidifyProvider.java index a988ef467..e3fdbd3c9 100644 --- a/solidify-util/src/main/java/ch/unige/solidify/config/SolidifyProvider.java +++ b/solidify-util/src/main/java/ch/unige/solidify/config/SolidifyProvider.java @@ -7,6 +7,6 @@ public class SolidifyProvider extends Provider { public SolidifyProvider() { super("SolidifyProvider", 1.0, "Solidify Provider"); - this.put("MessageDigest.CRC32", "ch.unige.solidify.config.CRC"); + this.put("MessageDigest.CRC32", "ch.unige.solidify.util.CRC"); } } -- GitLab From 2cbddb0c2b202fde972b5e52975c646676b87c77 Mon Sep 17 00:00:00 2001 From: wangr <raoli.wang@unige.ch> Date: Wed, 7 Nov 2018 11:17:16 +0100 Subject: [PATCH 06/14] fix: add Sort link only if corresponding meta data exists --- .../controller/SolidifyController.java | 65 ++++++++++++------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/solidify-controller/src/main/java/ch/unige/solidify/controller/SolidifyController.java b/solidify-controller/src/main/java/ch/unige/solidify/controller/SolidifyController.java index 3cb2d377b..22bf4a859 100644 --- a/solidify-controller/src/main/java/ch/unige/solidify/controller/SolidifyController.java +++ b/solidify-controller/src/main/java/ch/unige/solidify/controller/SolidifyController.java @@ -1,10 +1,13 @@ package ch.unige.solidify.controller; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Paths; - +import ch.unige.solidify.SolidifyConstants; +import ch.unige.solidify.exception.SolidifyRuntimeException; +import ch.unige.solidify.rest.ActionName; +import ch.unige.solidify.rest.Collection; +import ch.unige.solidify.rest.ResourceBase; +import ch.unige.solidify.rest.Tool; +import ch.unige.solidify.service.MessageService; +import ch.unige.solidify.util.StringTool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -20,14 +23,10 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.multipart.MultipartFile; -import ch.unige.solidify.SolidifyConstants; -import ch.unige.solidify.exception.SolidifyRuntimeException; -import ch.unige.solidify.rest.ActionName; -import ch.unige.solidify.rest.Collection; -import ch.unige.solidify.rest.ResourceBase; -import ch.unige.solidify.rest.Tool; -import ch.unige.solidify.service.MessageService; -import ch.unige.solidify.util.StringTool; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; public abstract class SolidifyController { private static final Logger log = LoggerFactory.getLogger(SolidifyController.class); @@ -36,7 +35,8 @@ public abstract class SolidifyController { protected MessageService messageService; protected ResponseEntity<InputStreamResource> download(String clazz, String id, InputStream in, - String fileTarget, String fileContentType, long fileSize) { + String fileTarget, String fileContentType, + long fileSize) { try { if (StringTool.isNullOrEmpty(fileTarget) || StringTool.isNullOrEmpty(fileContentType)) { return new ResponseEntity<>(HttpStatus.SERVICE_UNAVAILABLE); @@ -60,10 +60,10 @@ public abstract class SolidifyController { try { if (file.isEmpty()) { throw new SolidifyRuntimeException( - "Failed to save empty file " + file.getOriginalFilename()); + "Failed to save empty file " + file.getOriginalFilename()); } Files.copy(file.getInputStream(), - Paths.get(destinationFolder).resolve(file.getOriginalFilename())); + Paths.get(destinationFolder).resolve(file.getOriginalFilename())); } catch (IOException e) { String message = "Error uploading file " + file; log.error(message, e); @@ -73,22 +73,39 @@ public abstract class SolidifyController { } protected <T> void addSortLinks(ControllerLinkBuilder clb, Collection<T> collection) { - collection.add(Tool.sort(clb.toUriComponentsBuilder(), ActionName.CREATION) - .withRel(ActionName.LASTCREATED)); - collection.add(Tool.sort(clb.toUriComponentsBuilder(), ActionName.UPDATE) - .withRel(ActionName.LASTUPDATE)); + boolean creationExisted = true; + boolean lastUpdateExisted = true; + if (collection != null && collection.get_data() != null) { + if (collection.get_data().iterator().hasNext()) { + T t = collection.get_data().iterator().next(); + if (t instanceof ResourceBase) { + ResourceBase res = (ResourceBase) t; + if (res.getCreation() == null) creationExisted = false; + if (res.getLastUpdate() == null) lastUpdateExisted = false; + } + } + } + if (creationExisted) { + collection.add(Tool.sort(clb.toUriComponentsBuilder(), ActionName.CREATION) + .withRel(ActionName.LASTCREATED)); + } + if (lastUpdateExisted) { + collection.add(Tool.sort(clb.toUriComponentsBuilder(), ActionName.UPDATE) + .withRel(ActionName.LASTUPDATE)); + } } + protected <T> void addPageLinks(ControllerLinkBuilder clb, Collection<T> collection, - Pageable pageable) { + Pageable pageable) { if (collection.get_page().hasPrevious()) { collection.add( - Tool.pageLink(clb.toUriComponentsBuilder(), pageable.previousOrFirst().getPageSize(), - pageable.previousOrFirst().getPageNumber()).withRel(ActionName.PREVIOUS)); + Tool.pageLink(clb.toUriComponentsBuilder(), pageable.previousOrFirst().getPageSize(), + pageable.previousOrFirst().getPageNumber()).withRel(ActionName.PREVIOUS)); } if (collection.get_page().hasNext()) { collection.add(Tool.pageLink(clb.toUriComponentsBuilder(), pageable.next().getPageSize(), - pageable.next().getPageNumber()).withRel(ActionName.NEXT)); + pageable.next().getPageNumber()).withRel(ActionName.NEXT)); } } -- GitLab From 3c0c970cf788ee09c13f81ad4925300f87614d05 Mon Sep 17 00:00:00 2001 From: wangr <raoli.wang@unige.ch> Date: Wed, 7 Nov 2018 11:18:28 +0100 Subject: [PATCH 07/14] feat: implement search specification Controller module --- .../ResourceReadOnlyController.java | 93 +++++++++---- solidify-model/pom.xml | 6 + .../java/ch/unige/solidify/rest/Resource.java | 3 +- .../specification/SearchSpecification.java | 129 +++++++++++------- .../unige/solidify/util/SearchCriteria.java | 24 ++-- .../unige/solidify/util/SearchOperation.java | 2 +- .../unige/solidify/util/StringParserTool.java | 34 +++++ .../SearchSpecificationTest.java | 6 +- .../dao/MockUserSearchSpecification.java | 3 + .../test/util/StringParserToolTest.java | 28 ++++ 10 files changed, 237 insertions(+), 91 deletions(-) create mode 100644 solidify-model/src/main/java/ch/unige/solidify/util/StringParserTool.java create mode 100644 solidify-model/src/test/java/ch/unige/solidify/test/util/StringParserToolTest.java diff --git a/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java b/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java index f6566425e..e05294291 100644 --- a/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java +++ b/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java @@ -1,7 +1,13 @@ package ch.unige.solidify.controller; -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; - +import ch.unige.solidify.rest.ActionName; +import ch.unige.solidify.rest.Collection; +import ch.unige.solidify.rest.Resource; +import ch.unige.solidify.rest.Tool; +import ch.unige.solidify.service.CompositeResourceService; +import ch.unige.solidify.util.SearchCriteria; +import ch.unige.solidify.util.SearchOperation; +import ch.unige.solidify.util.StringParserTool; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -10,37 +16,22 @@ import org.springframework.hateoas.ResourceSupport; import org.springframework.http.HttpEntity; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.*; -import ch.unige.solidify.rest.ActionName; -import ch.unige.solidify.rest.Collection; -import ch.unige.solidify.rest.Resource; -import ch.unige.solidify.rest.Tool; -import ch.unige.solidify.service.CompositeResourceService; +import java.util.List; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; -public abstract class ResourceReadOnlyController<T extends Resource<T>> extends SolidifyController{ +public abstract class ResourceReadOnlyController<T extends Resource<T>> extends SolidifyController { @Autowired protected CompositeResourceService<T> itemService; @GetMapping public HttpEntity<Collection<T>> list(@ModelAttribute T search, Pageable pageable) { - Specification<T> spec = search.getSpecification(); final Page<T> listItem = this.itemService.findAll(spec, pageable); - - for (final T t : listItem) { - addLinks(t); - } - Collection<T> collection = new Collection<>(listItem, pageable); - collection.add(linkTo(this.getClass()).withSelfRel()); - collection.add(Tool.parentLink((linkTo(this.getClass())).toUriComponentsBuilder()) - .withRel(ActionName.MODULE)); - this.addSortLinks(linkTo(this.getClass()), collection); - this.addPageLinks(linkTo(this.getClass()), collection, pageable); - addOthersLinks(collection); + Collection<T> collection = setLinks(listItem, pageable); return new ResponseEntity<>(collection, HttpStatus.OK); } @@ -54,8 +45,62 @@ public abstract class ResourceReadOnlyController<T extends Resource<T>> extends return new ResponseEntity<>(HttpStatus.NOT_FOUND); } + + @PostMapping(value = "/search") + public HttpEntity<Collection<T>> advancedSearch(@RequestBody T search, + @RequestParam(value = "match", required = false) + String matchtype, + Pageable pageable) { + Page<T> listItem = findBySearchCriteria(search, matchtype, search.getSearchCriterias(), pageable); + Collection<T> collection = setLinks(listItem, pageable); + return new ResponseEntity(collection, HttpStatus.OK); + } + + @RequestMapping(method = RequestMethod.GET, value = "/search") + @ResponseBody + public HttpEntity<ch.unige.solidify.rest.Collection<T>> advancedSearch( + @ModelAttribute T resource, + @RequestParam(value = "search") String search, + @RequestParam(value = "match", required = false) String matchtype, + Pageable pageable) { + List<SearchCriteria> criterias = StringParserTool.parseSearchString(search); + Page<T> listItem = findBySearchCriteria(resource, matchtype, criterias, pageable); + Collection<T> collection = setLinks(listItem, pageable); + return new ResponseEntity(collection, HttpStatus.OK); + } + + + protected Page<T> findBySearchCriteria(T resource, String orOpredicateFlag, + List<SearchCriteria> criterias, + Pageable pageable) { + if (criterias == null || criterias.size() == 0) { + return null; + } + Specification<T> spec = resource.getSearchSpecification(criterias.get(0)); + if (spec == null) { + return null; + } + + boolean isOrPredicate = orOpredicateFlag == null ? false : + (orOpredicateFlag.equals(SearchOperation.OR_PREDICATE_FLAG)); + for (int i = 1; i < criterias.size(); i++) { + if (isOrPredicate) { + spec = Specification.where(spec) + .or(resource.getSearchSpecification(criterias.get(i))); + } else { + spec = Specification.where(spec) + .and(resource.getSearchSpecification(criterias.get(i))); + } + } + return this.itemService.findAll(spec, pageable); + + } + protected Collection<T> setLinks(Page<T> listItem, Pageable pageable) { + if (listItem == null || listItem.getSize() == 0) + return null; + for (final T t : listItem) { addLinks(t); } @@ -70,7 +115,6 @@ public abstract class ResourceReadOnlyController<T extends Resource<T>> extends return collection; } - protected void addLinks(T t) { t.removeLinks(); t.addLinks(linkTo(this.getClass()), true, true); @@ -79,5 +123,4 @@ public abstract class ResourceReadOnlyController<T extends Resource<T>> extends @SuppressWarnings("squid:S1172") protected <W extends ResourceSupport> void addOthersLinks(W w) { } - } diff --git a/solidify-model/pom.xml b/solidify-model/pom.xml index 74cfdbf4a..bacec3273 100644 --- a/solidify-model/pom.xml +++ b/solidify-model/pom.xml @@ -85,5 +85,11 @@ <groupId>net.sf.saxon</groupId> <artifactId>Saxon-HE</artifactId> </dependency> + + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>19.0</version> + </dependency> </dependencies> </project> diff --git a/solidify-model/src/main/java/ch/unige/solidify/rest/Resource.java b/solidify-model/src/main/java/ch/unige/solidify/rest/Resource.java index 0885e7e8f..59fe81d62 100644 --- a/solidify-model/src/main/java/ch/unige/solidify/rest/Resource.java +++ b/solidify-model/src/main/java/ch/unige/solidify/rest/Resource.java @@ -122,10 +122,9 @@ public abstract class Resource<T> extends ResourceBase { @Transient - protected List<SearchCriteria> searchCriterias; + public List<SearchCriteria> searchCriterias; @Transient - @JsonIgnore public List<SearchCriteria> getSearchCriterias() { return searchCriterias; } diff --git a/solidify-model/src/main/java/ch/unige/solidify/specification/SearchSpecification.java b/solidify-model/src/main/java/ch/unige/solidify/specification/SearchSpecification.java index 2ab1caebf..0ef3b17d2 100644 --- a/solidify-model/src/main/java/ch/unige/solidify/specification/SearchSpecification.java +++ b/solidify-model/src/main/java/ch/unige/solidify/specification/SearchSpecification.java @@ -1,6 +1,8 @@ package ch.unige.solidify.specification; import ch.unige.solidify.util.SearchCriteria; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.data.jpa.domain.Specification; import javax.persistence.criteria.CriteriaBuilder; @@ -10,6 +12,7 @@ import javax.persistence.criteria.Root; public abstract class SearchSpecification<T> implements Specification<T> { + private static final Logger log = LoggerFactory.getLogger(SearchSpecification.class); private SearchCriteria criteria; @@ -17,88 +20,110 @@ public abstract class SearchSpecification<T> implements Specification<T> { this.criteria = criteria; } - private boolean isStringField(Root<T> root, String fieldName) { - try { - Class[] classlist = root.get(fieldName).type().getJavaType().getClasses(); - for (Class c : classlist) { - if (c == String.class) { - return true; - } - } - } catch (Exception e) { - e.printStackTrace(); - } - - return false; - - } - @Override public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) { switch (criteria.getOperationType()) { case EQUALITY: - if (criteria.isCaseInsensitive() && isStringField(root, criteria.getKey())) { - return builder.equal( - builder.lower(root.get(criteria.getKey())), - criteria.getValue().toString().toLowerCase() - ); + try { + if (criteria.isCaseInsensitive()) { + return builder.equal( + builder.lower(root.get(criteria.getKey())), + criteria.getValue().toString().toLowerCase() + ); + } + } catch (Exception e) { + log.warn("Can not use LOWER operator for CASEINSENSITIVE", e); } return builder.equal( root.get(criteria.getKey()), criteria.getValue() ); case NEGATION: - if (criteria.isCaseInsensitive() && isStringField(root, criteria.getKey())) { - return builder.notEqual( - builder.lower(root.get(criteria.getKey())), - criteria.getValue().toString().toLowerCase() - ); + try { + if (criteria.isCaseInsensitive()) { + return builder.notEqual( + builder.lower(root.get(criteria.getKey())), + criteria.getValue().toString().toLowerCase() + ); + } + } catch (Exception e) { + log.warn("Can not use LOWER operator for CASEINSENSITIVE", e); } - return builder.notEqual(root.get(criteria.getKey()), criteria.getValue()); + return builder.equal( + root.get(criteria.getKey()), criteria.getValue() + ); case GREATER_THAN: - if (criteria.isCaseInsensitive() && isStringField(root, criteria.getKey())) { - return builder.greaterThan( - builder.lower(root.get(criteria.getKey())), - criteria.getValue().toString().toLowerCase() - ); + try { + if (criteria.isCaseInsensitive()) { + return builder.greaterThan( + builder.lower(root.get(criteria.getKey())), + criteria.getValue().toString().toLowerCase() + ); + } + } catch (Exception e) { + log.warn("Can not use LOWER operator for CASEINSENSITIVE", e); } + return builder.greaterThan(root.<String>get( criteria.getKey()), criteria.getValue().toString()); case LESS_THAN: - if (criteria.isCaseInsensitive() && isStringField(root, criteria.getKey())) { - return builder.lessThan( - builder.lower(root.get(criteria.getKey())), - criteria.getValue().toString().toLowerCase() - ); + try { + if (criteria.isCaseInsensitive()) { + return builder.lessThan( + builder.lower(root.get(criteria.getKey())), + criteria.getValue().toString().toLowerCase() + ); + } + } catch (Exception e) { + log.warn("Can not use LOWER operator for CASEINSENSITIVE", e); } + return builder.lessThan(root.get( criteria.getKey()), criteria.getValue().toString()); + case STARTS_WITH: - if (criteria.isCaseInsensitive()) { - return builder.like( - builder.lower(root.get(criteria.getKey())), - criteria.getValue().toString().toLowerCase() + "%" - ); + try { + if (criteria.isCaseInsensitive()) { + return builder.like( + builder.lower(root.get(criteria.getKey())), + criteria.getValue().toString().toLowerCase() + "%" + ); + } + } catch (Exception e) { + log.warn("Can not use LOWER operator for CASEINSENSITIVE", e); } + return builder.like(root.<String>get(criteria.getKey()), criteria.getValue() + "%"); case ENDS_WITH: - if (criteria.isCaseInsensitive()) { - return builder.like( - builder.lower(root.get(criteria.getKey())), - "%" + criteria.getValue().toString().toLowerCase() - ); + try { + if (criteria.isCaseInsensitive()) { + return builder.like( + builder.lower(root.get(criteria.getKey())), + "%" + criteria.getValue().toString().toLowerCase() + ); + } + } catch (Exception e) { + log.warn("Can not use LOWER operator for CASEINSENSITIVE", e); } + + return builder.like(root.<String>get(criteria.getKey()), "%" + criteria.getValue()); case CONTAINS: - if (criteria.isCaseInsensitive()) { - return builder.like( - builder.lower(root.get(criteria.getKey())), - "%" + criteria.getValue().toString().toLowerCase() + "%" - ); + try { + if (criteria.isCaseInsensitive()) { + return builder.like( + builder.lower(root.get(criteria.getKey())), + "%" + criteria.getValue().toString().toLowerCase() + "%" + ); + } + } catch (Exception e) { + log.warn("Can not use LOWER operator for CASEINSENSITIVE", e); } + + return builder.like(root.<String>get( criteria.getKey()), "%" + criteria.getValue() + "%"); default: diff --git a/solidify-model/src/main/java/ch/unige/solidify/util/SearchCriteria.java b/solidify-model/src/main/java/ch/unige/solidify/util/SearchCriteria.java index 6c9b7c57c..47250a899 100644 --- a/solidify-model/src/main/java/ch/unige/solidify/util/SearchCriteria.java +++ b/solidify-model/src/main/java/ch/unige/solidify/util/SearchCriteria.java @@ -5,19 +5,16 @@ import java.io.Serializable; public class SearchCriteria implements Serializable { private String key; - private SearchOperation operationType; private String operation; - private Object value; - - - private boolean caseInsensitive = false; private String caseType; + private boolean caseInsensitive = false; public SearchCriteria() { super(); } + public SearchCriteria(final String key, final String operation, final Object value) { super(); this.operation = operation; @@ -34,7 +31,6 @@ public class SearchCriteria implements Serializable { } - public SearchCriteria(final String caseType, final String key, final String operation, final Object value) { super(); @@ -46,10 +42,12 @@ public class SearchCriteria implements Serializable { this.value = value; } - public SearchCriteria(final String caseType, final String key, final SearchOperation operationType, + public SearchCriteria(final String caseType, final String key, + final SearchOperation operationType, final Object value) { super(); - this.caseInsensitive =(caseType != null && caseType.equals(SearchOperation.CASE_INSENSITIVE_FLAG)); + this.caseInsensitive = + (caseType != null && caseType.equals(SearchOperation.CASE_INSENSITIVE_FLAG)); this.key = key; this.operationType = operationType; this.value = value; @@ -103,4 +101,14 @@ public class SearchCriteria implements Serializable { (caseType != null && caseType.equals(SearchOperation.CASE_INSENSITIVE_FLAG)); } + @Override + public String toString() { + return "SearchCriteria{" + + "key='" + key + '\'' + + ", operationType=" + operationType + + ", operator='" + operation + '\'' + + ", value=" + value + + ", caseInsensitive=" + caseInsensitive + + '}'; + } } diff --git a/solidify-model/src/main/java/ch/unige/solidify/util/SearchOperation.java b/solidify-model/src/main/java/ch/unige/solidify/util/SearchOperation.java index c275c6434..27bc1b1b8 100644 --- a/solidify-model/src/main/java/ch/unige/solidify/util/SearchOperation.java +++ b/solidify-model/src/main/java/ch/unige/solidify/util/SearchOperation.java @@ -5,7 +5,7 @@ public enum SearchOperation { EQUALITY, NEGATION, GREATER_THAN, LESS_THAN, CONTAINS, STARTS_WITH, ENDS_WITH; public static final String[] OPERATION_SET = {":", "!", ">", "<", "~", "¬", "§"}; - public static final String OR_PREDICATE_FLAG = "OR"; + public static final String OR_PREDICATE_FLAG = "any"; public static final String CASE_INSENSITIVE_FLAG = "i"; diff --git a/solidify-model/src/main/java/ch/unige/solidify/util/StringParserTool.java b/solidify-model/src/main/java/ch/unige/solidify/util/StringParserTool.java new file mode 100644 index 000000000..a0935e17d --- /dev/null +++ b/solidify-model/src/main/java/ch/unige/solidify/util/StringParserTool.java @@ -0,0 +1,34 @@ +package ch.unige.solidify.util; + +import com.google.common.base.Joiner; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class StringParserTool { + + + public static List<SearchCriteria> parseSearchString(String search) { + if (search == null || search.trim().isEmpty()) { + return null; + } + String delim = ","; + String regex = "(?<!\\\\)" + Pattern.quote(delim); + String[] searchList = search.split(regex); + List<SearchCriteria> criterias = new ArrayList<SearchCriteria>(); + String operationSetExper = Joiner.on("|").join(SearchOperation.OPERATION_SET); + Pattern pattern = Pattern.compile("([ic]-)??([\\w|_]+?)(" + operationSetExper + ")(.+?)$"); + for (String item : searchList) { + Matcher matcher = pattern.matcher(item); + if (matcher.find()) { + String caseFlag=matcher.group(1)==null? null:matcher.group(1).trim().substring(0,1); + SearchCriteria criteria = + new SearchCriteria(caseFlag, matcher.group(2), matcher.group(3), + matcher.group(4)); + criterias.add(criteria); + } + } + return criterias; + } +} diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/specification/SearchSpecificationTest.java b/solidify-model/src/test/java/ch/unige/solidify/test/specification/SearchSpecificationTest.java index 362b59690..afb81e825 100644 --- a/solidify-model/src/test/java/ch/unige/solidify/test/specification/SearchSpecificationTest.java +++ b/solidify-model/src/test/java/ch/unige/solidify/test/specification/SearchSpecificationTest.java @@ -67,8 +67,8 @@ public class SearchSpecificationTest { List<MockUser> results = userRepository.findAll( Specification.where(new MockUserSearchSpecification(criteria1)) .and(new MockUserSearchSpecification(criteria2))); - Assert.assertThat(user1, isIn(results)); - Assert.assertThat(user3, not(isIn(results))); + Assert.assertEquals("john", user1.getName()); + Assert.assertNotEquals("jo", user1.getName()); } /** @@ -88,7 +88,7 @@ public class SearchSpecificationTest { //Test Greater Than and less Than @Test public void ageSearch_ByGreaterLess_thenCorrect() { - SearchCriteria criteria1 = new SearchCriteria("i","age", SearchOperation.LESS_THAN, 30); + SearchCriteria criteria1 = new SearchCriteria("c","age", SearchOperation.LESS_THAN, 30); SearchCriteria criteria2 = new SearchCriteria("age", SearchOperation.GREATER_THAN, 20); List<MockUser> results = userRepository.findAll( Specification.where(new MockUserSearchSpecification(criteria1)) diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUserSearchSpecification.java b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUserSearchSpecification.java index b57aa6623..5f63d97f4 100644 --- a/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUserSearchSpecification.java +++ b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUserSearchSpecification.java @@ -3,8 +3,11 @@ package ch.unige.solidify.test.specification.dao; import ch.unige.solidify.specification.SearchSpecification; import ch.unige.solidify.util.SearchCriteria; +import javax.persistence.criteria.Root; + public class MockUserSearchSpecification extends SearchSpecification<MockUser> { public MockUserSearchSpecification(SearchCriteria criteria) { super(criteria); } + } diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/util/StringParserToolTest.java b/solidify-model/src/test/java/ch/unige/solidify/test/util/StringParserToolTest.java new file mode 100644 index 000000000..095d505b4 --- /dev/null +++ b/solidify-model/src/test/java/ch/unige/solidify/test/util/StringParserToolTest.java @@ -0,0 +1,28 @@ +package ch.unige.solidify.test.util; + +import ch.unige.solidify.util.SearchCriteria; +import ch.unige.solidify.util.SearchOperation; +import ch.unige.solidify.util.StringParserTool; +import com.google.common.base.Joiner; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class StringParserToolTest { + + + @Test + public void testString(){ + String search="lastName9:<w!.*\\\\,ang%,i-my_test_9§this@gmail.com,i-age:"; + List<SearchCriteria> criterias=StringParserTool.parseSearchString( search); + Assert.assertEquals(2,criterias.size()); + for (SearchCriteria item : criterias) { + System.out.println(item); + } + } + +} + -- GitLab From 54e6c9515b1d615d95098c0ce7b7a6fb5da6a6fc Mon Sep 17 00:00:00 2001 From: wangr <raoli.wang@unige.ch> Date: Fri, 9 Nov 2018 17:07:21 +0100 Subject: [PATCH 08/14] feat: add unit test in Controller module --- solidify-controller/pom.xml | 13 +++ .../ResourceReadOnlyController.java | 2 +- .../ResourceReadOnlyControllerTest.java | 97 +++++++++++++++++++ .../ApplicationLinkedResourceChecker.java | 18 ++++ .../controller/business/MockUserService.java | 10 ++ .../test/controller/config/JpaConfig.java | 72 ++++++++++++++ .../controller/config/MockApplication.java | 17 +++- .../controller/config/WebSecurityConfig.java | 13 +++ .../test/controller/dao/MockUser.java | 88 +++++++++++++++++ .../controller/dao/MockUserRepository.java | 8 ++ .../dao/MockUserSearchSpecification.java | 11 +++ .../controller/dao/MockUserSpecification.java | 31 ++++++ .../service/ApplicationRestClientService.java | 8 ++ .../service/rest/MockUserResourceService.java | 18 ++++ .../web/ResourceReadOnlyControllerImp.java | 12 +++ .../test/specification/ResourceTest.java | 62 ++++++++++++ .../test/specification/dao/EntityMock.java | 56 +++++++++++ .../dao/EntityMockRepository.java | 7 ++ .../dao/EntityMockSpecification.java | 28 ++++++ .../java/ch/unige/solidify/TestTools.java | 22 +++++ 20 files changed, 591 insertions(+), 2 deletions(-) create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/ResourceReadOnlyControllerTest.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/business/ApplicationLinkedResourceChecker.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/business/MockUserService.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/JpaConfig.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/WebSecurityConfig.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUser.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUserRepository.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUserSearchSpecification.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUserSpecification.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/service/ApplicationRestClientService.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/service/rest/MockUserResourceService.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/web/ResourceReadOnlyControllerImp.java create mode 100644 solidify-model/src/test/java/ch/unige/solidify/test/specification/ResourceTest.java create mode 100644 solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/EntityMock.java create mode 100644 solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/EntityMockRepository.java create mode 100644 solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/EntityMockSpecification.java create mode 100644 solidify-util/src/main/java/ch/unige/solidify/TestTools.java diff --git a/solidify-controller/pom.xml b/solidify-controller/pom.xml index 12ee85761..36d8534fa 100644 --- a/solidify-controller/pom.xml +++ b/solidify-controller/pom.xml @@ -102,6 +102,19 @@ <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> </dependency> + <!--dependence for test--> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <version>1.4.197</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-entitymanager</artifactId> + <version>5.3.7.Final</version> + <scope>test</scope> + </dependency> </dependencies> </project> diff --git a/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java b/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java index e05294291..89d14a792 100644 --- a/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java +++ b/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java @@ -49,7 +49,7 @@ public abstract class ResourceReadOnlyController<T extends Resource<T>> extends @PostMapping(value = "/search") public HttpEntity<Collection<T>> advancedSearch(@RequestBody T search, @RequestParam(value = "match", required = false) - String matchtype, + String matchtype, Pageable pageable) { Page<T> listItem = findBySearchCriteria(search, matchtype, search.getSearchCriterias(), pageable); Collection<T> collection = setLinks(listItem, pageable); diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/ResourceReadOnlyControllerTest.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/ResourceReadOnlyControllerTest.java new file mode 100644 index 000000000..2e15b4c7f --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/ResourceReadOnlyControllerTest.java @@ -0,0 +1,97 @@ +package ch.unige.solidify.test.controller; + +import ch.unige.solidify.TestTools; +import ch.unige.solidify.test.controller.config.MockApplication; +import ch.unige.solidify.test.controller.dao.MockUser; +import ch.unige.solidify.test.controller.dao.MockUserRepository; +import ch.unige.solidify.util.SearchCriteria; +import ch.unige.solidify.util.SearchOperation; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@RunWith(SpringRunner.class) +@AutoConfigureMockMvc +@SpringBootTest(classes = MockApplication.class) +@WithMockUser(username = "TEST", password = "TEST") +public class ResourceReadOnlyControllerTest { + + @Autowired + private MockMvc mockMvc; + @Autowired + MockUserRepository userRepository; + + MockUser user1; + MockUser user2; + MockUser user3; + + @Before + public void setup() { + user1 = new MockUser("1", "john", 22); + user2 = new MockUser("2", "doe", 15); + user3 = new MockUser("3", "jo", 40); + userRepository.save(user1); + userRepository.save(user2); + userRepository.save(user3); + } + + + @Test + public void testGetRequest() throws Exception { + mockMvc.perform(get("/test?size=2&sort=age")) + .andExpect(status().isOk()) + .andExpect(content().contentType("application/hal+json;charset=UTF-8")) + .andExpect(jsonPath("_data[0].name", is("doe"))); + } + + @Test + public void testGetWithId() throws Exception { + mockMvc.perform(get("/test/3")) + .andExpect(status().isOk()) + .andExpect(content().contentType("application/hal+json;charset=UTF-8")) + .andExpect(jsonPath("name", is("jo"))); + } + + + @Test + public void testGetSearch() throws Exception { + mockMvc.perform(get("/test/search?search=i-name~o,age:15&match=all")) + .andExpect(status().isOk()) + .andExpect(content().contentType("application/hal+json;charset=UTF-8")) + //.andDo(print()); + .andExpect(jsonPath("_data[0].name", is("doe"))); + } + + @Test + public void testPostSearch() throws Exception { + MockUser user = new MockUser(); + List<SearchCriteria> allCriterias = new ArrayList(); + user.setSearchCriterias(allCriterias); + SearchCriteria criteria = new SearchCriteria("i", "name", SearchOperation.CONTAINS, "o"); + allCriterias.add(criteria); + criteria = new SearchCriteria("c", "age", SearchOperation.LESS_THAN, "18"); + allCriterias.add(criteria); + + mockMvc.perform(post("/test/search?match=all") + .contentType(TestTools.APPLICATION_JSON_UTF8) + .content(TestTools.convertObjectToJsonBytes(user))) + .andExpect(status().isOk()) + .andExpect(content().contentType("application/hal+json;charset=UTF-8")) + //.andDo(print()); + .andExpect(jsonPath("_data[0].name", is("doe"))); + } +} diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/business/ApplicationLinkedResourceChecker.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/business/ApplicationLinkedResourceChecker.java new file mode 100644 index 000000000..8591b8e12 --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/business/ApplicationLinkedResourceChecker.java @@ -0,0 +1,18 @@ +package ch.unige.solidify.test.controller.business; + +import ch.unige.solidify.rest.Resource; +import ch.unige.solidify.service.CompositeResourceService; +import ch.unige.solidify.service.LinkedResourceChecker; +import org.springframework.stereotype.Service; +import org.springframework.validation.BindingResult; + +@Service +public class ApplicationLinkedResourceChecker<T extends Resource<T>> + implements LinkedResourceChecker<T> { + + @Override + public void validateLinkedResources(CompositeResourceService<T> service, T item, + BindingResult errors) { + service.validateLinkedResources(item, errors); + } +} diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/business/MockUserService.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/business/MockUserService.java new file mode 100644 index 000000000..c1b47cc45 --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/business/MockUserService.java @@ -0,0 +1,10 @@ +package ch.unige.solidify.test.controller.business; + +import ch.unige.solidify.service.CompositeResourceService; +import ch.unige.solidify.test.controller.dao.MockUser; +import org.springframework.stereotype.Service; + +@Service +public class MockUserService extends CompositeResourceService<MockUser> { +} + diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/JpaConfig.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/JpaConfig.java new file mode 100644 index 000000000..61748ce90 --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/JpaConfig.java @@ -0,0 +1,72 @@ +package ch.unige.solidify.test.controller.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.test.context.TestPropertySource; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; +import java.util.Properties; + +@Configuration +@ComponentScan(basePackages = {"ch.unige.solidify.test.controller.**", "ch.unige.solidify"}) +@EnableJpaRepositories(basePackages = {"ch.unige.solidify.test.controller.dao"}) +@EnableTransactionManagement +public class JpaConfig { + @Autowired + private Environment env; + + + @Bean + public DataSource dataSource() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName("org.h2.Driver"); + dataSource.setUrl("jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1"); + return dataSource; + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(dataSource()); + em.setPackagesToScan(new String[]{"ch.unige.solidify.test"}); + em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + em.setJpaProperties(additionalProperties()); + return em; + } + + @Bean + JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + JpaTransactionManager transactionManager = new JpaTransactionManager(); + transactionManager.setEntityManagerFactory(entityManagerFactory); + return transactionManager; + } + + + final Properties additionalProperties() { + final Properties hibernateProperties = new Properties(); + + hibernateProperties + .setProperty("hibernate.hbm2ddl.auto", "create"); + hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); + hibernateProperties.setProperty("hibernate.show_sql", "true"); + hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", + "false"); + hibernateProperties.setProperty("hibernate.cache.use_query_cache", + "false"); + + return hibernateProperties; + } + +} diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java index d42cd94ba..9b1b47440 100644 --- a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java @@ -1,21 +1,35 @@ package ch.unige.solidify.test.controller.config; import ch.unige.solidify.service.GitInfoService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; +import java.util.Properties; @SpringBootApplication @EnableWebSecurity @EnableGlobalMethodSecurity(jsr250Enabled = true, proxyTargetClass = true, prePostEnabled = true) -@ComponentScan(basePackages = {"ch.unige.solidify.test.controller.config", "ch.unige.solidify"}) public class MockApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { @@ -26,4 +40,5 @@ public class MockApplication extends SpringBootServletInitializer { SpringApplication.run(MockApplication.class, args); } + } diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/WebSecurityConfig.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/WebSecurityConfig.java new file mode 100644 index 000000000..9eda7194a --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/WebSecurityConfig.java @@ -0,0 +1,13 @@ +package ch.unige.solidify.test.controller.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf().disable(); + } +} diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUser.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUser.java new file mode 100644 index 000000000..86cc5def3 --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUser.java @@ -0,0 +1,88 @@ +package ch.unige.solidify.test.controller.dao; + +import ch.unige.solidify.model.ChangeInfo; +import ch.unige.solidify.rest.ResourceDiverse; +import ch.unige.solidify.util.SearchCriteria; +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.springframework.data.jpa.domain.Specification; + +import javax.persistence.*; + +@Entity +@Table(name = "MockUser") +public class MockUser extends ResourceDiverse<MockUser> { + + @Id + @Column(name = "id") + private String entityId; + private String name; + private int age; + + public MockUser() { + super(); + } + public MockUser(String id,String name, int age) { + super(); + this.entityId=id; + this.name = name; + this.age = age; + } + + + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + public void setEntityId(String entityId) { + this.entityId = entityId; + } + + @Override + public String getEntityId() { + return this.entityId; + } + + @Override + public ChangeInfo getCreation() { + return null; + } + + @Override + public ChangeInfo getLastUpdate() { + return null; + } + + @Override + public Specification<MockUser> getSpecification() { + return new MockUserSpecification(this); + } + + @Override + @java.beans.Transient + @JsonIgnore + public Specification<MockUser> getSearchSpecification(SearchCriteria criteria) { + return new MockUserSearchSpecification(criteria); + } + @Override + public void init() { + + } + + @Override + public String managedBy() { + return null; + } +} diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUserRepository.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUserRepository.java new file mode 100644 index 000000000..a9c6dd46d --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUserRepository.java @@ -0,0 +1,8 @@ +package ch.unige.solidify.test.controller.dao; + +import ch.unige.solidify.repository.SolidifyRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface MockUserRepository extends SolidifyRepository<MockUser> { +} diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUserSearchSpecification.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUserSearchSpecification.java new file mode 100644 index 000000000..f19cdc418 --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUserSearchSpecification.java @@ -0,0 +1,11 @@ +package ch.unige.solidify.test.controller.dao; + +import ch.unige.solidify.specification.SearchSpecification; +import ch.unige.solidify.util.SearchCriteria; + +public class MockUserSearchSpecification extends SearchSpecification<MockUser> { + public MockUserSearchSpecification(SearchCriteria criteria) { + super(criteria); + } + +} diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUserSpecification.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUserSpecification.java new file mode 100644 index 000000000..d62bbd33a --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/dao/MockUserSpecification.java @@ -0,0 +1,31 @@ +package ch.unige.solidify.test.controller.dao; + +import org.springframework.data.jpa.domain.Specification; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; +import java.util.List; + +public class MockUserSpecification implements Specification<MockUser> + +{ + private final MockUser criteria; + + public MockUserSpecification(MockUser criteria) { + this.criteria = criteria; + } + + @Override + public Predicate toPredicate(Root<MockUser> root, CriteriaQuery<?> criteriaQuery, + CriteriaBuilder criteriaBuilder) { + List<Predicate> listPredicate = new ArrayList<>(); + if (criteria.getName() != null) { + listPredicate.add(criteriaBuilder + .like(root.get("name"), "%" + criteria.getName() + "%")); + } + return criteriaBuilder.and(listPredicate.toArray(new Predicate[listPredicate.size()])); + } +} diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/service/ApplicationRestClientService.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/service/ApplicationRestClientService.java new file mode 100644 index 000000000..f68e3942d --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/service/ApplicationRestClientService.java @@ -0,0 +1,8 @@ +package ch.unige.solidify.test.controller.service; + +import ch.unige.solidify.service.SolidifyRestClientService; +import org.springframework.stereotype.Service; + +@Service +public class ApplicationRestClientService extends SolidifyRestClientService { +} diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/service/rest/MockUserResourceService.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/service/rest/MockUserResourceService.java new file mode 100644 index 000000000..442737e45 --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/service/rest/MockUserResourceService.java @@ -0,0 +1,18 @@ +package ch.unige.solidify.test.controller.service.rest; + +import ch.unige.solidify.service.ResourceService; +import ch.unige.solidify.test.controller.dao.MockUser; +import org.springframework.stereotype.Service; + +@Service +public class MockUserResourceService extends ResourceService<MockUser> { + @Override + protected Class<MockUser> getResourceClass() { + return MockUser.class; + } + + @Override + protected String getResourceUrl() { + return "/"; + } +} diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/web/ResourceReadOnlyControllerImp.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/web/ResourceReadOnlyControllerImp.java new file mode 100644 index 000000000..db5d4f219 --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/web/ResourceReadOnlyControllerImp.java @@ -0,0 +1,12 @@ +package ch.unige.solidify.test.controller.web; + + +import ch.unige.solidify.controller.ResourceReadOnlyController; +import ch.unige.solidify.test.controller.dao.MockUser; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/test") +public class ResourceReadOnlyControllerImp extends ResourceReadOnlyController<MockUser> { +} diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/specification/ResourceTest.java b/solidify-model/src/test/java/ch/unige/solidify/test/specification/ResourceTest.java new file mode 100644 index 000000000..f787d4230 --- /dev/null +++ b/solidify-model/src/test/java/ch/unige/solidify/test/specification/ResourceTest.java @@ -0,0 +1,62 @@ +package ch.unige.solidify.test.specification; + +import ch.unige.solidify.test.config.MockUserJpaConfig; +import ch.unige.solidify.test.specification.dao.EntityMock; +import ch.unige.solidify.test.specification.dao.EntityMockRepository; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import javax.annotation.Resource; +import javax.transaction.Transactional; +import java.time.OffsetDateTime; +import java.util.Optional; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration( + classes = {MockUserJpaConfig.class}, + loader = AnnotationConfigContextLoader.class) +@Transactional +public class ResourceTest { + @Resource + private EntityMockRepository entityMockRepository; + + @Before + public void setup() { + EntityMock entity = new EntityMock(); + entity.setEntityId("1"); + entity.setLabel("first"); + entity.setCreatedBy("unitTEST"); + entityMockRepository.save(entity); + } + + @Test + public void Test_insert_metaData() { + + Optional<EntityMock> entity2 = entityMockRepository.findById("1"); + Assert.assertNotNull(entity2.get().getCreationTime()); + Assert.assertNotNull(entity2.get().getUpdateTime()); + } + + @Test + public void Test_update() { + Optional<EntityMock> entity = entityMockRepository.findById("1"); + EntityMock entityMock = entity.get(); + OffsetDateTime oldUpdateAt = entityMock.getUpdateTime(); + entityMock.setLabel("changed"); + entityMockRepository.save(entityMock); + Optional<EntityMock> entity2 = entityMockRepository.findById("1"); + Assert.assertEquals("changed", entity2.get().getLabel()); + } + + @Test + public void Test_delete() { + entityMockRepository.deleteById("1"); + Optional<EntityMock> entity2 = entityMockRepository.findById("1"); + Assert.assertFalse(entity2.isPresent()); + } +} diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/EntityMock.java b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/EntityMock.java new file mode 100644 index 000000000..a4f5fcf70 --- /dev/null +++ b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/EntityMock.java @@ -0,0 +1,56 @@ +package ch.unige.solidify.test.specification.dao; + +import ch.unige.solidify.rest.ResourceLegacy; +import org.springframework.data.jpa.domain.Specification; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "EntityMock") +public class EntityMock extends ResourceLegacy<EntityMock> { + + + @Id + @Column(name = "id") + private String entityId; + + + private String label; + + + @Override + public String getEntityId() { + return this.entityId; + } + + + public void setEntityId(String entityId) { + this.entityId = entityId; + } + + @Override + public Specification<EntityMock> getSpecification() { + return null; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + @Override + public void init() { + + } + + @Override + public String managedBy() { + return null; + } +} diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/EntityMockRepository.java b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/EntityMockRepository.java new file mode 100644 index 000000000..e8f162ed5 --- /dev/null +++ b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/EntityMockRepository.java @@ -0,0 +1,7 @@ +package ch.unige.solidify.test.specification.dao; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface EntityMockRepository extends JpaRepository<EntityMock, String>, JpaSpecificationExecutor<EntityMock> { +} diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/EntityMockSpecification.java b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/EntityMockSpecification.java new file mode 100644 index 000000000..7b1b82164 --- /dev/null +++ b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/EntityMockSpecification.java @@ -0,0 +1,28 @@ +package ch.unige.solidify.test.specification.dao; + +import org.springframework.data.jpa.domain.Specification; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; +import java.util.List; + +public class EntityMockSpecification implements Specification<EntityMock> { + private final EntityMock criteria; + + public EntityMockSpecification(EntityMock criteria) { + this.criteria = criteria; + } + + @Override + public Predicate toPredicate(Root<EntityMock> root, CriteriaQuery<?> criteriaQuery, + CriteriaBuilder criteriaBuilder) { + List<Predicate> listPredicate = new ArrayList<>(); + if (criteria.getLabel() != null) { + listPredicate.add(criteriaBuilder + .like(root.get("label"), "%" + criteria.getLabel() + "%")); + } + return criteriaBuilder.and(listPredicate.toArray(new Predicate[listPredicate.size()])); } +} diff --git a/solidify-util/src/main/java/ch/unige/solidify/TestTools.java b/solidify-util/src/main/java/ch/unige/solidify/TestTools.java new file mode 100644 index 000000000..9b9f7ae48 --- /dev/null +++ b/solidify-util/src/main/java/ch/unige/solidify/TestTools.java @@ -0,0 +1,22 @@ +package ch.unige.solidify; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.http.MediaType; + +import java.io.IOException; +import java.nio.charset.Charset; + +public class TestTools { + + public static MediaType + APPLICATION_JSON_UTF8 = + new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), + Charset.forName("utf8")); + + public static byte[] convertObjectToJsonBytes(Object object) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.writeValueAsBytes(object); + } +} \ No newline at end of file -- GitLab From 04e033b64c987d52fe961d4fe1192f8cdd8b7fa6 Mon Sep 17 00:00:00 2001 From: Dardan Salihi <dardan.salihi@unige.ch> Date: Mon, 12 Nov 2018 15:26:44 +0100 Subject: [PATCH 09/14] feat: separate setlinks method in two to differenciatate between link setter for resource and creation of a collection with links --- .../ResourceReadOnlyController.java | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java b/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java index 89d14a792..1b3f40d99 100644 --- a/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java +++ b/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java @@ -31,7 +31,8 @@ public abstract class ResourceReadOnlyController<T extends Resource<T>> extends public HttpEntity<Collection<T>> list(@ModelAttribute T search, Pageable pageable) { Specification<T> spec = search.getSpecification(); final Page<T> listItem = this.itemService.findAll(spec, pageable); - Collection<T> collection = setLinks(listItem, pageable); + setRessourceLinks(listItem); + Collection<T> collection = setCollectionLinks(listItem, pageable); return new ResponseEntity<>(collection, HttpStatus.OK); } @@ -49,10 +50,12 @@ public abstract class ResourceReadOnlyController<T extends Resource<T>> extends @PostMapping(value = "/search") public HttpEntity<Collection<T>> advancedSearch(@RequestBody T search, @RequestParam(value = "match", required = false) - String matchtype, + String matchtype, Pageable pageable) { - Page<T> listItem = findBySearchCriteria(search, matchtype, search.getSearchCriterias(), pageable); - Collection<T> collection = setLinks(listItem, pageable); + Page<T> listItem = + findBySearchCriteria(search, matchtype, search.getSearchCriterias(), pageable); + setRessourceLinks(listItem); + Collection<T> collection = setCollectionLinks(listItem, pageable); return new ResponseEntity(collection, HttpStatus.OK); } @@ -65,14 +68,15 @@ public abstract class ResourceReadOnlyController<T extends Resource<T>> extends Pageable pageable) { List<SearchCriteria> criterias = StringParserTool.parseSearchString(search); Page<T> listItem = findBySearchCriteria(resource, matchtype, criterias, pageable); - Collection<T> collection = setLinks(listItem, pageable); + setRessourceLinks(listItem); + Collection<T> collection = setCollectionLinks(listItem, pageable); return new ResponseEntity(collection, HttpStatus.OK); } protected Page<T> findBySearchCriteria(T resource, String orOpredicateFlag, - List<SearchCriteria> criterias, - Pageable pageable) { + List<SearchCriteria> criterias, + Pageable pageable) { if (criterias == null || criterias.size() == 0) { return null; } @@ -96,14 +100,18 @@ public abstract class ResourceReadOnlyController<T extends Resource<T>> extends } - protected Collection<T> setLinks(Page<T> listItem, Pageable pageable) { + protected void setRessourceLinks(Page<T> listItem) { if (listItem == null || listItem.getSize() == 0) - return null; + return; for (final T t : listItem) { addLinks(t); } + } + + protected Collection<T> setCollectionLinks(Page<T> listItem, Pageable pageable) { + Collection<T> collection = new Collection<>(listItem, pageable); collection.add(linkTo(this.getClass()).withSelfRel()); collection.add(Tool.parentLink((linkTo(this.getClass())).toUriComponentsBuilder()) -- GitLab From 2e2d2b0217eb21a3e2929c2c3d37c455664eaabd Mon Sep 17 00:00:00 2001 From: wangr <raoli.wang@unige.ch> Date: Mon, 12 Nov 2018 17:01:29 +0100 Subject: [PATCH 10/14] fix: code refactoring for unit tests in Controller module --- .../solidify/controller/UserController.java | 2 +- .../UserShibboletControllerTest.java | 2 +- .../test/controller/config/JpaConfig.java | 72 ------------------- .../controller/config/MockApplication.java | 21 ++---- .../src/test/resources/application.yaml | 13 +++- 5 files changed, 20 insertions(+), 90 deletions(-) delete mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/JpaConfig.java diff --git a/solidify-controller/src/main/java/ch/unige/solidify/controller/UserController.java b/solidify-controller/src/main/java/ch/unige/solidify/controller/UserController.java index dd586ed51..1f841e733 100644 --- a/solidify-controller/src/main/java/ch/unige/solidify/controller/UserController.java +++ b/solidify-controller/src/main/java/ch/unige/solidify/controller/UserController.java @@ -16,7 +16,7 @@ public class UserController { * * @return User's sso data */ - @RequestMapping("/api/user") + @RequestMapping("/user") public User user() { return userService.current(); } diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java index d4d3156c5..6df6bd750 100644 --- a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java @@ -44,7 +44,7 @@ public class UserShibboletControllerTest { uTest.setUniqueid(HEADER_UNIQUEID_VALUE); // User uTest = User.create(u); when(userService.current()).thenReturn(uTest); - mockMvc.perform(get("/api/user")) + mockMvc.perform(get("/user")) .andExpect(status().isOk()) .andExpect(content().contentType("application/json;charset=UTF-8")) .andExpect(jsonPath("$.email", is("test@unige.ch"))) diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/JpaConfig.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/JpaConfig.java deleted file mode 100644 index 61748ce90..000000000 --- a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/JpaConfig.java +++ /dev/null @@ -1,72 +0,0 @@ -package ch.unige.solidify.test.controller.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.PropertySource; -import org.springframework.core.env.Environment; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.jdbc.datasource.DriverManagerDataSource; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; -import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.test.context.TestPropertySource; -import org.springframework.transaction.annotation.EnableTransactionManagement; - -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; -import java.util.Properties; - -@Configuration -@ComponentScan(basePackages = {"ch.unige.solidify.test.controller.**", "ch.unige.solidify"}) -@EnableJpaRepositories(basePackages = {"ch.unige.solidify.test.controller.dao"}) -@EnableTransactionManagement -public class JpaConfig { - @Autowired - private Environment env; - - - @Bean - public DataSource dataSource() { - DriverManagerDataSource dataSource = new DriverManagerDataSource(); - dataSource.setDriverClassName("org.h2.Driver"); - dataSource.setUrl("jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1"); - return dataSource; - } - - @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); - em.setDataSource(dataSource()); - em.setPackagesToScan(new String[]{"ch.unige.solidify.test"}); - em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); - em.setJpaProperties(additionalProperties()); - return em; - } - - @Bean - JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { - JpaTransactionManager transactionManager = new JpaTransactionManager(); - transactionManager.setEntityManagerFactory(entityManagerFactory); - return transactionManager; - } - - - final Properties additionalProperties() { - final Properties hibernateProperties = new Properties(); - - hibernateProperties - .setProperty("hibernate.hbm2ddl.auto", "create"); - hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); - hibernateProperties.setProperty("hibernate.show_sql", "true"); - hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", - "false"); - hibernateProperties.setProperty("hibernate.cache.use_query_cache", - "false"); - - return hibernateProperties; - } - -} diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java index 9b1b47440..02b1b2338 100644 --- a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/config/MockApplication.java @@ -1,35 +1,26 @@ package ch.unige.solidify.test.controller.config; -import ch.unige.solidify.service.GitInfoService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.FilterType; -import org.springframework.context.annotation.PropertySource; -import org.springframework.core.env.Environment; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.jdbc.datasource.DriverManagerDataSource; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; -import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.transaction.annotation.EnableTransactionManagement; -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; -import java.util.Properties; - @SpringBootApplication @EnableWebSecurity @EnableGlobalMethodSecurity(jsr250Enabled = true, proxyTargetClass = true, prePostEnabled = true) +@EntityScan(basePackages = "ch.unige.solidify.test.controller.dao") +@ComponentScan( + basePackages = {"ch.unige.solidify.test.controller.dao", "ch.unige.solidify.test.controller", + "ch.unige.solidify"}) +@EnableJpaRepositories(basePackages = {"ch.unige.solidify.test.controller.dao"}) public class MockApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { diff --git a/solidify-controller/src/test/resources/application.yaml b/solidify-controller/src/test/resources/application.yaml index e31e505cf..d622b43e5 100644 --- a/solidify-controller/src/test/resources/application.yaml +++ b/solidify-controller/src/test/resources/application.yaml @@ -1,3 +1,14 @@ spring: application: - name: test \ No newline at end of file + name: test + datasource: + url: jdbc:h2:~/testdb;DB_CLOSE_DELAY=-1 + username: sa + password: + driver-class-name: org.h2.Driver + h2: + console: + enabled: true + jpa: + database-platform: org.hibernate.dialect.H2Dialect + show-sql: true \ No newline at end of file -- GitLab From ea28f1ab7fcb6ecc8ec0e36d2c6ee68d7f0b4980 Mon Sep 17 00:00:00 2001 From: wangr <raoli.wang@unige.ch> Date: Mon, 12 Nov 2018 17:01:50 +0100 Subject: [PATCH 11/14] fix: code refactoring for unit tests in Model module --- .../specification/SearchSpecification.java | 2 +- .../test/config/MockUserJpaConfig.java | 104 ++++++++---------- .../test/specification/ResourceTest.java | 13 +-- .../SearchSpecificationTest.java | 17 +-- .../test/specification/dao/MockUser.java | 9 ++ .../src/test/resources/application.yaml | 14 +++ .../resources/persistence-test.properties | 9 -- 7 files changed, 84 insertions(+), 84 deletions(-) create mode 100644 solidify-model/src/test/resources/application.yaml delete mode 100644 solidify-model/src/test/resources/persistence-test.properties diff --git a/solidify-model/src/main/java/ch/unige/solidify/specification/SearchSpecification.java b/solidify-model/src/main/java/ch/unige/solidify/specification/SearchSpecification.java index 0ef3b17d2..0583e2df0 100644 --- a/solidify-model/src/main/java/ch/unige/solidify/specification/SearchSpecification.java +++ b/solidify-model/src/main/java/ch/unige/solidify/specification/SearchSpecification.java @@ -50,7 +50,7 @@ public abstract class SearchSpecification<T> implements Specification<T> { } catch (Exception e) { log.warn("Can not use LOWER operator for CASEINSENSITIVE", e); } - return builder.equal( + return builder.notEqual( root.get(criteria.getKey()), criteria.getValue() ); diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/config/MockUserJpaConfig.java b/solidify-model/src/test/java/ch/unige/solidify/test/config/MockUserJpaConfig.java index 0388a5613..1725d4cf2 100644 --- a/solidify-model/src/test/java/ch/unige/solidify/test/config/MockUserJpaConfig.java +++ b/solidify-model/src/test/java/ch/unige/solidify/test/config/MockUserJpaConfig.java @@ -1,71 +1,57 @@ + + package ch.unige.solidify.test.config; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; +import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.PropertySource; -import org.springframework.core.env.Environment; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.jdbc.datasource.DriverManagerDataSource; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; -import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.transaction.annotation.EnableTransactionManagement; -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; -import java.util.Properties; - @Configuration @EnableJpaRepositories(basePackages = "ch.unige.solidify.test.specification.dao") -@ComponentScan({ "ch.unige.solidify.test" }) -@PropertySource("persistence-test.properties") +@ComponentScan({"ch.unige.solidify.test"}) +@EntityScan(basePackages = "ch.unige.solidify.test") +//@PropertySource("persistence-test.properties") @EnableTransactionManagement public class MockUserJpaConfig { - @Autowired - private Environment env; - - @Bean - public DataSource dataSource() { - DriverManagerDataSource dataSource = new DriverManagerDataSource(); - dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName")); - dataSource.setUrl(env.getProperty("jdbc.url")); - dataSource.setUsername(env.getProperty("jdbc.user")); - dataSource.setPassword(env.getProperty("jdbc.pass")); - - return dataSource; - } - - @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); - em.setDataSource(dataSource()); - em.setPackagesToScan(new String[]{"ch.unige.solidify.test"}); - em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); - em.setJpaProperties(additionalProperties()); - return em; - } - - @Bean - JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { - JpaTransactionManager transactionManager = new JpaTransactionManager(); - transactionManager.setEntityManagerFactory(entityManagerFactory); - return transactionManager; - } - - final Properties additionalProperties() { - final Properties hibernateProperties = new Properties(); - - hibernateProperties - .setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); - hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); - hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql")); - hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", - env.getProperty("hibernate.cache.use_second_level_cache")); - hibernateProperties.setProperty("hibernate.cache.use_query_cache", - env.getProperty("hibernate.cache.use_query_cache")); - - return hibernateProperties; - } + /** @Autowired private Environment env; + + @Bean public DataSource dataSource() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName")); + dataSource.setUrl(env.getProperty("jdbc.url")); + dataSource.setUsername(env.getProperty("jdbc.user")); + dataSource.setPassword(env.getProperty("jdbc.pass")); + return dataSource; + } + + @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(dataSource()); + em.setPackagesToScan(new String[]{"ch.unige.solidify.test"}); + em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + em.setJpaProperties(additionalProperties()); + return em; + } + + @Bean JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + JpaTransactionManager transactionManager = new JpaTransactionManager(); + transactionManager.setEntityManagerFactory(entityManagerFactory); + return transactionManager; + } + + final Properties additionalProperties() { + final Properties hibernateProperties = new Properties(); + hibernateProperties + .setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); + hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); + hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql")); + hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", + env.getProperty("hibernate.cache.use_second_level_cache")); + hibernateProperties.setProperty("hibernate.cache.use_query_cache", + env.getProperty("hibernate.cache.use_query_cache")); + return hibernateProperties; + }*/ } + diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/specification/ResourceTest.java b/solidify-model/src/test/java/ch/unige/solidify/test/specification/ResourceTest.java index f787d4230..3f26652da 100644 --- a/solidify-model/src/test/java/ch/unige/solidify/test/specification/ResourceTest.java +++ b/solidify-model/src/test/java/ch/unige/solidify/test/specification/ResourceTest.java @@ -7,20 +7,17 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.support.AnnotationConfigContextLoader; +import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; -import javax.transaction.Transactional; import java.time.OffsetDateTime; import java.util.Optional; -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration( - classes = {MockUserJpaConfig.class}, - loader = AnnotationConfigContextLoader.class) -@Transactional +@RunWith(SpringRunner.class) +@DataJpaTest +@ContextConfiguration(classes = {MockUserJpaConfig.class}) public class ResourceTest { @Resource private EntityMockRepository entityMockRepository; diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/specification/SearchSpecificationTest.java b/solidify-model/src/test/java/ch/unige/solidify/test/specification/SearchSpecificationTest.java index afb81e825..12b578bb7 100644 --- a/solidify-model/src/test/java/ch/unige/solidify/test/specification/SearchSpecificationTest.java +++ b/solidify-model/src/test/java/ch/unige/solidify/test/specification/SearchSpecificationTest.java @@ -10,9 +10,11 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.data.jpa.domain.Specification; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.support.AnnotationConfigContextLoader; import javax.annotation.Resource; @@ -22,11 +24,9 @@ import java.util.List; import static org.hamcrest.collection.IsIn.isIn; import static org.hamcrest.core.IsNot.not; -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration( - classes = {MockUserJpaConfig.class}, - loader = AnnotationConfigContextLoader.class) -@Transactional +@RunWith(SpringRunner.class) +@DataJpaTest +@ContextConfiguration( classes = {MockUserJpaConfig.class}) public class SearchSpecificationTest { @Resource @@ -67,8 +67,9 @@ public class SearchSpecificationTest { List<MockUser> results = userRepository.findAll( Specification.where(new MockUserSearchSpecification(criteria1)) .and(new MockUserSearchSpecification(criteria2))); - Assert.assertEquals("john", user1.getName()); - Assert.assertNotEquals("jo", user1.getName()); + MockUser userFound =results.get(0); + Assert.assertEquals("john", userFound.getName()); + Assert.assertNotEquals("jo", userFound.getName()); } /** @@ -94,6 +95,8 @@ public class SearchSpecificationTest { Specification.where(new MockUserSearchSpecification(criteria1)) .and(new MockUserSearchSpecification(criteria2))); Assert.assertEquals(1,results.size()); + MockUser userFound =results.get(0); + Assert.assertEquals("john", userFound.getName()); } diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUser.java b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUser.java index cdb19dc2a..1c5e0b0d2 100644 --- a/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUser.java +++ b/solidify-model/src/test/java/ch/unige/solidify/test/specification/dao/MockUser.java @@ -42,4 +42,13 @@ public class MockUser { public void setAge(int age) { this.age = age; } + + @Override + public String toString() { + return "MockUser{" + + "id=" + id + + ", name='" + name + '\'' + + ", age=" + age + + '}'; + } } diff --git a/solidify-model/src/test/resources/application.yaml b/solidify-model/src/test/resources/application.yaml new file mode 100644 index 000000000..d622b43e5 --- /dev/null +++ b/solidify-model/src/test/resources/application.yaml @@ -0,0 +1,14 @@ +spring: + application: + name: test + datasource: + url: jdbc:h2:~/testdb;DB_CLOSE_DELAY=-1 + username: sa + password: + driver-class-name: org.h2.Driver + h2: + console: + enabled: true + jpa: + database-platform: org.hibernate.dialect.H2Dialect + show-sql: true \ No newline at end of file diff --git a/solidify-model/src/test/resources/persistence-test.properties b/solidify-model/src/test/resources/persistence-test.properties deleted file mode 100644 index 3b6b58063..000000000 --- a/solidify-model/src/test/resources/persistence-test.properties +++ /dev/null @@ -1,9 +0,0 @@ -jdbc.driverClassName=org.h2.Driver -jdbc.url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1 - -hibernate.dialect=org.hibernate.dialect.H2Dialect -hibernate.show_sql=true -hibernate.hbm2ddl.auto=create - -hibernate.cache.use_second_level_cache=false -hibernate.cache.use_query_cache=false \ No newline at end of file -- GitLab From f248e114cfd48ca95aa5292d591c600d3f5f275d Mon Sep 17 00:00:00 2001 From: wangr <raoli.wang@unige.ch> Date: Mon, 12 Nov 2018 17:01:50 +0100 Subject: [PATCH 12/14] fix: code refactoring for unit tests in Model module --- .../controller/UserShibboletControllerTest.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java index 6df6bd750..0e0f4af12 100644 --- a/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/controller/UserShibboletControllerTest.java @@ -26,14 +26,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest(classes = MockApplication.class) public class UserShibboletControllerTest { + private static final String HEADER_UNIQUEID_VALUE = "123456@unige.ch"; @Autowired private MockMvc mockMvc; - @MockBean private UserService userService; - private static final String HEADER_UNIQUEID_VALUE = "123456@unige.ch"; - @Test @WithMockUser(username = "admin", roles = {"techAdmin"}) public void shibboUserTest() throws Exception { @@ -55,14 +53,5 @@ public class UserShibboletControllerTest { } - @Test - @WithMockUser() - public void userTest() throws Exception { - SsoUserData uTest = new SsoUserData(); - // User uTest = User.create(u); - when(userService.current()).thenReturn(uTest); - mockMvc.perform(get("/api/user")) - .andDo(print()); - } } -- GitLab From 3d3be9530b4ad7d830d898ca71906fe5ff876292 Mon Sep 17 00:00:00 2001 From: wangr <raoli.wang@unige.ch> Date: Mon, 26 Nov 2018 16:39:53 +0100 Subject: [PATCH 13/14] feat: use junit test suite to share cached spring context --- pom.xml | 2 +- .../ch/unige/solidify/test/AuthTestSuite.java | 17 +++++++++ .../solidify/test/ControllerTestSuite.java | 19 ++++++++++ .../unige/solidify/test/ModelTestSuite.java | 19 ++++++++++ .../test/config/MockUserJpaConfig.java | 38 ------------------- .../ch/unige/solidify/test/UtilTestSuite.java | 17 +++++++++ 6 files changed, 73 insertions(+), 39 deletions(-) create mode 100644 solidify-auth/src/test/java/ch/unige/solidify/test/AuthTestSuite.java create mode 100644 solidify-controller/src/test/java/ch/unige/solidify/test/ControllerTestSuite.java create mode 100644 solidify-model/src/test/java/ch/unige/solidify/test/ModelTestSuite.java create mode 100644 solidify-util/src/test/java/ch/unige/solidify/test/UtilTestSuite.java diff --git a/pom.xml b/pom.xml index 7af71e9a8..7eba230d4 100644 --- a/pom.xml +++ b/pom.xml @@ -197,7 +197,7 @@ <configuration> <trimStackTrace>false</trimStackTrace> <includes> - <include>**/*Test.java</include> + <include>**/*TestSuite.java</include> <include>**/*Documentation.java</include> </includes> </configuration> diff --git a/solidify-auth/src/test/java/ch/unige/solidify/test/AuthTestSuite.java b/solidify-auth/src/test/java/ch/unige/solidify/test/AuthTestSuite.java new file mode 100644 index 000000000..539450acd --- /dev/null +++ b/solidify-auth/src/test/java/ch/unige/solidify/test/AuthTestSuite.java @@ -0,0 +1,17 @@ +package ch.unige.solidify.test; + +import ch.unige.solidify.test.auth.AuthMockToollTest; +import ch.unige.solidify.test.auth.ShibboFilterTest; +import ch.unige.solidify.test.auth.ShibbolethSsoUserTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) + +@Suite.SuiteClasses(value = { + AuthMockToollTest.class, + ShibboFilterTest.class, + ShibbolethSsoUserTest.class +}) +public class AuthTestSuite { +} diff --git a/solidify-controller/src/test/java/ch/unige/solidify/test/ControllerTestSuite.java b/solidify-controller/src/test/java/ch/unige/solidify/test/ControllerTestSuite.java new file mode 100644 index 000000000..aaee5f90e --- /dev/null +++ b/solidify-controller/src/test/java/ch/unige/solidify/test/ControllerTestSuite.java @@ -0,0 +1,19 @@ +package ch.unige.solidify.test; + +import ch.unige.solidify.test.controller.ResourceReadOnlyControllerTest; +import ch.unige.solidify.test.controller.UserServiceTest; +import ch.unige.solidify.test.controller.UserShibboletControllerTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses( + value={ + ResourceReadOnlyControllerTest.class, + UserServiceTest.class, + UserShibboletControllerTest.class + } +) +public class ControllerTestSuite { + +} diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/ModelTestSuite.java b/solidify-model/src/test/java/ch/unige/solidify/test/ModelTestSuite.java new file mode 100644 index 000000000..4ebc1ea0e --- /dev/null +++ b/solidify-model/src/test/java/ch/unige/solidify/test/ModelTestSuite.java @@ -0,0 +1,19 @@ +package ch.unige.solidify.test; + +import ch.unige.solidify.test.specification.ResourceTest; +import ch.unige.solidify.test.specification.SearchSpecificationTest; +import ch.unige.solidify.test.util.ChecksumTest; +import ch.unige.solidify.test.util.StringParserToolTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) + +@Suite.SuiteClasses(value = { + ResourceTest.class, + SearchSpecificationTest.class, + ChecksumTest.class, + StringParserToolTest.class +}) +public class ModelTestSuite { +} diff --git a/solidify-model/src/test/java/ch/unige/solidify/test/config/MockUserJpaConfig.java b/solidify-model/src/test/java/ch/unige/solidify/test/config/MockUserJpaConfig.java index 1725d4cf2..8c7b3de97 100644 --- a/solidify-model/src/test/java/ch/unige/solidify/test/config/MockUserJpaConfig.java +++ b/solidify-model/src/test/java/ch/unige/solidify/test/config/MockUserJpaConfig.java @@ -12,46 +12,8 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; @EnableJpaRepositories(basePackages = "ch.unige.solidify.test.specification.dao") @ComponentScan({"ch.unige.solidify.test"}) @EntityScan(basePackages = "ch.unige.solidify.test") -//@PropertySource("persistence-test.properties") @EnableTransactionManagement public class MockUserJpaConfig { - /** @Autowired private Environment env; - @Bean public DataSource dataSource() { - DriverManagerDataSource dataSource = new DriverManagerDataSource(); - dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName")); - dataSource.setUrl(env.getProperty("jdbc.url")); - dataSource.setUsername(env.getProperty("jdbc.user")); - dataSource.setPassword(env.getProperty("jdbc.pass")); - return dataSource; - } - - @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); - em.setDataSource(dataSource()); - em.setPackagesToScan(new String[]{"ch.unige.solidify.test"}); - em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); - em.setJpaProperties(additionalProperties()); - return em; - } - - @Bean JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { - JpaTransactionManager transactionManager = new JpaTransactionManager(); - transactionManager.setEntityManagerFactory(entityManagerFactory); - return transactionManager; - } - - final Properties additionalProperties() { - final Properties hibernateProperties = new Properties(); - hibernateProperties - .setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); - hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); - hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql")); - hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", - env.getProperty("hibernate.cache.use_second_level_cache")); - hibernateProperties.setProperty("hibernate.cache.use_query_cache", - env.getProperty("hibernate.cache.use_query_cache")); - return hibernateProperties; - }*/ } diff --git a/solidify-util/src/test/java/ch/unige/solidify/test/UtilTestSuite.java b/solidify-util/src/test/java/ch/unige/solidify/test/UtilTestSuite.java new file mode 100644 index 000000000..4d0952cdf --- /dev/null +++ b/solidify-util/src/test/java/ch/unige/solidify/test/UtilTestSuite.java @@ -0,0 +1,17 @@ +package ch.unige.solidify.test; + +import ch.unige.solidify.test.util.StringTest; +import ch.unige.solidify.test.util.XMLTest; +import ch.unige.solidify.test.util.ZipTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) + +@Suite.SuiteClasses(value = { + StringTest.class, + XMLTest.class, + ZipTest.class +}) +public class UtilTestSuite { +} -- GitLab From 55517bbd94d8c0c3b2238fbb58ea4ad959d7aae2 Mon Sep 17 00:00:00 2001 From: wangr <raoli.wang@unige.ch> Date: Mon, 26 Nov 2018 16:42:05 +0100 Subject: [PATCH 14/14] feat: move advanced search fonction into service and add methode-link function --- .../ResourceReadOnlyController.java | 45 ++++----- .../service/CompositeResourceService.java | 92 +++++++++++++------ 2 files changed, 79 insertions(+), 58 deletions(-) diff --git a/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java b/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java index 1b3f40d99..df3f34148 100644 --- a/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java +++ b/solidify-controller/src/main/java/ch/unige/solidify/controller/ResourceReadOnlyController.java @@ -6,13 +6,13 @@ import ch.unige.solidify.rest.Resource; import ch.unige.solidify.rest.Tool; import ch.unige.solidify.service.CompositeResourceService; import ch.unige.solidify.util.SearchCriteria; -import ch.unige.solidify.util.SearchOperation; import ch.unige.solidify.util.StringParserTool; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.hateoas.ResourceSupport; +import org.springframework.hateoas.mvc.ControllerLinkBuilder; import org.springframework.http.HttpEntity; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; public abstract class ResourceReadOnlyController<T extends Resource<T>> extends SolidifyController { @@ -53,7 +54,7 @@ public abstract class ResourceReadOnlyController<T extends Resource<T>> extends String matchtype, Pageable pageable) { Page<T> listItem = - findBySearchCriteria(search, matchtype, search.getSearchCriterias(), pageable); + itemService.findBySearchCriteria(search, matchtype, search.getSearchCriterias(), pageable); setRessourceLinks(listItem); Collection<T> collection = setCollectionLinks(listItem, pageable); return new ResponseEntity(collection, HttpStatus.OK); @@ -67,38 +68,15 @@ public abstract class ResourceReadOnlyController<T extends Resource<T>> extends @RequestParam(value = "match", required = false) String matchtype, Pageable pageable) { List<SearchCriteria> criterias = StringParserTool.parseSearchString(search); - Page<T> listItem = findBySearchCriteria(resource, matchtype, criterias, pageable); + Page<T> listItem = itemService.findBySearchCriteria(resource, matchtype, criterias, pageable); + ControllerLinkBuilder clb=linkTo(methodOn(this.getClass()).advancedSearch(null,search,matchtype,pageable)); setRessourceLinks(listItem); - Collection<T> collection = setCollectionLinks(listItem, pageable); + Collection<T> collection = setCollectionLinksForMethod(listItem, pageable,clb); return new ResponseEntity(collection, HttpStatus.OK); } - protected Page<T> findBySearchCriteria(T resource, String orOpredicateFlag, - List<SearchCriteria> criterias, - Pageable pageable) { - if (criterias == null || criterias.size() == 0) { - return null; - } - Specification<T> spec = resource.getSearchSpecification(criterias.get(0)); - if (spec == null) { - return null; - } - - boolean isOrPredicate = orOpredicateFlag == null ? false : - (orOpredicateFlag.equals(SearchOperation.OR_PREDICATE_FLAG)); - for (int i = 1; i < criterias.size(); i++) { - if (isOrPredicate) { - spec = Specification.where(spec) - .or(resource.getSearchSpecification(criterias.get(i))); - } else { - spec = Specification.where(spec) - .and(resource.getSearchSpecification(criterias.get(i))); - } - } - return this.itemService.findAll(spec, pageable); - } protected void setRessourceLinks(Page<T> listItem) { @@ -123,6 +101,17 @@ public abstract class ResourceReadOnlyController<T extends Resource<T>> extends return collection; } + protected Collection<T> setCollectionLinksForMethod(Page<T> listItem, Pageable pageable, ControllerLinkBuilder clb) { + Collection<T> collection = new Collection<T>(listItem, pageable); + collection.add(clb.withSelfRel()); + collection.add(Tool.parentLink((linkTo(this.getClass())).toUriComponentsBuilder()) + .withRel(ActionName.MODULE)); + this.addSortLinks(clb, collection); + this.addPageLinks(clb, collection, pageable); + addOthersLinks(collection); + return collection; + } + protected void addLinks(T t) { t.removeLinks(); t.addLinks(linkTo(this.getClass()), true, true); diff --git a/solidify-controller/src/main/java/ch/unige/solidify/service/CompositeResourceService.java b/solidify-controller/src/main/java/ch/unige/solidify/service/CompositeResourceService.java index f4fa3c3d0..600a63ba4 100644 --- a/solidify-controller/src/main/java/ch/unige/solidify/service/CompositeResourceService.java +++ b/solidify-controller/src/main/java/ch/unige/solidify/service/CompositeResourceService.java @@ -1,10 +1,15 @@ package ch.unige.solidify.service; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.NoSuchElementException; - +import ch.unige.solidify.config.SolidifyProperties; +import ch.unige.solidify.exception.SolidifyValidationException; +import ch.unige.solidify.repository.SolidifyRepository; +import ch.unige.solidify.rest.Collection; +import ch.unige.solidify.rest.Resource; +import ch.unige.solidify.rest.SubResourceContainer; +import ch.unige.solidify.util.SearchCriteria; +import ch.unige.solidify.util.SearchOperation; +import ch.unige.solidify.util.StringTool; +import ch.unige.solidify.validation.ValidationError; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -15,14 +20,10 @@ import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.BindingResult; import org.springframework.validation.Validator; -import ch.unige.solidify.config.SolidifyProperties; -import ch.unige.solidify.exception.SolidifyValidationException; -import ch.unige.solidify.repository.SolidifyRepository; -import ch.unige.solidify.rest.Collection; -import ch.unige.solidify.rest.Resource; -import ch.unige.solidify.rest.SubResourceContainer; -import ch.unige.solidify.util.StringTool; -import ch.unige.solidify.validation.ValidationError; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; public abstract class CompositeResourceService<T extends Resource<T>> { @@ -60,7 +61,8 @@ public abstract class CompositeResourceService<T extends Resource<T>> { } protected T localFindOne(String id) { - T item = this.itemRepository.findById(id).orElseThrow(()->new NoSuchElementException("No resource with id " + id)); + T item = this.itemRepository.findById(id) + .orElseThrow(() -> new NoSuchElementException("No resource with id " + id)); return item; } @@ -80,6 +82,33 @@ public abstract class CompositeResourceService<T extends Resource<T>> { this.itemRepository.deleteById(id); } + protected Page<T> localFindBySearchCriteria(T resource, String orOpredicateFlag, + List<SearchCriteria> criterias, + Pageable pageable) { + if (criterias == null || criterias.size() == 0) { + return null; + } + Specification<T> spec = resource.getSearchSpecification(criterias.get(0)); + if (spec == null) { + return null; + } + + boolean isOrPredicate = orOpredicateFlag == null ? false : + (orOpredicateFlag.equals(SearchOperation.OR_PREDICATE_FLAG)); + for (int i = 1; i < criterias.size(); i++) { + if (isOrPredicate) { + spec = Specification.where(spec) + .or(resource.getSearchSpecification(criterias.get(i))); + } else { + spec = Specification.where(spec) + .and(resource.getSearchSpecification(criterias.get(i))); + } + } + return localFindAll(spec, pageable); + + } + + /************************************************ * REST ************************************************/ @@ -101,7 +130,7 @@ public abstract class CompositeResourceService<T extends Resource<T>> { protected void completeEmbededResources(T item) { if (item instanceof SubResourceContainer) { List<Class<? extends Resource>> embeddedResources = ((SubResourceContainer) item) - .getEmbeddedResourceTypes(); + .getEmbeddedResourceTypes(); for (Class<? extends Resource> type : embeddedResources) { this.completeEmbededResources(item, type); } @@ -109,7 +138,7 @@ public abstract class CompositeResourceService<T extends Resource<T>> { } protected <V extends Resource<V>> void completeEmbededResources(T item, - Class<V> subResourceType) { + Class<V> subResourceType) { if (item instanceof SubResourceContainer) { @@ -128,8 +157,8 @@ public abstract class CompositeResourceService<T extends Resource<T>> { if (child == null) { log.error(this.messageService.get("resources.embedded.notfound", - new Object[] { subResourceId, subResourceType.getName(), item.getResId(), - item.getClass().getName() })); + new Object[]{subResourceId, subResourceType.getName(), item.getResId(), + item.getClass().getName()})); } /* @@ -142,7 +171,7 @@ public abstract class CompositeResourceService<T extends Resource<T>> { } catch (Exception ex) { log.error("Unable to retrieve subresource " + subResourceType.getSimpleName() + " " - + item.getResId()); + + item.getResId()); } } else { throw new IllegalArgumentException("item must be a SubResourceContainer"); @@ -156,6 +185,11 @@ public abstract class CompositeResourceService<T extends Resource<T>> { /************************************************ * Composit interface ************************************************/ + public Page<T> findBySearchCriteria(T resource, String orOpredicateFlag, + List<SearchCriteria> criterias, + Pageable pageable) { + return this.localFindBySearchCriteria(resource, orOpredicateFlag, criterias, pageable); + } public boolean existsById(String resId) { return this.localExistsById(resId); @@ -189,12 +223,12 @@ public abstract class CompositeResourceService<T extends Resource<T>> { } public <V> Page<V> findByParentId(String parentId, Class<?> parentClass, Class<V> childClass, - Pageable pageable) { + Pageable pageable) { return this.itemRepository.findByParentId(parentId, parentClass, childClass, pageable); } public <V> V findByParentIdAndChildId(String parentId, Class<?> parentClass, Class<V> childClass, - String id) { + String id) { return this.itemRepository.findByParentIdAndChildId(parentId, parentClass, childClass, id); } @@ -216,7 +250,7 @@ public abstract class CompositeResourceService<T extends Resource<T>> { } public <S extends Resource<S>> Collection<S> getSubResources(Iterable<String> subResourceIds, - Class<S> subResourceType) { + Class<S> subResourceType) { List<S> items = new ArrayList<>(); if (subResourceIds != null) { @@ -280,10 +314,8 @@ public abstract class CompositeResourceService<T extends Resource<T>> { /** * Validate Bean constraints * - * @param RESTResource - * item - * @param BindingResult - * errors + * @param RESTResource item + * @param BindingResult errors */ protected void validateConstraints(T item, BindingResult errors) { this.validator.validate(item, errors); @@ -293,10 +325,8 @@ public abstract class CompositeResourceService<T extends Resource<T>> { * Checks that all linked RESTResources identified by their resId exist. Must be overridden if the * Resource has linked RESTResources * - * @param RESTResource - * item - * @param BindingResult - * errors + * @param RESTResource item + * @param BindingResult errors */ public void validateLinkedResources(T item, BindingResult errors) { } @@ -309,4 +339,6 @@ public abstract class CompositeResourceService<T extends Resource<T>> { */ protected void validateItemSpecificRules(T item, BindingResult errors) { } + + } -- GitLab