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