You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2019/05/30 10:05:34 UTC
[syncope] 02/02: [SYNCOPE-1455] SRA now working with routes defined
in Core
This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
commit 23cc100d28e4dfe2b54304681a053fdcd1bffe26
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Thu May 30 12:04:01 2019 +0200
[SYNCOPE-1455] SRA now working with routes defined in Core
---
archetype/pom.xml | 14 +-
.../META-INF/maven/archetype-metadata.xml | 20 +
.../resources/archetype-resources/enduser/pom.xml | 177 --------
.../main/resources/archetype-resources/fit/pom.xml | 275 ++++++++++++
.../main/resources/archetype-resources/sra/pom.xml | 85 ++++
.../sra/src/test}/resources/keymaster.properties | 0
archetype/src/main/resources/meta-pom.xml | 8 +
.../client/lib/SyncopeClientFactoryBean.java | 6 +-
.../syncope/common/lib/to/GatewayRouteTO.java | 26 +-
.../syncope/common/lib/types/AMEntitlement.java | 4 -
.../syncope/common/lib/types/FilterFactory.java | 7 +-
...{GatewayFilter.java => GatewayRouteFilter.java} | 20 +-
...ayPredicate.java => GatewayRoutePredicate.java} | 37 +-
.../syncope/common/lib/types/PredicateCond.java | 3 +-
.../syncope/common/lib/types/PredicateFactory.java | 1 -
.../rest/api/service/GatewayRouteService.java | 12 -
.../zookeper/ZookeeperKeymasterClientContext.java | 38 +-
.../syncope/core/logic/GatewayRouteLogic.java | 41 +-
.../rest/cxf/service/GatewayRouteServiceImpl.java | 7 +-
.../logic/init/IdMImplementationTypeLoader.java | 3 +-
.../init/ClassPathScanImplementationLookup.java | 3 +-
.../logic/init/IdRepoImplementationTypeLoader.java | 3 +-
.../core/rest/cxf/RestServiceExceptionMapper.java | 7 +-
.../core/persistence/api/entity/GatewayRoute.java | 16 +-
.../core/persistence/jpa/RuntimeDomainLoader.java | 4 +-
.../core/persistence/jpa/StartupDomainLoader.java | 2 +-
.../persistence/jpa/entity/JPAGatewayRoute.java | 29 +-
.../persistence/jpa/inner/GatewayRouteTest.java | 8 +-
.../java/data/GatewayRouteDataBinderImpl.java | 19 +-
.../syncope/core/starter/SyncopeCoreStartup.java | 2 +-
docker/sra/pom.xml | 36 +-
docker/sra/src/main/resources/Dockerfile | 4 +-
.../sra}/src/main/resources/application.properties | 6 +-
.../sra/src/main/resources/keymaster.properties | 9 +-
docker/sra/src/main/resources/log4j2.xml | 16 +-
.../sra}/src/main/resources/sra.properties | 2 -
docker/sra/src/main/resources/startup.sh | 3 +-
...ompose-zookeeper.yml => docker-compose-all.yml} | 19 +-
fit/console-reference/pom.xml | 3 +-
fit/core-reference/pom.xml | 2 +-
.../syncope/fit/core/GatewayRouteITCase.java | 52 ++-
fit/enduser-reference/pom.xml | 3 +-
pom.xml | 41 +-
sra/pom.xml | 188 ++++++--
...tartup.java => CustomGatewayFilterFactory.java} | 32 +-
.../syncope/sra/CustomRoutePredicateFactory.java | 56 +++
.../apache/syncope/sra/ManagementController.java | 93 ++++
.../java/org/apache/syncope/sra/RouteProvider.java | 472 +++++++++++++++++++++
.../org/apache/syncope/sra/RouteRefresher.java | 21 +-
.../apache/syncope/sra/SyncopeSRAApplication.java | 49 ++-
.../org/apache/syncope/sra/SyncopeSRAShutdown.java | 2 +
.../org/apache/syncope/sra/SyncopeSRAStartup.java | 2 +
sra/src/main/resources/application.properties | 4 -
sra/src/main/resources/log4j2.xml | 9 +-
sra/src/main/resources/sra.properties | 2 -
.../BodyPropertyAddingGatewayFilterFactory.java | 201 +++++++++
.../BodyPropertyMatchingRoutePredicateFactory.java | 81 ++++
.../org/apache/syncope/sra/SyncopeSRATest.java | 207 +++++++++
.../syncope/sra/SyncopeSRATestController.java} | 15 +-
.../syncope/sra/SyncopeSRATestCoreStartup.java | 116 +++++
.../sra/SyncopeSRATestKeymasterStartup.java | 92 ++++
.../{main => test}/resources/keymaster.properties | 2 +-
62 files changed, 2330 insertions(+), 387 deletions(-)
diff --git a/archetype/pom.xml b/archetype/pom.xml
index 2fcee7d..c51466b 100644
--- a/archetype/pom.xml
+++ b/archetype/pom.xml
@@ -42,7 +42,7 @@ under the License.
<extension>
<groupId>org.apache.maven.archetype</groupId>
<artifactId>archetype-packaging</artifactId>
- <version>3.0.1</version>
+ <version>3.1.0</version>
</extension>
</extensions>
@@ -334,6 +334,18 @@ under the License.
<directory>../fit/enduser-reference/src/main/webapp/WEB-INF</directory>
<targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/main/webapp/WEB-INF</targetPath>
</resource>
+
+ <resource>
+ <directory>../sra/src/main/resources</directory>
+ <targetPath>${project.build.outputDirectory}/archetype-resources/sra/src/main/resources</targetPath>
+ </resource>
+ <resource>
+ <directory>../sra/src/test/resources</directory>
+ <targetPath>${project.build.outputDirectory}/archetype-resources/sra/src/main/resources</targetPath>
+ <includes>
+ <include>keymaster.properties</include>
+ </includes>
+ </resource>
</resources>
</build>
diff --git a/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml b/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
index 391cd25..f22b5fb 100644
--- a/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
+++ b/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -79,5 +79,25 @@ under the License.
</fileSet>
</fileSets>
</module>
+ <module id="syncope-sra" dir="sra" name="syncope-sra">
+ <fileSets>
+ <fileSet filtered="false" encoding="UTF-8">
+ <directory>src/main/resources</directory>
+ </fileSet>
+ <fileSet filtered="false" encoding="UTF-8">
+ <directory>src/test/resources</directory>
+ </fileSet>
+ </fileSets>
+ </module>
+ <module id="syncope-fit" dir="fit" name="syncope-fit">
+ <fileSets>
+ <fileSet filtered="false" encoding="UTF-8">
+ <directory>src/main/resources</directory>
+ </fileSet>
+ <fileSet filtered="false" encoding="UTF-8">
+ <directory>src/test/resources</directory>
+ </fileSet>
+ </fileSets>
+ </module>
</modules>
</archetype-descriptor>
diff --git a/archetype/src/main/resources/archetype-resources/enduser/pom.xml b/archetype/src/main/resources/archetype-resources/enduser/pom.xml
index 1004ea7..2902c73 100644
--- a/archetype/src/main/resources/archetype-resources/enduser/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/enduser/pom.xml
@@ -94,183 +94,6 @@ under the License.
<profiles>
<profile>
- <id>embedded</id>
-
- <properties>
- <conf.directory>${project.build.directory}/test-classes</conf.directory>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.apache.syncope.fit</groupId>
- <artifactId>syncope-fit-build-tools</artifactId>
- <version>${syncope.version}</version>
- <type>war</type>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.h2database</groupId>
- <artifactId>h2</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <defaultGoal>clean verify cargo:run</defaultGoal>
-
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-antrun-plugin</artifactId>
- <inherited>true</inherited>
- <executions>
- <execution>
- <id>addFlowableTestContent</id>
- <phase>prepare-package</phase>
- <configuration>
- <target>
- <taskdef resource="net/sf/antcontrib/antcontrib.properties"
- classpathref="maven.plugin.classpath"/>
-
- <if>
- <not>
- <available file="../core/target/test-classes/backup/MasterContent.xml"/>
- </not>
- <then>
- <mkdir dir="../core/target/test-classes/backup"/>
- <copy file="../core/target/test-classes/domains/MasterContent.xml"
- todir="../core/target/test-classes/backup"/>
- </then>
- </if>
- <if>
- <available file="../core/target/syncope/WEB-INF/lib/flowable-engine-${flowable.version}.jar"/>
- <then>
- <xslt basedir="../core/target/test-classes/backup"
- includes="MasterContent.xml"
- destdir="../core/target/test-classes/domains"
- extension=".xml"
- force="true"
- style="../core/src/test/resources/addFlowableToContent.xsl"/>
- </then>
- </if>
- </target>
- </configuration>
- <goals>
- <goal>run</goal>
- </goals>
- </execution>
- <execution>
- <id>setupEmbeddedConf</id>
- <phase>package</phase>
- <configuration>
- <target>
- <delete dir="../core/target/syncope/WEB-INF/classes/domains"/>
- <copy todir="../core/target/syncope/WEB-INF/classes/domains">
- <fileset dir="../core/target/test-classes/domains"/>
- </copy>
- <copy file="../core/target/test-classes/connid.properties"
- todir="../core/target/syncope/WEB-INF/classes"
- overwrite="true"/>
- <copy file="../core/target/test-classes/keymaster.properties"
- todir="../core/target/syncope/WEB-INF/classes"
- overwrite="true"/>
-
- <copy file="../console/target/test-classes/keymaster.properties"
- todir="../console/target/syncope-console/WEB-INF/classes"
- overwrite="true"/>
-
- <copy file="${project.build.directory}/test-classes/enduser.properties"
- todir="${project.build.directory}/${project.build.finalName}/WEB-INF/classes"
- overwrite="true"/>
- <copy file="${project.build.directory}/test-classes/keymaster.properties"
- todir="${project.build.directory}/${project.build.finalName}/WEB-INF/classes"
- overwrite="true"/>
- <copy file="${project.build.directory}/test-classes/customFormAttributes.json"
- todir="${project.build.directory}/${project.build.finalName}/WEB-INF/classes"
- overwrite="true"/>
- <copy file="${project.build.directory}/test-classes/customTemplate.json"
- todir="${project.build.directory}/${project.build.finalName}/WEB-INF/classes"
- overwrite="true"/>
- </target>
- </configuration>
- <goals>
- <goal>run</goal>
- </goals>
- </execution>
- </executions>
- <dependencies>
- <dependency>
- <groupId>ant-contrib</groupId>
- <artifactId>ant-contrib</artifactId>
- <version>20020829</version>
- </dependency>
- </dependencies>
- </plugin>
-
- <plugin>
- <groupId>org.codehaus.cargo</groupId>
- <artifactId>cargo-maven2-plugin</artifactId>
- <inherited>true</inherited>
- <configuration>
- <container>
- <containerId>tomcat9x</containerId>
- <zipUrlInstaller>
- <url>http://central.maven.org/maven2/org/apache/tomcat/tomcat/${tomcat.version}/tomcat-${tomcat.version}.zip</url>
- <downloadDir>${settings.localRepository}/org/codehaus/cargo/cargo-container-archives</downloadDir>
- <extractDir>${project.build.directory}/cargo/extract</extractDir>
- </zipUrlInstaller>
- <dependencies>
- <dependency>
- <groupId>com.h2database</groupId>
- <artifactId>h2</artifactId>
- </dependency>
- </dependencies>
- <timeout>300000</timeout>
- <log>${cargo.log}</log>
- <output>${cargo.output}</output>
- </container>
- <configuration>
- <properties>
- <cargo.jvmargs>
- -Dspring.profiles.active=embedded
- -XX:+CMSClassUnloadingEnabled -Xmx1024m -Xms512m</cargo.jvmargs>
- </properties>
- </configuration>
- <deployables>
- <deployable>
- <groupId>org.apache.syncope.fit</groupId>
- <artifactId>syncope-fit-build-tools</artifactId>
- <type>war</type>
- <properties>
- <context>syncope-fit-build-tools</context>
- </properties>
- </deployable>
- <deployable>
- <location>../core/target/syncope</location>
- <properties>
- <context>syncope</context>
- </properties>
- </deployable>
- <deployable>
- <location>../console/target/syncope-console</location>
- <properties>
- <context>syncope-console</context>
- </properties>
- </deployable>
- <deployable>
- <location>${project.build.directory}/${project.build.finalName}</location>
- <properties>
- <context>syncope-enduser</context>
- </properties>
- </deployable>
- </deployables>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </profile>
-
- <profile>
<id>all</id>
<dependencies>
diff --git a/archetype/src/main/resources/archetype-resources/fit/pom.xml b/archetype/src/main/resources/archetype-resources/fit/pom.xml
new file mode 100644
index 0000000..7c939d2
--- /dev/null
+++ b/archetype/src/main/resources/archetype-resources/fit/pom.xml
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<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>${groupId}</groupId>
+ <artifactId>${rootArtifactId}</artifactId>
+ <version>${version}</version>
+ </parent>
+
+ <name>Apache Syncope sample project - FIT</name>
+ <groupId>${groupId}</groupId>
+ <artifactId>${artifactId}</artifactId>
+ <packaging>war</packaging>
+
+ <properties>
+ <exec.skip>true</exec.skip>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.syncope.fit</groupId>
+ <artifactId>syncope-fit-build-tools</artifactId>
+ <version>${syncope.version}</version>
+ <type>war</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-install-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-war-plugin</artifactId>
+ <inherited>false</inherited>
+ <configuration>
+ <failOnMissingWebXml>false</failOnMissingWebXml>
+ </configuration>
+ <executions>
+ <execution>
+ <id>default-war</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <inherited>true</inherited>
+ <executions>
+ <execution>
+ <id>addFlowableTestContent</id>
+ <phase>prepare-package</phase>
+ <configuration>
+ <target>
+ <taskdef resource="net/sf/antcontrib/antcontrib.properties"
+ classpathref="maven.plugin.classpath"/>
+
+ <if>
+ <not>
+ <available file="../core/target/test-classes/backup/MasterContent.xml"/>
+ </not>
+ <then>
+ <mkdir dir="../core/target/test-classes/backup"/>
+ <copy file="../core/target/test-classes/domains/MasterContent.xml"
+ todir="../core/target/test-classes/backup"/>
+ </then>
+ </if>
+ <if>
+ <available file="../core/target/syncope/WEB-INF/lib/flowable-engine-${flowable.version}.jar"/>
+ <then>
+ <xslt basedir="../core/target/test-classes/backup"
+ includes="MasterContent.xml"
+ destdir="../core/target/test-classes/domains"
+ extension=".xml"
+ force="true"
+ style="../core/src/test/resources/addFlowableToContent.xsl"/>
+ </then>
+ </if>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>setupEmbeddedConf</id>
+ <phase>package</phase>
+ <configuration>
+ <target>
+ <delete dir="../core/target/syncope/WEB-INF/classes/domains"/>
+ <copy todir="../core/target/syncope/WEB-INF/classes/domains">
+ <fileset dir="../core/target/test-classes/domains"/>
+ </copy>
+ <copy file="../core/target/test-classes/connid.properties"
+ todir="../core/target/syncope/WEB-INF/classes"
+ overwrite="true"/>
+ <copy file="../core/target/test-classes/keymaster.properties"
+ todir="../core/target/syncope/WEB-INF/classes"
+ overwrite="true"/>
+
+ <copy file="../console/target/test-classes/keymaster.properties"
+ todir="../console/target/syncope-console/WEB-INF/classes"
+ overwrite="true"/>
+
+ <copy file="../enduser/target/test-classes/keymaster.properties"
+ todir="../enduser/target/syncope-enduser/WEB-INF/classes"
+ overwrite="true"/>
+ <copy file="../enduser/target/test-classes/customFormAttributes.json"
+ todir="../enduser/target/syncope-enduser/WEB-INF/classes"
+ overwrite="true"/>
+ <copy file="../enduser/target/test-classes/customTemplate.json"
+ todir="../enduser/target/syncope-enduser/WEB-INF/classes"
+ overwrite="true"/>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>ant-contrib</groupId>
+ <artifactId>ant-contrib</artifactId>
+ <version>20020829</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <inherited>true</inherited>
+ <executions>
+ <execution>
+ <id>default-cli</id>
+ <phase>pre-integration-test</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <configuration>
+ <executable>java</executable>
+ <arguments>
+ <argument>-jar</argument>
+ <argument>${basedir}/../sra/target/syncope-sra.jar</argument>
+ <argument>-Dreactor.netty.http.server.accessLogEnabled=true</argument>
+ </arguments>
+ <environmentVariables>
+ <LOADER_PATH>${basedir}/../sra/target/test-classes</LOADER_PATH>
+ </environmentVariables>
+ <async>true</async>
+ <asyncDestroyOnShutdown>true</asyncDestroyOnShutdown>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.cargo</groupId>
+ <artifactId>cargo-maven2-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <container>
+ <containerId>tomcat9x</containerId>
+ <zipUrlInstaller>
+ <url>http://central.maven.org/maven2/org/apache/tomcat/tomcat/${tomcat.version}/tomcat-${tomcat.version}.zip</url>
+ <downloadDir>${settings.localRepository}/org/codehaus/cargo/cargo-container-archives</downloadDir>
+ <extractDir>${project.build.directory}/cargo/extract</extractDir>
+ </zipUrlInstaller>
+ <dependencies>
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ </dependency>
+ </dependencies>
+ <timeout>300000</timeout>
+ <log>${cargo.log}</log>
+ <output>${cargo.output}</output>
+ </container>
+ <configuration>
+ <properties>
+ <cargo.jvmargs>
+ -Dspring.profiles.active=embedded
+ -XX:+CMSClassUnloadingEnabled -Xmx1024m -Xms512m</cargo.jvmargs>
+ </properties>
+ </configuration>
+ <deployables>
+ <deployable>
+ <groupId>org.apache.syncope.fit</groupId>
+ <artifactId>syncope-fit-build-tools</artifactId>
+ <type>war</type>
+ <properties>
+ <context>syncope-fit-build-tools</context>
+ </properties>
+ </deployable>
+ <deployable>
+ <location>../core/target/syncope</location>
+ <properties>
+ <context>syncope</context>
+ </properties>
+ </deployable>
+ <deployable>
+ <location>../console/target/syncope-console</location>
+ <properties>
+ <context>syncope-console</context>
+ </properties>
+ </deployable>
+ <deployable>
+ <location>../enduser/target/syncope-enduser</location>
+ <properties>
+ <context>syncope-enduser</context>
+ </properties>
+ </deployable>
+ </deployables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>embedded</id>
+
+ <properties>
+ <exec.skip>false</exec.skip>
+ </properties>
+
+ <build>
+ <defaultGoal>clean verify cargo:run</defaultGoal>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/archetype/src/main/resources/archetype-resources/sra/pom.xml b/archetype/src/main/resources/archetype-resources/sra/pom.xml
new file mode 100644
index 0000000..48376d9
--- /dev/null
+++ b/archetype/src/main/resources/archetype-resources/sra/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<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>${groupId}</groupId>
+ <artifactId>${rootArtifactId}</artifactId>
+ <version>${version}</version>
+ </parent>
+
+ <name>Apache Syncope sample project - SRA</name>
+ <groupId>${groupId}</groupId>
+ <artifactId>${artifactId}</artifactId>
+ <packaging>jar</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>${groupId}</groupId>
+ <artifactId>syncope-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.syncope</groupId>
+ <artifactId>syncope-sra</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.syncope.ext.self-keymaster</groupId>
+ <artifactId>syncope-ext-self-keymaster-client</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.syncope.common.keymaster</groupId>
+ <artifactId>syncope-common-keymaster-client-zookeeper</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <finalName>syncope-sra</finalName>
+
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>org.apache.syncope.sra.SyncopeSRAApplication</mainClass>
+ <layout>ZIP</layout>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </build>
+</project>
diff --git a/sra/src/main/resources/keymaster.properties b/archetype/src/main/resources/archetype-resources/sra/src/test/resources/keymaster.properties
similarity index 100%
copy from sra/src/main/resources/keymaster.properties
copy to archetype/src/main/resources/archetype-resources/sra/src/test/resources/keymaster.properties
diff --git a/archetype/src/main/resources/meta-pom.xml b/archetype/src/main/resources/meta-pom.xml
index d16d99a..8eb4a43 100644
--- a/archetype/src/main/resources/meta-pom.xml
+++ b/archetype/src/main/resources/meta-pom.xml
@@ -83,6 +83,12 @@ under the License.
</dependency>
<dependency>
+ <groupId>org.apache.syncope</groupId>
+ <artifactId>syncope-sra</artifactId>
+ <version>${syncope.version}</version>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.syncope.ext.self-keymaster</groupId>
<artifactId>syncope-ext-self-keymaster-rest-cxf</artifactId>
<version>${syncope.version}</version>
@@ -128,6 +134,8 @@ under the License.
<module>core</module>
<module>console</module>
<module>enduser</module>
+ <module>sra</module>
+ <module>fit</module>
</modules>
</project>
diff --git a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
index f055f83..c250a19 100644
--- a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
+++ b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
@@ -277,13 +277,11 @@ public class SyncopeClientFactoryBean {
/**
* Builds client instance which will be passing the provided value in the
- * {@link javax.ws.rs.core.HttpHeaders#AUTHORIZATION}
- * request header.
+ * {@link javax.ws.rs.core.HttpHeaders#AUTHORIZATION} request header.
*
* @param jwt value received after login, in the {@link RESTHeaders#TOKEN} response header
* @return client instance which will be passing the provided value in the
- * {@link javax.ws.rs.core.HttpHeaders#AUTHORIZATION}
- * request header
+ * {@link javax.ws.rs.core.HttpHeaders#AUTHORIZATION} request header
*/
public SyncopeClient create(final String jwt) {
return create(new JWTAuthenticationHandler(jwt));
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/GatewayRouteTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/GatewayRouteTO.java
index f973c70..17ae03a 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/GatewayRouteTO.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/GatewayRouteTO.java
@@ -25,12 +25,16 @@ import java.util.List;
import javax.ws.rs.PathParam;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
-import org.apache.syncope.common.lib.types.GatewayFilter;
-import org.apache.syncope.common.lib.types.GatewayPredicate;
+import org.apache.syncope.common.lib.types.GatewayRouteFilter;
+import org.apache.syncope.common.lib.types.GatewayRoutePredicate;
import org.apache.syncope.common.lib.types.GatewayRouteStatus;
+@XmlRootElement(name = "gatewayRoute")
+@XmlType
public class GatewayRouteTO implements EntityTO {
private static final long serialVersionUID = 4044528284951757870L;
@@ -39,11 +43,13 @@ public class GatewayRouteTO implements EntityTO {
private String name;
+ private int order = 0;
+
private URI target;
- private final List<GatewayFilter> filters = new ArrayList<>();
+ private final List<GatewayRouteFilter> filters = new ArrayList<>();
- private final List<GatewayPredicate> predicates = new ArrayList<>();
+ private final List<GatewayRoutePredicate> predicates = new ArrayList<>();
private GatewayRouteStatus status;
@@ -66,6 +72,14 @@ public class GatewayRouteTO implements EntityTO {
this.name = name;
}
+ public int getOrder() {
+ return order;
+ }
+
+ public void setOrder(final int order) {
+ this.order = order;
+ }
+
public URI getTarget() {
return target;
}
@@ -77,14 +91,14 @@ public class GatewayRouteTO implements EntityTO {
@XmlElementWrapper(name = "filters")
@XmlElement(name = "filter")
@JsonProperty("filters")
- public List<GatewayFilter> getFilters() {
+ public List<GatewayRouteFilter> getFilters() {
return filters;
}
@XmlElementWrapper(name = "predicates")
@XmlElement(name = "predicate")
@JsonProperty("predicates")
- public List<GatewayPredicate> getPredicates() {
+ public List<GatewayRoutePredicate> getPredicates() {
return predicates;
}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
index 6d7d762..06d511c 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
@@ -26,12 +26,8 @@ import java.util.TreeSet;
public final class AMEntitlement {
- public static final String GATEWAY_ROUTE_LIST = "GATEWAY_ROUTE_LIST";
-
public static final String GATEWAY_ROUTE_CREATE = "GATEWAY_ROUTE_CREATE";
- public static final String GATEWAY_ROUTE_READ = "GATEWAY_ROUTE_READ";
-
public static final String GATEWAY_ROUTE_UPDATE = "GATEWAY_ROUTE_UPDATE";
public static final String GATEWAY_ROUTE_DELETE = "GATEWAY_ROUTE_DELETE";
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/FilterFactory.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/FilterFactory.java
index 1120af9..e7fd117 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/FilterFactory.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/FilterFactory.java
@@ -26,8 +26,7 @@ public enum FilterFactory {
ADD_REQUEST_PARAMETER,
ADD_RESPONSE_HEADER,
HYSTRIX,
- MODIFY_REQUEST_BODY,
- MODIFY_RESPONSE_BODY,
+ FALLBACK_HEADERS,
PREFIX_PATH,
PRESERVE_HOST_HEADER,
REDIRECT,
@@ -45,9 +44,7 @@ public enum FilterFactory {
SAVE_SESSION,
STRIP_PREFIX,
REQUEST_HEADER_TO_REQUEST_URI,
- CHANGE_REQUEST_URI,
- SET_REQIEST_SIZE,
- FALLBACK_HEADERS,
+ SET_REQUEST_SIZE,
CUSTOM
}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/GatewayFilter.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/GatewayRouteFilter.java
similarity index 80%
rename from common/am/lib/src/main/java/org/apache/syncope/common/lib/types/GatewayFilter.java
rename to common/am/lib/src/main/java/org/apache/syncope/common/lib/types/GatewayRouteFilter.java
index cbf946e..1b103a5 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/GatewayFilter.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/GatewayRouteFilter.java
@@ -19,16 +19,20 @@
package org.apache.syncope.common.lib.types;
import java.io.Serializable;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
-public class GatewayFilter implements Serializable {
+@XmlRootElement(name = "gatewayRouteFilter")
+@XmlType
+public class GatewayRouteFilter implements Serializable {
private static final long serialVersionUID = -635785645207375128L;
public static class Builder {
- private final GatewayFilter instance = new GatewayFilter();
+ private final GatewayRouteFilter instance = new GatewayRouteFilter();
public Builder factory(final FilterFactory factory) {
instance.setFactory(factory);
@@ -40,7 +44,7 @@ public class GatewayFilter implements Serializable {
return this;
}
- public GatewayFilter build() {
+ public GatewayRouteFilter build() {
return instance;
}
}
@@ -84,10 +88,18 @@ public class GatewayFilter implements Serializable {
if (getClass() != obj.getClass()) {
return false;
}
- final GatewayFilter other = (GatewayFilter) obj;
+ final GatewayRouteFilter other = (GatewayRouteFilter) obj;
return new EqualsBuilder().
append(factory, other.factory).
append(args, other.args).
build();
}
+
+ @Override
+ public String toString() {
+ return "GatewayFilter{"
+ + "factory=" + factory
+ + ", args=" + args
+ + '}';
+ }
}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/GatewayPredicate.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/GatewayRoutePredicate.java
similarity index 74%
rename from common/am/lib/src/main/java/org/apache/syncope/common/lib/types/GatewayPredicate.java
rename to common/am/lib/src/main/java/org/apache/syncope/common/lib/types/GatewayRoutePredicate.java
index 27c6635..9f26bbe 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/GatewayPredicate.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/GatewayRoutePredicate.java
@@ -19,16 +19,25 @@
package org.apache.syncope.common.lib.types;
import java.io.Serializable;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
-public class GatewayPredicate implements Serializable {
+@XmlRootElement(name = "gatewayRoutePredicate")
+@XmlType
+public class GatewayRoutePredicate implements Serializable {
private static final long serialVersionUID = -635785645207375128L;
public static class Builder {
- private final GatewayPredicate instance = new GatewayPredicate();
+ private final GatewayRoutePredicate instance = new GatewayRoutePredicate();
+
+ public Builder negate() {
+ instance.setNegate(true);
+ return this;
+ }
public Builder cond(final PredicateCond cond) {
instance.setCond(cond);
@@ -45,17 +54,27 @@ public class GatewayPredicate implements Serializable {
return this;
}
- public GatewayPredicate build() {
+ public GatewayRoutePredicate build() {
return instance;
}
}
+ private boolean negate;
+
private PredicateCond cond;
private PredicateFactory factory;
private String args;
+ public boolean isNegate() {
+ return negate;
+ }
+
+ public void setNegate(final boolean negate) {
+ this.negate = negate;
+ }
+
public PredicateCond getCond() {
return cond;
}
@@ -100,11 +119,21 @@ public class GatewayPredicate implements Serializable {
if (getClass() != obj.getClass()) {
return false;
}
- final GatewayPredicate other = (GatewayPredicate) obj;
+ final GatewayRoutePredicate other = (GatewayRoutePredicate) obj;
return new EqualsBuilder().
append(cond, other.cond).
append(factory, other.factory).
append(args, other.args).
build();
}
+
+ @Override
+ public String toString() {
+ return "GatewayPredicate{"
+ + "negate=" + negate
+ + ", cond=" + cond
+ + ", factory=" + factory
+ + ", args=" + args
+ + '}';
+ }
}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/PredicateCond.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/PredicateCond.java
index 93b0bad..12d2704 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/PredicateCond.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/PredicateCond.java
@@ -23,7 +23,6 @@ import javax.xml.bind.annotation.XmlEnum;
@XmlEnum
public enum PredicateCond {
AND,
- OR,
- NOT
+ OR
}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/PredicateFactory.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/PredicateFactory.java
index 5cf4e77..c1e431f 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/PredicateFactory.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/PredicateFactory.java
@@ -32,7 +32,6 @@ public enum PredicateFactory {
PATH,
QUERY,
REMOTE_ADDR,
- READ_BODY,
CUSTOM
}
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GatewayRouteService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GatewayRouteService.java
index 6b957b8..c44af7e 100644
--- a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GatewayRouteService.java
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GatewayRouteService.java
@@ -129,16 +129,4 @@ public interface GatewayRouteService extends JAXRSService {
@Path("push")
@Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
void pushToSRA();
-
- /**
- * Push route with matching key to SRA.
- *
- * @param key route key
- */
- @ApiResponses(
- @ApiResponse(responseCode = "204", description = "Operation was successful"))
- @POST
- @Path("push/{key}")
- @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
- void pushToSRA(@NotNull @PathParam("key") String key);
}
diff --git a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeper/ZookeeperKeymasterClientContext.java b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeper/ZookeeperKeymasterClientContext.java
index 234093f..033f5b7 100644
--- a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeper/ZookeeperKeymasterClientContext.java
+++ b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeper/ZookeeperKeymasterClientContext.java
@@ -59,7 +59,8 @@ public class ZookeeperKeymasterClientContext {
@Value("${keymaster.maxRetries:3}")
private Integer maxRetries;
- @ConditionalOnExpression("#{'${keymaster.address}' matches '^(?!http).+'}")
+ @ConditionalOnExpression("#{'${keymaster.address}' "
+ + "matches '^((\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})|[a-z\\.]+):[0-9]+$'}")
@Bean
public CuratorFramework curatorFramework() throws InterruptedException {
if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) {
@@ -86,20 +87,18 @@ public class ZookeeperKeymasterClientContext {
connectString(address).
retryPolicy(new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries));
if (StringUtils.isNotBlank(username)) {
- clientBuilder.
- authorization("digest", (username).getBytes()).
- aclProvider(new ACLProvider() {
-
- @Override
- public List<ACL> getDefaultAcl() {
- return ZooDefs.Ids.CREATOR_ALL_ACL;
- }
-
- @Override
- public List<ACL> getAclForPath(final String path) {
- return ZooDefs.Ids.CREATOR_ALL_ACL;
- }
- });
+ clientBuilder.authorization("digest", username.getBytes()).aclProvider(new ACLProvider() {
+
+ @Override
+ public List<ACL> getDefaultAcl() {
+ return ZooDefs.Ids.CREATOR_ALL_ACL;
+ }
+
+ @Override
+ public List<ACL> getAclForPath(final String path) {
+ return ZooDefs.Ids.CREATOR_ALL_ACL;
+ }
+ });
}
CuratorFramework client = clientBuilder.build();
client.start();
@@ -108,20 +107,23 @@ public class ZookeeperKeymasterClientContext {
return client;
}
- @ConditionalOnExpression("#{'${keymaster.address}' matches '^(?!http).+'}")
+ @ConditionalOnExpression("#{'${keymaster.address}' "
+ + "matches '^((\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})|[a-z\\.]+):[0-9]+$'}")
@Bean
public ConfParamOps selfConfParamOps() {
return new ZookeeperConfParamOps();
}
- @ConditionalOnExpression("#{'${keymaster.address}' matches '^(?!http).+'}")
+ @ConditionalOnExpression("#{'${keymaster.address}' "
+ + "matches '^((\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})|[a-z\\.]+):[0-9]+$'}")
@Bean
public ServiceOps serviceOps() {
return new ZookeeperServiceDiscoveryOps();
//return new ZookeeperServiceOps();
}
- @ConditionalOnExpression("#{'${keymaster.address}' matches '^(?!http).+'}")
+ @ConditionalOnExpression("#{'${keymaster.address}' "
+ + "matches '^((\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})|[a-z\\.]+):[0-9]+$'}")
@Bean
public DomainOps domainOps() {
return new ZookeeperDomainOps();
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/GatewayRouteLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/GatewayRouteLogic.java
index 3d57c17..71a343e 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/GatewayRouteLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/GatewayRouteLogic.java
@@ -19,9 +19,20 @@
package org.apache.syncope.core.logic;
import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
import java.util.List;
import java.util.stream.Collectors;
+import javax.annotation.Resource;
+import javax.ws.rs.core.HttpHeaders;
import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.transport.http.auth.DefaultBasicAuthSupplier;
+import org.apache.syncope.common.keymaster.client.api.KeymasterException;
+import org.apache.syncope.common.keymaster.client.api.ServiceOps;
+import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
import org.apache.syncope.common.lib.to.GatewayRouteTO;
import org.apache.syncope.common.lib.types.AMEntitlement;
import org.apache.syncope.core.persistence.api.dao.GatewayRouteDAO;
@@ -45,6 +56,16 @@ public class GatewayRouteLogic extends AbstractTransactionalLogic<GatewayRouteTO
@Autowired
private EntityFactory entityFactory;
+ @Autowired
+ private ServiceOps serviceOps;
+
+ @Resource(name = "anonymousUser")
+ private String anonymousUser;
+
+ @Resource(name = "anonymousKey")
+ private String anonymousKey;
+
+ @PreAuthorize("isAuthenticated()")
public List<GatewayRouteTO> list() {
return routeDAO.findAll().stream().map(binder::getGatewayRouteTO).collect(Collectors.toList());
}
@@ -57,7 +78,7 @@ public class GatewayRouteLogic extends AbstractTransactionalLogic<GatewayRouteTO
return binder.getGatewayRouteTO(routeDAO.save(route));
}
- @PreAuthorize("hasRole('" + AMEntitlement.GATEWAY_ROUTE_READ + "')")
+ @PreAuthorize("isAuthenticated()")
public GatewayRouteTO read(final String key) {
GatewayRoute route = routeDAO.find(key);
if (route == null) {
@@ -92,12 +113,18 @@ public class GatewayRouteLogic extends AbstractTransactionalLogic<GatewayRouteTO
@PreAuthorize("hasRole('" + AMEntitlement.GATEWAY_ROUTE_PUSH + "')")
public void pushToSRA() {
- // TODO
- }
-
- @PreAuthorize("hasRole('" + AMEntitlement.GATEWAY_ROUTE_PUSH + "')")
- public void pushToSRA(final String key) {
- // TODO
+ try {
+ NetworkService sra = serviceOps.get(NetworkService.Type.SRA);
+ HttpClient.newBuilder().build().sendAsync(
+ HttpRequest.newBuilder(URI.create(
+ StringUtils.appendIfMissing(sra.getAddress(), "/") + "management/routes/refresh")).
+ header(HttpHeaders.AUTHORIZATION,
+ DefaultBasicAuthSupplier.getBasicAuthHeader(anonymousUser, anonymousKey)).
+ POST(HttpRequest.BodyPublishers.noBody()).build(),
+ HttpResponse.BodyHandlers.discarding());
+ } catch (KeymasterException e) {
+ throw new NotFoundException("Could not find any SRA instance", e);
+ }
}
@Override
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GatewayRouteServiceImpl.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GatewayRouteServiceImpl.java
index f02f0c1..545b630 100644
--- a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GatewayRouteServiceImpl.java
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GatewayRouteServiceImpl.java
@@ -33,7 +33,7 @@ public class GatewayRouteServiceImpl extends AbstractServiceImpl implements Gate
@Autowired
private GatewayRouteLogic logic;
-
+
@Override
public List<GatewayRouteTO> list() {
return logic.list();
@@ -67,9 +67,4 @@ public class GatewayRouteServiceImpl extends AbstractServiceImpl implements Gate
public void pushToSRA() {
logic.pushToSRA();
}
-
- @Override
- public void pushToSRA(final String key) {
- logic.pushToSRA(key);
- }
}
diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/init/IdMImplementationTypeLoader.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/init/IdMImplementationTypeLoader.java
index b3a039c..1a64ee1 100644
--- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/init/IdMImplementationTypeLoader.java
+++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/init/IdMImplementationTypeLoader.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.logic.init;
import org.apache.syncope.common.lib.types.IdMImplementationType;
import org.apache.syncope.common.lib.types.ImplementationTypesHolder;
import org.apache.syncope.core.persistence.api.SyncopeCoreLoader;
+import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
@Component
@@ -28,7 +29,7 @@ public class IdMImplementationTypeLoader implements SyncopeCoreLoader {
@Override
public int getOrder() {
- return Integer.MIN_VALUE;
+ return Ordered.HIGHEST_PRECEDENCE;
}
@Override
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
index f1c21a8..c594ecb 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
@@ -61,6 +61,7 @@ import org.apache.syncope.core.spring.security.JWTSSOProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.core.Ordered;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.util.ClassUtils;
@@ -91,7 +92,7 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
@Override
public int getOrder() {
- return 250;
+ return Ordered.HIGHEST_PRECEDENCE;
}
/**
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/IdRepoImplementationTypeLoader.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/IdRepoImplementationTypeLoader.java
index 3735d1c..0eded04 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/IdRepoImplementationTypeLoader.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/IdRepoImplementationTypeLoader.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.logic.init;
import org.apache.syncope.common.lib.types.IdRepoImplementationType;
import org.apache.syncope.common.lib.types.ImplementationTypesHolder;
import org.apache.syncope.core.persistence.api.SyncopeCoreLoader;
+import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
@Component
@@ -28,7 +29,7 @@ public class IdRepoImplementationTypeLoader implements SyncopeCoreLoader {
@Override
public int getOrder() {
- return Integer.MIN_VALUE;
+ return Ordered.HIGHEST_PRECEDENCE;
}
@Override
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
index d3b1b77..f5e6be1 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
@@ -103,10 +103,13 @@ public class RestServiceExceptionMapper implements ExceptionMapper<Exception> {
builder = builder(ClientExceptionType.DelegatedAdministration, ExceptionUtils.getRootCauseMessage(ex));
} else if (ex instanceof EntityExistsException || ex instanceof DuplicateException
- || ex instanceof PersistenceException && ex.getCause() instanceof EntityExistsException) {
+ || ((ex instanceof PersistenceException || ex instanceof DataIntegrityViolationException)
+ && ex.getCause() instanceof EntityExistsException)) {
builder = builder(ClientExceptionType.EntityExists,
- getPersistenceErrorMessage(ex instanceof PersistenceException ? ex.getCause() : ex));
+ getPersistenceErrorMessage(
+ ex instanceof PersistenceException || ex instanceof DataIntegrityViolationException
+ ? ex.getCause() : ex));
} else if (ex instanceof DataIntegrityViolationException || ex instanceof UncategorizedDataAccessException) {
builder = builder(ClientExceptionType.DataIntegrityViolation, getPersistenceErrorMessage(ex));
} else if (ex instanceof ConnectorException) {
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/GatewayRoute.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/GatewayRoute.java
index 70f1e6e..4aef3f7 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/GatewayRoute.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/GatewayRoute.java
@@ -20,8 +20,8 @@ package org.apache.syncope.core.persistence.api.entity;
import java.net.URI;
import java.util.List;
-import org.apache.syncope.common.lib.types.GatewayFilter;
-import org.apache.syncope.common.lib.types.GatewayPredicate;
+import org.apache.syncope.common.lib.types.GatewayRouteFilter;
+import org.apache.syncope.common.lib.types.GatewayRoutePredicate;
import org.apache.syncope.common.lib.types.GatewayRouteStatus;
public interface GatewayRoute extends Entity {
@@ -30,17 +30,21 @@ public interface GatewayRoute extends Entity {
void setName(String name);
+ int getOrder();
+
+ void setOrder(int order);
+
URI getTarget();
void setTarget(URI target);
- List<GatewayFilter> getFilters();
+ List<GatewayRouteFilter> getFilters();
- void setFilters(List<GatewayFilter> filters);
+ void setFilters(List<GatewayRouteFilter> filters);
- List<GatewayPredicate> getPredicates();
+ List<GatewayRoutePredicate> getPredicates();
- void setPredicates(List<GatewayPredicate> predicates);
+ void setPredicates(List<GatewayRoutePredicate> predicates);
GatewayRouteStatus getStatus();
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/RuntimeDomainLoader.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/RuntimeDomainLoader.java
index ffc258d..36ba211 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/RuntimeDomainLoader.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/RuntimeDomainLoader.java
@@ -53,7 +53,7 @@ public class RuntimeDomainLoader implements DomainWatcher {
ApplicationContextProvider.getApplicationContext().getBeansOfType(SyncopeCoreLoader.class).values().
stream().sorted(Comparator.comparing(SyncopeCoreLoader::getOrder)).
- forEach(loader -> {
+ forEachOrdered(loader -> {
String loaderName = AopUtils.getTargetClass(loader).getName();
LOG.debug("[{}] Starting on domain '{}'", loaderName, domain);
@@ -72,7 +72,7 @@ public class RuntimeDomainLoader implements DomainWatcher {
ApplicationContextProvider.getApplicationContext().getBeansOfType(SyncopeCoreLoader.class).values().
stream().sorted(Comparator.comparing(SyncopeCoreLoader::getOrder).reversed()).
- forEach(loader -> {
+ forEachOrdered(loader -> {
String loaderName = AopUtils.getTargetClass(loader).getName();
LOG.debug("[{}] Starting on domain '{}'", loaderName, domain);
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java
index 6364dd2..545c14c 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java
@@ -69,7 +69,7 @@ public class StartupDomainLoader implements SyncopeCoreLoader {
@Override
public int getOrder() {
- return Ordered.HIGHEST_PRECEDENCE;
+ return Ordered.LOWEST_PRECEDENCE;
}
@Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAGatewayRoute.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAGatewayRoute.java
index 8aee362..a6c4e1e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAGatewayRoute.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAGatewayRoute.java
@@ -29,8 +29,8 @@ import javax.persistence.Enumerated;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
-import org.apache.syncope.common.lib.types.GatewayFilter;
-import org.apache.syncope.common.lib.types.GatewayPredicate;
+import org.apache.syncope.common.lib.types.GatewayRouteFilter;
+import org.apache.syncope.common.lib.types.GatewayRoutePredicate;
import org.apache.syncope.common.lib.types.GatewayRouteStatus;
import org.apache.syncope.core.persistence.api.entity.GatewayRoute;
import org.apache.syncope.core.persistence.jpa.validation.entity.GatewayRouteCheck;
@@ -48,6 +48,9 @@ public class JPAGatewayRoute extends AbstractGeneratedKeyEntity implements Gatew
@Column(unique = true, nullable = false)
private String name;
+ private Integer routeOrder;
+
+ @NotNull
private String target;
@Lob
@@ -71,6 +74,16 @@ public class JPAGatewayRoute extends AbstractGeneratedKeyEntity implements Gatew
}
@Override
+ public int getOrder() {
+ return routeOrder == null ? 0 : routeOrder;
+ }
+
+ @Override
+ public void setOrder(final int order) {
+ this.routeOrder = order;
+ }
+
+ @Override
public URI getTarget() {
return URI.create(target);
}
@@ -81,26 +94,26 @@ public class JPAGatewayRoute extends AbstractGeneratedKeyEntity implements Gatew
}
@Override
- public List<GatewayFilter> getFilters() {
+ public List<GatewayRouteFilter> getFilters() {
return filters == null
? Collections.emptyList()
- : Arrays.asList(POJOHelper.deserialize(filters, GatewayFilter[].class));
+ : Arrays.asList(POJOHelper.deserialize(filters, GatewayRouteFilter[].class));
}
@Override
- public void setFilters(final List<GatewayFilter> filters) {
+ public void setFilters(final List<GatewayRouteFilter> filters) {
this.filters = POJOHelper.serialize(filters);
}
@Override
- public List<GatewayPredicate> getPredicates() {
+ public List<GatewayRoutePredicate> getPredicates() {
return predicates == null
? Collections.emptyList()
- : Arrays.asList(POJOHelper.deserialize(predicates, GatewayPredicate[].class));
+ : Arrays.asList(POJOHelper.deserialize(predicates, GatewayRoutePredicate[].class));
}
@Override
- public void setPredicates(final List<GatewayPredicate> predicates) {
+ public void setPredicates(final List<GatewayRoutePredicate> predicates) {
this.predicates = POJOHelper.serialize(predicates);
}
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GatewayRouteTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GatewayRouteTest.java
index 2bcc959..7fdab60 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GatewayRouteTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GatewayRouteTest.java
@@ -28,8 +28,8 @@ import java.util.List;
import java.util.UUID;
import javax.ws.rs.HttpMethod;
import org.apache.syncope.common.lib.types.FilterFactory;
-import org.apache.syncope.common.lib.types.GatewayFilter;
-import org.apache.syncope.common.lib.types.GatewayPredicate;
+import org.apache.syncope.common.lib.types.GatewayRouteFilter;
+import org.apache.syncope.common.lib.types.GatewayRoutePredicate;
import org.apache.syncope.common.lib.types.GatewayRouteStatus;
import org.apache.syncope.common.lib.types.PredicateFactory;
import org.apache.syncope.core.persistence.api.dao.GatewayRouteDAO;
@@ -68,9 +68,9 @@ public class GatewayRouteTest extends AbstractTest {
GatewayRoute route = entityFactory.newEntity(GatewayRoute.class);
route.setName("just for test");
route.setTarget(URI.create("http://httpbin.org:80"));
- route.setPredicates(Arrays.asList(new GatewayPredicate.Builder().
+ route.setPredicates(Arrays.asList(new GatewayRoutePredicate.Builder().
factory(PredicateFactory.METHOD).args(HttpMethod.GET).build()));
- route.setFilters(Arrays.asList(new GatewayFilter.Builder().
+ route.setFilters(Arrays.asList(new GatewayRouteFilter.Builder().
factory(FilterFactory.ADD_REQUEST_HEADER).args("X-Request-Foo, Bar").build()));
route.setStatus(GatewayRouteStatus.DRAFT);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GatewayRouteDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GatewayRouteDataBinderImpl.java
index ac7e563..0259510 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GatewayRouteDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GatewayRouteDataBinderImpl.java
@@ -18,7 +18,11 @@
*/
package org.apache.syncope.core.provisioning.java.data;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.GatewayRouteTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.GatewayRouteStatus;
import org.apache.syncope.core.persistence.api.entity.GatewayRoute;
import org.apache.syncope.core.provisioning.api.data.GatewayRouteDataBinder;
import org.springframework.stereotype.Component;
@@ -28,11 +32,23 @@ public class GatewayRouteDataBinderImpl implements GatewayRouteDataBinder {
@Override
public void getGatewayRoute(final GatewayRoute route, final GatewayRouteTO routeTO) {
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
+ if (StringUtils.isBlank(routeTO.getName())) {
+ sce.getElements().add("name");
+ }
+ if (routeTO.getTarget() == null) {
+ sce.getElements().add("target");
+ }
+ if (!sce.isEmpty()) {
+ throw sce;
+ }
+
route.setName(routeTO.getName());
+ route.setOrder(routeTO.getOrder());
route.setTarget(routeTO.getTarget());
route.setFilters(routeTO.getFilters());
route.setPredicates(routeTO.getPredicates());
- route.setStatus(routeTO.getStatus());
+ route.setStatus(routeTO.getStatus() == null ? GatewayRouteStatus.DRAFT : routeTO.getStatus());
}
@Override
@@ -40,6 +56,7 @@ public class GatewayRouteDataBinderImpl implements GatewayRouteDataBinder {
GatewayRouteTO routeTO = new GatewayRouteTO();
routeTO.setKey(route.getKey());
routeTO.setName(route.getName());
+ routeTO.setOrder(route.getOrder());
routeTO.setTarget(route.getTarget());
routeTO.getFilters().addAll(route.getFilters());
routeTO.getPredicates().addAll(route.getPredicates());
diff --git a/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreStartup.java b/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreStartup.java
index a1d130a..f93de92 100644
--- a/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreStartup.java
+++ b/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreStartup.java
@@ -51,7 +51,7 @@ public class SyncopeCoreStartup extends SyncopeCoreStartStop
public void onApplicationEvent(final ContextRefreshedEvent event) {
event.getApplicationContext().getBeansOfType(SyncopeCoreLoader.class).values().stream().
sorted(Comparator.comparing(SyncopeCoreLoader::getOrder)).
- forEach(loader -> {
+ forEachOrdered(loader -> {
String loaderName = AopUtils.getTargetClass(loader).getName();
LOG.debug("[{}] Starting initialization", loaderName);
diff --git a/docker/sra/pom.xml b/docker/sra/pom.xml
index 2866259..6a80959 100644
--- a/docker/sra/pom.xml
+++ b/docker/sra/pom.xml
@@ -42,26 +42,36 @@ under the License.
<groupId>org.apache.syncope</groupId>
<artifactId>syncope-sra</artifactId>
<version>${project.version}</version>
- <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.syncope.ext.self-keymaster</groupId>
+ <artifactId>syncope-ext-self-keymaster-client</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.syncope.common.keymaster</groupId>
+ <artifactId>syncope-common-keymaster-client-zookeeper</artifactId>
+ <version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-antrun-plugin</artifactId>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>org.apache.syncope.sra.SyncopeSRAApplication</mainClass>
+ <layout>ZIP</layout>
+ </configuration>
<executions>
<execution>
<goals>
- <goal>run</goal>
+ <goal>repackage</goal>
</goals>
- <phase>package</phase>
<configuration>
- <target>
- <copy file="${settings.localRepository}/org/apache/syncope/syncope-sra/${syncope.version}/syncope-sra-${syncope.version}.jar"
- todir="${project.build.outputDirectory}"/>
- </target>
+ <outputDirectory>${project.build.outputDirectory}</outputDirectory>
</configuration>
</execution>
</executions>
@@ -105,14 +115,6 @@ under the License.
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
-
- <resource>
- <directory>${project.basedir}/../../sra/src/main/resources</directory>
- <includes>
- <include>application.properties</include>
- </includes>
- <filtering>true</filtering>
- </resource>
</resources>
</build>
diff --git a/docker/sra/src/main/resources/Dockerfile b/docker/sra/src/main/resources/Dockerfile
index 5651fc0..c7398f3 100644
--- a/docker/sra/src/main/resources/Dockerfile
+++ b/docker/sra/src/main/resources/Dockerfile
@@ -26,10 +26,10 @@ RUN mkdir /opt/syncope/conf
RUN mkdir /opt/syncope/lib
RUN mkdir /opt/syncope/log
-COPY application.properties /opt/syncope/conf/
+COPY *.properties /opt/syncope/conf/
COPY log4j2.xml /opt/syncope/conf/
-COPY syncope-sra-*jar /opt/syncope/lib/syncope-sra.jar
+COPY syncope-docker-sra-*jar /opt/syncope/lib/syncope-sra.jar
COPY startup.sh /opt/syncope/bin
RUN chmod 755 /opt/syncope/bin/startup.sh
diff --git a/sra/src/main/resources/application.properties b/docker/sra/src/main/resources/application.properties
similarity index 85%
copy from sra/src/main/resources/application.properties
copy to docker/sra/src/main/resources/application.properties
index 98bc964..b6b5147 100644
--- a/sra/src/main/resources/application.properties
+++ b/docker/sra/src/main/resources/application.properties
@@ -20,13 +20,9 @@ spring.main.banner-mode=log
server.port=8080
-management.endpoint.gateway.enabled=true
-management.endpoints.web.exposure.include=gateway
-
spring.cloud.gateway.metrics.enabled=true
management.endpoint.metrics.enabled=true
-management.endpoints.web.exposure.include=*
management.endpoint.prometheus.enabled=true
management.metrics.export.prometheus.enabled=true
-service.discovery.address=http://localhost:8080/
+service.discovery.address=${SERVICE_DISCOVERY_ADDRESS}
diff --git a/sra/src/main/resources/sra.properties b/docker/sra/src/main/resources/keymaster.properties
similarity index 86%
copy from sra/src/main/resources/sra.properties
copy to docker/sra/src/main/resources/keymaster.properties
index 6789cfd..14e8ca6 100644
--- a/sra/src/main/resources/sra.properties
+++ b/docker/sra/src/main/resources/keymaster.properties
@@ -14,9 +14,6 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-sra.directory=${conf.directory}
-
-anonymousUser=${anonymousUser}
-anonymousKey=${anonymousKey}
-
-useGZIPCompression=true
+keymaster.address=${KEYMASTER_ADDRESS}
+keymaster.username=${KEYMASTER_USERNAME}
+keymaster.password=${KEYMASTER_PASSWORD}
diff --git a/docker/sra/src/main/resources/log4j2.xml b/docker/sra/src/main/resources/log4j2.xml
index 033f1ed..6fdccd1 100644
--- a/docker/sra/src/main/resources/log4j2.xml
+++ b/docker/sra/src/main/resources/log4j2.xml
@@ -32,11 +32,23 @@ under the License.
<asyncLogger name="org.apache.syncope.client.lib" additivity="false" level="OFF">
<appender-ref ref="console"/>
</asyncLogger>
-
<asyncLogger name="org.apache.syncope.sra" additivity="false" level="INFO">
<appender-ref ref="console"/>
</asyncLogger>
-
+
+ <asyncLogger name="org.apache.cxf" additivity="false" level="ERROR">
+ <appender-ref ref="console"/>
+ </asyncLogger>
+
+ <asyncLogger name="org.springframework.cloud.gateway" additivity="false" level="INFO">
+ <appender-ref ref="console"/>
+ </asyncLogger>
+
+ <!-- Requires -Dreactor.netty.http.server.accessLogEnabled=true to work-->
+ <asyncLogger name="reactor.netty.http.server.AccessLog" additivity="false" level="INFO">
+ <appender-ref ref="console"/>
+ </asyncLogger>
+
<root level="INFO">
<appender-ref ref="console"/>
</root>
diff --git a/sra/src/main/resources/sra.properties b/docker/sra/src/main/resources/sra.properties
similarity index 96%
copy from sra/src/main/resources/sra.properties
copy to docker/sra/src/main/resources/sra.properties
index 6789cfd..77cb93e 100644
--- a/sra/src/main/resources/sra.properties
+++ b/docker/sra/src/main/resources/sra.properties
@@ -14,8 +14,6 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-sra.directory=${conf.directory}
-
anonymousUser=${anonymousUser}
anonymousKey=${anonymousKey}
diff --git a/docker/sra/src/main/resources/startup.sh b/docker/sra/src/main/resources/startup.sh
index 69093c3..df42979 100755
--- a/docker/sra/src/main/resources/startup.sh
+++ b/docker/sra/src/main/resources/startup.sh
@@ -19,4 +19,5 @@
export LOADER_PATH="/opt/syncope/conf,/opt/syncope/lib"
java -Dfile.encoding=UTF-8 -server -Xms1536m -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m \
- -XX:+DisableExplicitGC -Djava.security.egd=file:/dev/./urandom -jar /opt/syncope/lib/syncope-sra.jar
+ -XX:+DisableExplicitGC -Djava.security.egd=file:/dev/./urandom \
+ -Dreactor.netty.http.server.accessLogEnabled=true -jar /opt/syncope/lib/syncope-sra.jar
diff --git a/docker/src/main/resources/docker-compose/docker-compose-zookeeper.yml b/docker/src/main/resources/docker-compose/docker-compose-all.yml
similarity index 82%
rename from docker/src/main/resources/docker-compose/docker-compose-zookeeper.yml
rename to docker/src/main/resources/docker-compose/docker-compose-all.yml
index 2d216f6..1ed6be1 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-zookeeper.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-all.yml
@@ -15,7 +15,7 @@
# specific language governing permissions and limitations
# under the License.
-# Full deployment (Core, Console, Enduser) on PostgreSQL, with Keymaster on Zookeeper
+# Full deployment (Core, Console, Enduser, SRA) on PostgreSQL, with Keymaster on Zookeeper
# Zookeeper is configured without JAAS, hence empty KEYMASTER_USERNAME / KEYMASTER_PASSWORD
# are passed to other containers
@@ -38,6 +38,7 @@ services:
syncope:
depends_on:
- db
+ - keymaster
image: apache/syncope:${SYNCOPE_VERSION}
ports:
- "18080:8080"
@@ -58,6 +59,7 @@ services:
syncope-console:
depends_on:
- syncope
+ - keymaster
image: apache/syncope-console:${SYNCOPE_VERSION}
ports:
- "28080:8080"
@@ -71,6 +73,7 @@ services:
syncope-enduser:
depends_on:
- syncope
+ - keymaster
image: apache/syncope-enduser:${SYNCOPE_VERSION}
ports:
- "38080:8080"
@@ -80,3 +83,17 @@ services:
KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-}
KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-}
SERVICE_DISCOVERY_ADDRESS: http://syncope-enduser:8080/syncope-enduser/
+
+ syncope-sra:
+ depends_on:
+ - syncope
+ - keymaster
+ image: apache/syncope-sra:${SYNCOPE_VERSION}
+ ports:
+ - "48080:8080"
+ restart: always
+ environment:
+ KEYMASTER_ADDRESS: keymaster:2181
+ KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-}
+ KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-}
+ SERVICE_DISCOVERY_ADDRESS: http://syncope-sra:8080/
diff --git a/fit/console-reference/pom.xml b/fit/console-reference/pom.xml
index d5ee498..1971cc9 100644
--- a/fit/console-reference/pom.xml
+++ b/fit/console-reference/pom.xml
@@ -291,8 +291,7 @@ under the License.
-Dspring.profiles.active=embedded
-javaagent:${java.home}/lib/hotswap/hotswap-agent.jar=autoHotswap=true,disablePlugin=Spring,disablePlugin=Hibernate,disablePlugin=CxfJAXRS
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
- -XX:+CMSClassUnloadingEnabled
- -Xmx1024m -Xms512m</cargo.jvmargs>
+ -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -Xmx1024m -Xms512m</cargo.jvmargs>
</properties>
</configuration>
</configuration>
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index 039d1f74..0f6bf3b 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -1626,7 +1626,7 @@ under the License.
-Dspring.profiles.active=embedded
-javaagent:${java.home}/lib/hotswap/hotswap-agent.jar=autoHotswap=true,disablePlugin=Spring,disablePlugin=Hibernate
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
- -XX:+CMSClassUnloadingEnabled -XX:+UseG1GC -Xmx1024m -Xms512m</cargo.jvmargs>
+ -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -Xmx1024m -Xms512m</cargo.jvmargs>
</properties>
</configuration>
</configuration>
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GatewayRouteITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GatewayRouteITCase.java
index 5d4f8c4..4994066 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GatewayRouteITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GatewayRouteITCase.java
@@ -32,8 +32,8 @@ import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.GatewayRouteTO;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.lib.types.FilterFactory;
-import org.apache.syncope.common.lib.types.GatewayFilter;
-import org.apache.syncope.common.lib.types.GatewayPredicate;
+import org.apache.syncope.common.lib.types.GatewayRouteFilter;
+import org.apache.syncope.common.lib.types.GatewayRoutePredicate;
import org.apache.syncope.common.lib.types.GatewayRouteStatus;
import org.apache.syncope.common.lib.types.PredicateFactory;
import org.apache.syncope.common.rest.api.service.GatewayRouteService;
@@ -69,9 +69,9 @@ public class GatewayRouteITCase extends AbstractITCase {
GatewayRouteTO route = new GatewayRouteTO();
route.setName("just for test");
route.setTarget(URI.create("http://httpbin.org:80"));
- route.getPredicates().add(new GatewayPredicate.Builder().
+ route.getPredicates().add(new GatewayRoutePredicate.Builder().
factory(PredicateFactory.METHOD).args(HttpMethod.GET).build());
- route.getFilters().add(new GatewayFilter.Builder().
+ route.getFilters().add(new GatewayRouteFilter.Builder().
factory(FilterFactory.ADD_REQUEST_HEADER).args("X-Request-Foo, Bar").build());
route.setStatus(GatewayRouteStatus.DRAFT);
@@ -103,4 +103,48 @@ public class GatewayRouteITCase extends AbstractITCase {
int endCount = gatewayRouteService.list().size();
assertEquals(endCount, beforeCount);
}
+
+ @Test
+ public void exceptions() {
+ GatewayRouteTO route = new GatewayRouteTO();
+ try {
+ gatewayRouteService.create(route);
+ fail();
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+ }
+
+ route.setName("createException");
+ try {
+ gatewayRouteService.create(route);
+ fail();
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+ }
+
+ route.setTarget(URI.create("http://httpbin.org:80"));
+ Response response = gatewayRouteService.create(route);
+ assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+ try {
+ gatewayRouteService.create(route);
+ fail();
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.EntityExists, e.getType());
+ }
+
+ route.setKey(UUID.randomUUID().toString());
+ try {
+ gatewayRouteService.update(route);
+ fail();
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.NotFound, e.getType());
+ }
+ try {
+ gatewayRouteService.delete(route.getKey());
+ fail();
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.NotFound, e.getType());
+ }
+ }
}
diff --git a/fit/enduser-reference/pom.xml b/fit/enduser-reference/pom.xml
index 8cd5fe2..9bf0e73 100644
--- a/fit/enduser-reference/pom.xml
+++ b/fit/enduser-reference/pom.xml
@@ -282,8 +282,7 @@ under the License.
-Dspring.profiles.active=embedded
-javaagent:${java.home}/lib/hotswap/hotswap-agent.jar=autoHotswap=true,disablePlugin=Spring,disablePlugin=Hibernate,disablePlugin=CxfJAXRS
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
- -XX:+CMSClassUnloadingEnabled
- -Xmx1024m -Xms512m</cargo.jvmargs>
+ -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -Xmx1024m -Xms512m</cargo.jvmargs>
</properties>
</configuration>
</configuration>
diff --git a/pom.xml b/pom.xml
index 1de07d5..639d09a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -396,6 +396,7 @@ under the License.
<spring.version>5.1.7.RELEASE</spring.version>
<spring-security.version>5.1.5.RELEASE</spring-security.version>
<spring-boot.version>2.1.5.RELEASE</spring-boot.version>
+ <spring-cloud-gateway.version>2.1.1.RELEASE</spring-cloud-gateway.version>
<openjpa.version>3.1.0</openjpa.version>
<hikaricp.version>3.3.1</hikaricp.version>
@@ -1035,10 +1036,13 @@ under the License.
<dependency>
<groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-dependencies</artifactId>
- <version>Greenwich.SR1</version>
- <type>pom</type>
- <scope>import</scope>
+ <artifactId>spring-cloud-starter-gateway</artifactId>
+ <version>${spring-cloud-gateway.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
+ <version>${spring-cloud-gateway.version}</version>
</dependency>
<dependency>
@@ -1831,6 +1835,17 @@ under the License.
</exclusions>
</dependency>
<dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-x-discovery</artifactId>
+ <version>${curator.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.zookeeper</groupId>
+ <artifactId>zookeeper</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>${zookeeper.version}</version>
@@ -1848,6 +1863,24 @@ under the License.
<!-- TEST -->
<dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-test</artifactId>
+ <version>2.13.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-transports-http-netty-server</artifactId>
+ <version>${cxf.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-contract-wiremock</artifactId>
+ <version>${spring-cloud-gateway.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.61</version>
diff --git a/sra/pom.xml b/sra/pom.xml
index b9df9a8..5b0d595 100644
--- a/sra/pom.xml
+++ b/sra/pom.xml
@@ -43,18 +43,18 @@ under the License.
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-security</artifactId>
<exclusions>
<exclusion>
- <artifactId>spring-boot-starter-web</artifactId>
<groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
- </exclusions>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -77,13 +77,8 @@ under the License.
</dependency>
<dependency>
- <groupId>org.apache.syncope.ext.self-keymaster</groupId>
- <artifactId>syncope-ext-self-keymaster-client</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
<groupId>org.apache.syncope.common.keymaster</groupId>
- <artifactId>syncope-common-keymaster-client-zookeeper</artifactId>
+ <artifactId>syncope-common-keymaster-client-api</artifactId>
<version>${project.version}</version>
</dependency>
@@ -124,6 +119,33 @@ under the License.
</dependency>
<dependency>
+ <groupId>org.apache.syncope.common.keymaster</groupId>
+ <artifactId>syncope-common-keymaster-client-zookeeper</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-transports-http-netty-server</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-servlet_2.5_spec</artifactId>
+ </exclusion>
+ </exclusions>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-contract-wiremock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
@@ -138,18 +160,14 @@ under the License.
<build>
<plugins>
<plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <inherited>true</inherited>
<configuration>
- <mainClass>org.apache.syncope.sra.SyncopeSRAApplication</mainClass>
+ <systemPropertyVariables>
+ <reactor.netty.http.server.accessLogEnabled>true</reactor.netty.http.server.accessLogEnabled>
+ </systemPropertyVariables>
</configuration>
- <executions>
- <execution>
- <goals>
- <goal>repackage</goal>
- </goals>
- </execution>
- </executions>
</plugin>
</plugins>
@@ -158,7 +176,6 @@ under the License.
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
-
<resource>
<directory>${basedir}/../src/main/resources</directory>
<filtering>true</filtering>
@@ -167,10 +184,131 @@ under the License.
</includes>
</resource>
</resources>
+
+ <testResources>
+ <testResource>
+ <directory>${basedir}/src/test/resources</directory>
+ <filtering>true</filtering>
+ </testResource>
+ </testResources>
</build>
<profiles>
<profile>
+ <id>debug</id>
+
+ <properties>
+ <skipTests>true</skipTests>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.syncope.common.keymaster</groupId>
+ <artifactId>syncope-common-keymaster-client-zookeeper</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-test</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-transports-http-netty-server</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <defaultGoal>clean package io.fabric8:docker-maven-plugin:start spring-boot:run</defaultGoal>
+
+ <plugins>
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ <configuration>
+ <images>
+ <image>
+ <name>zookeeper:${zookeeper.version}</name>
+ <run>
+ <ports>
+ <port>2181:2181</port>
+ </ports>
+ <volumes>
+ <bind>
+ <volume>${project.build.testOutputDirectory}/zoo.cfg:/conf/zoo.cfg</volume>
+ <volume>${project.build.testOutputDirectory}/java.env:/conf/java.env</volume>
+ <volume>${project.build.testOutputDirectory}/server-jaas.conf:/conf/server-jaas.conf</volume>
+ <volume>${project.build.testOutputDirectory}/client-jaas.conf:/conf/client-jaas.conf</volume>
+ </bind>
+ </volumes>
+ </run>
+ </image>
+ </images>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>${basedir}/src/test/java</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>**/org/apache/syncope/sra/**Test.java</exclude>
+ <exclude>**/org/apache/syncope/sra/**Keymaster*.java</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>org.apache.syncope.sra.SyncopeSRAApplication</mainClass>
+ <systemPropertyVariables>
+ <reactor.netty.http.server.accessLogEnabled>true</reactor.netty.http.server.accessLogEnabled>
+ </systemPropertyVariables>
+ <jvmArguments>
+ -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
+ </jvmArguments>
+ </configuration>
+ </plugin>
+ </plugins>
+
+ <resources>
+ <resource>
+ <directory>${basedir}/src/test/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+
+ <testResources>
+ <testResource>
+ <directory>${basedir}/../common/keymaster/client-zookeeper/src/main/resources</directory>
+ <filtering>true</filtering>
+ </testResource>
+ </testResources>
+ </build>
+ </profile>
+
+ <profile>
<id>site</id>
<build>
diff --git a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAStartup.java b/sra/src/main/java/org/apache/syncope/sra/CustomGatewayFilterFactory.java
similarity index 54%
copy from sra/src/main/java/org/apache/syncope/sra/SyncopeSRAStartup.java
copy to sra/src/main/java/org/apache/syncope/sra/CustomGatewayFilterFactory.java
index 08ea447..7ac2cd8 100644
--- a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAStartup.java
+++ b/sra/src/main/java/org/apache/syncope/sra/CustomGatewayFilterFactory.java
@@ -18,14 +18,32 @@
*/
package org.apache.syncope.sra;
-import org.springframework.context.ApplicationListener;
-import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
-public class SyncopeSRAStartup extends SyncopeSRAStartStop
- implements ApplicationListener<ContextRefreshedEvent> {
+/**
+ * Base class for custom gateway filter factories.
+ */
+public abstract class CustomGatewayFilterFactory
+ extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.Config> {
- @Override
- public void onApplicationEvent(final ContextRefreshedEvent event) {
- serviceOps.register(getNetworkService());
+ public static class Config {
+
+ private String data;
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(final String data) {
+ this.data = data;
+ }
}
+
+ public CustomGatewayFilterFactory() {
+ super(CustomGatewayFilterFactory.Config.class);
+ }
+
+ @Override
+ public abstract GatewayFilter apply(Config config);
}
diff --git a/sra/src/main/java/org/apache/syncope/sra/CustomRoutePredicateFactory.java b/sra/src/main/java/org/apache/syncope/sra/CustomRoutePredicateFactory.java
new file mode 100644
index 0000000..22d9299
--- /dev/null
+++ b/sra/src/main/java/org/apache/syncope/sra/CustomRoutePredicateFactory.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.sra;
+
+import java.util.function.Predicate;
+import org.springframework.cloud.gateway.handler.AsyncPredicate;
+import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
+import org.springframework.web.server.ServerWebExchange;
+
+/**
+ * Base class for custom predicate factories.
+ */
+public abstract class CustomRoutePredicateFactory
+ extends AbstractRoutePredicateFactory<CustomRoutePredicateFactory.Config> {
+
+ public static class Config {
+
+ private String data;
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(final String data) {
+ this.data = data;
+ }
+ }
+
+ public CustomRoutePredicateFactory() {
+ super(CustomRoutePredicateFactory.Config.class);
+ }
+
+ @Override
+ public abstract AsyncPredicate<ServerWebExchange> applyAsync(Config config);
+
+ @Override
+ public Predicate<ServerWebExchange> apply(final Config config) {
+ throw new UnsupportedOperationException(getClass().getName() + " is only async.");
+ }
+}
diff --git a/sra/src/main/java/org/apache/syncope/sra/ManagementController.java b/sra/src/main/java/org/apache/syncope/sra/ManagementController.java
new file mode 100644
index 0000000..7d15556
--- /dev/null
+++ b/sra/src/main/java/org/apache/syncope/sra/ManagementController.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.sra;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.gateway.route.Route;
+import org.springframework.cloud.gateway.route.RouteDefinition;
+import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
+import org.springframework.cloud.gateway.route.RouteLocator;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
+
+@RestController
+@RequestMapping(path = "/management")
+public class ManagementController {
+
+ @Autowired
+ private RouteRefresher routeRefresher;
+
+ @Autowired
+ private RouteDefinitionLocator routeDefinitionLocator;
+
+ @Autowired
+ private RouteLocator routeLocator;
+
+ @PostMapping("/routes/refresh")
+ public Mono<Void> refresh() {
+ routeRefresher.refresh();
+ return Mono.empty();
+ }
+
+ @GetMapping("/routes")
+ public Mono<List<Map<String, Object>>> routes() {
+ Mono<Map<String, RouteDefinition>> routeDefs =
+ routeDefinitionLocator.getRouteDefinitions().collectMap(RouteDefinition::getId);
+ Mono<List<Route>> routes = routeLocator.getRoutes().collectList();
+ return Mono.zip(routeDefs, routes).map(tuple -> {
+ Map<String, RouteDefinition> defs = tuple.getT1();
+ List<Route> routeList = tuple.getT2();
+ List<Map<String, Object>> allRoutes = new ArrayList<>();
+
+ routeList.forEach(route -> {
+ Map<String, Object> r = new HashMap<>();
+ r.put("route_id", route.getId());
+ r.put("order", route.getOrder());
+
+ if (defs.containsKey(route.getId())) {
+ r.put("route_definition", defs.get(route.getId()));
+ } else {
+ Map<String, Object> obj = new HashMap<>();
+
+ obj.put("predicate", route.getPredicate().toString());
+
+ if (!route.getFilters().isEmpty()) {
+ obj.put("filters",
+ route.getFilters().stream().map(Object::toString).collect(Collectors.toList()));
+ }
+
+ if (!obj.isEmpty()) {
+ r.put("route_object", obj);
+ }
+ }
+ allRoutes.add(r);
+ });
+
+ return allRoutes;
+ });
+ }
+}
diff --git a/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java b/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java
new file mode 100644
index 0000000..709705a
--- /dev/null
+++ b/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java
@@ -0,0 +1,472 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.sra;
+
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.common.keymaster.client.api.ServiceOps;
+import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
+import org.apache.syncope.common.lib.to.GatewayRouteTO;
+import org.apache.syncope.common.lib.types.GatewayRouteFilter;
+import org.apache.syncope.common.lib.types.GatewayRoutePredicate;
+import org.apache.syncope.common.lib.types.GatewayRouteStatus;
+import org.apache.syncope.common.rest.api.service.GatewayRouteService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
+import org.springframework.cloud.gateway.filter.factory.AddRequestHeaderGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.AddRequestParameterGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.AddResponseHeaderGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.FallbackHeadersGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.HystrixGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.PrefixPathGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.PreserveHostHeaderGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.RedirectToGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.RemoveRequestHeaderGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.RemoveResponseHeaderGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.RequestHeaderToRequestUriGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.RequestSizeGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.RetryGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.RewriteResponseHeaderGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.SaveSessionGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.SecureHeadersGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.SetPathGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.SetRequestHeaderGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.SetResponseHeaderGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.SetStatusGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.StripPrefixGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
+import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
+import org.springframework.cloud.gateway.handler.AsyncPredicate;
+import org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory;
+import org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactory;
+import org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory;
+import org.springframework.cloud.gateway.handler.predicate.CookieRoutePredicateFactory;
+import org.springframework.cloud.gateway.handler.predicate.HeaderRoutePredicateFactory;
+import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFactory;
+import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory;
+import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory;
+import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory;
+import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory;
+import org.springframework.cloud.gateway.route.Route;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.Ordered;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+
+@Component
+public class RouteProvider {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RouteProvider.class);
+
+ @Autowired
+ private ServiceOps serviceOps;
+
+ @Autowired
+ private ConfigurableApplicationContext ctx;
+
+ @Value("${anonymousUser}")
+ private String anonymousUser;
+
+ @Value("${anonymousKey}")
+ private String anonymousKey;
+
+ @Value("${useGZIPCompression}")
+ private boolean useGZIPCompression;
+
+ private SyncopeClient client;
+
+ @SuppressWarnings("unchecked")
+ private GatewayFilter toFilter(final String routeId, final GatewayRouteFilter gwfilter)
+ throws ClassNotFoundException {
+
+ GatewayFilter filter;
+
+ switch (gwfilter.getFactory()) {
+ case ADD_REQUEST_HEADER:
+ String[] addRequestHeaderArgs = gwfilter.getArgs().split(",");
+ filter = ctx.getBean(AddRequestHeaderGatewayFilterFactory.class).
+ apply(c -> c.setName(addRequestHeaderArgs[0].trim()).
+ setValue(addRequestHeaderArgs[1].trim()));
+ break;
+
+ case ADD_REQUEST_PARAMETER:
+ String[] addRequestParameterArgs = gwfilter.getArgs().split(",");
+ filter = ctx.getBean(AddRequestParameterGatewayFilterFactory.class).
+ apply(c -> c.setName(addRequestParameterArgs[0].trim()).
+ setValue(addRequestParameterArgs[1].trim()));
+ break;
+
+ case ADD_RESPONSE_HEADER:
+ String[] addResponseHeaderArgs = gwfilter.getArgs().split(",");
+ filter = ctx.getBean(AddResponseHeaderGatewayFilterFactory.class).
+ apply(c -> c.setName(addResponseHeaderArgs[0].trim()).
+ setValue(addResponseHeaderArgs[1].trim()));
+ break;
+
+ case HYSTRIX:
+ String[] hystrixArgs = gwfilter.getArgs().split(",");
+ filter = ctx.getBean(HystrixGatewayFilterFactory.class).
+ apply(routeId, c -> {
+ if (StringUtils.isNotBlank(hystrixArgs[0])) {
+ c.setName(hystrixArgs[0].trim());
+ }
+ if (StringUtils.isNotBlank(hystrixArgs[1])) {
+ c.setFallbackUri(hystrixArgs[1].trim());
+ }
+ });
+ break;
+
+ case FALLBACK_HEADERS:
+ String[] fallbackHeadersArgs = gwfilter.getArgs().split(",");
+ filter = ctx.getBean(FallbackHeadersGatewayFilterFactory.class).
+ apply(c -> {
+ if (StringUtils.isNotBlank(fallbackHeadersArgs[0])) {
+ c.setCauseExceptionMessageHeaderName(fallbackHeadersArgs[0].trim());
+ }
+ if (StringUtils.isNotBlank(fallbackHeadersArgs[1])) {
+ c.setExecutionExceptionMessageHeaderName(fallbackHeadersArgs[1].trim());
+ }
+ if (StringUtils.isNotBlank(fallbackHeadersArgs[2])) {
+ c.setExecutionExceptionTypeHeaderName(fallbackHeadersArgs[2].trim());
+ }
+ if (StringUtils.isNotBlank(fallbackHeadersArgs[3])) {
+ c.setRootCauseExceptionTypeHeaderName(fallbackHeadersArgs[3].trim());
+ }
+ });
+ break;
+
+ case PREFIX_PATH:
+ filter = ctx.getBean(PrefixPathGatewayFilterFactory.class).
+ apply(c -> c.setPrefix(gwfilter.getArgs().trim()));
+ break;
+
+ case PRESERVE_HOST_HEADER:
+ filter = ctx.getBean(PreserveHostHeaderGatewayFilterFactory.class).apply();
+ break;
+
+ case REDIRECT:
+ String[] redirectArgs = gwfilter.getArgs().split(",");
+ filter = ctx.getBean(RedirectToGatewayFilterFactory.class).
+ apply(redirectArgs[0].trim(), redirectArgs[1].trim());
+ break;
+
+ case REMOVE_REQUEST_HEADER:
+ filter = ctx.getBean(RemoveRequestHeaderGatewayFilterFactory.class).
+ apply(c -> c.setName(gwfilter.getArgs().trim()));
+ break;
+
+ case REMOVE_RESPONSE_HEADER:
+ filter = ctx.getBean(RemoveResponseHeaderGatewayFilterFactory.class).
+ apply(c -> c.setName(gwfilter.getArgs().trim()));
+ break;
+
+ case REQUEST_RATE_LIMITER:
+ String[] requestRateLimiterArgs = gwfilter.getArgs().split(",");
+ filter = ctx.getBean(RequestRateLimiterGatewayFilterFactory.class).
+ apply(c -> {
+ if (StringUtils.isNotBlank(requestRateLimiterArgs[0])) {
+ c.setDenyEmptyKey(BooleanUtils.toBoolean(requestRateLimiterArgs[0].trim()));
+ }
+ if (StringUtils.isNotBlank(requestRateLimiterArgs[1])) {
+ c.setEmptyKeyStatus(requestRateLimiterArgs[1].trim());
+ }
+ if (StringUtils.isNotBlank(requestRateLimiterArgs[2])) {
+ c.setKeyResolver(ctx.getBean(requestRateLimiterArgs[2].trim(), KeyResolver.class));
+ }
+ if (StringUtils.isNotBlank(requestRateLimiterArgs[3])) {
+ c.setRateLimiter(ctx.getBean(requestRateLimiterArgs[3].trim(), RateLimiter.class));
+ }
+ if (StringUtils.isNotBlank(requestRateLimiterArgs[4])) {
+ c.setStatusCode(HttpStatus.valueOf(requestRateLimiterArgs[4].trim()));
+ }
+ });
+ break;
+
+ case REWRITE_PATH:
+ String[] rewritePathArgs = gwfilter.getArgs().split(",");
+ filter = ctx.getBean(RewritePathGatewayFilterFactory.class).
+ apply(c -> c.setRegexp(rewritePathArgs[0].trim()).
+ setReplacement(rewritePathArgs[1].trim()));
+ break;
+
+ case RETRY:
+ filter = ctx.getBean(RetryGatewayFilterFactory.class).
+ apply(c -> c.setRetries(Integer.valueOf(gwfilter.getArgs().trim())));
+ break;
+
+ case SECURE_HEADERS:
+ filter = ctx.getBean(SecureHeadersGatewayFilterFactory.class).apply(c -> {
+ });
+ break;
+
+ case SET_PATH:
+ filter = ctx.getBean(SetPathGatewayFilterFactory.class).
+ apply(c -> c.setTemplate(gwfilter.getArgs().trim()));
+ break;
+
+ case SET_REQUEST_HEADER:
+ String[] setRequestHeaderArgs = gwfilter.getArgs().split(",");
+ filter = ctx.getBean(SetRequestHeaderGatewayFilterFactory.class).
+ apply(c -> c.setName(setRequestHeaderArgs[0].trim()).
+ setValue(setRequestHeaderArgs[1].trim()));
+ break;
+
+ case SET_RESPONSE_HEADER:
+ String[] setResponseHeaderArgs = gwfilter.getArgs().split(",");
+ filter = ctx.getBean(SetResponseHeaderGatewayFilterFactory.class).
+ apply(c -> c.setName(setResponseHeaderArgs[0].trim()).
+ setValue(setResponseHeaderArgs[1].trim()));
+ break;
+
+ case REWRITE_RESPONSE_HEADER:
+ String[] rewriteResponseHeaderArgs = gwfilter.getArgs().split(",");
+ filter = ctx.getBean(RewriteResponseHeaderGatewayFilterFactory.class).
+ apply(c -> c.setReplacement(rewriteResponseHeaderArgs[2].trim()).
+ setRegexp(rewriteResponseHeaderArgs[1].trim()).
+ setName(rewriteResponseHeaderArgs[0].trim()));
+ break;
+
+ case SET_STATUS:
+ filter = ctx.getBean(SetStatusGatewayFilterFactory.class).
+ apply(c -> c.setStatus(gwfilter.getArgs().trim()));
+ break;
+
+ case SAVE_SESSION:
+ filter = ctx.getBean(SaveSessionGatewayFilterFactory.class).apply(c -> {
+ });
+ break;
+
+ case STRIP_PREFIX:
+ filter = ctx.getBean(StripPrefixGatewayFilterFactory.class).
+ apply(c -> c.setParts(Integer.valueOf(gwfilter.getArgs().trim())));
+ break;
+
+ case REQUEST_HEADER_TO_REQUEST_URI:
+ filter = ctx.getBean(RequestHeaderToRequestUriGatewayFilterFactory.class).
+ apply(c -> c.setName(gwfilter.getArgs().trim()));
+ break;
+
+ case SET_REQUEST_SIZE:
+ filter = ctx.getBean(RequestSizeGatewayFilterFactory.class).
+ apply(c -> c.setMaxSize(Long.valueOf(gwfilter.getArgs().trim())));
+ break;
+
+ case CUSTOM:
+ String[] customArgs = gwfilter.getArgs().split(";");
+ CustomGatewayFilterFactory factory;
+ if (ctx.getBeanFactory().containsSingleton(customArgs[0])) {
+ factory = (CustomGatewayFilterFactory) ctx.getBeanFactory().getSingleton(customArgs[0]);
+ } else {
+ factory = (CustomGatewayFilterFactory) ctx.getBeanFactory().
+ createBean(Class.forName(customArgs[0]), AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+ ctx.getBeanFactory().registerSingleton(customArgs[0], factory);
+ }
+ filter = factory.apply(c -> c.setData(customArgs[1]));
+ break;
+
+ default:
+ filter = null;
+ }
+
+ if (filter == null) {
+ throw new IllegalArgumentException("Could not translate " + gwfilter);
+ }
+
+ return filter instanceof Ordered ? filter : new OrderedGatewayFilter(filter, 0);
+ }
+
+ private AsyncPredicate<ServerWebExchange> toPredicate(final GatewayRoutePredicate gwpredicate, final boolean negate)
+ throws ClassNotFoundException {
+
+ AsyncPredicate<ServerWebExchange> predicate;
+ switch (gwpredicate.getFactory()) {
+ case AFTER:
+ predicate = ctx.getBean(AfterRoutePredicateFactory.class).
+ applyAsync(c -> c.setDatetime(ZonedDateTime.parse(gwpredicate.getArgs().trim())));
+ break;
+
+ case BEFORE:
+ predicate = ctx.getBean(BeforeRoutePredicateFactory.class).
+ applyAsync(c -> c.setDatetime(ZonedDateTime.parse(gwpredicate.getArgs().trim())));
+ break;
+
+ case BETWEEN:
+ String[] betweenArgs = gwpredicate.getArgs().split(",");
+ predicate = ctx.getBean(BetweenRoutePredicateFactory.class).
+ applyAsync(c -> c.setDatetime1(ZonedDateTime.parse(betweenArgs[0].trim())).
+ setDatetime2(ZonedDateTime.parse(betweenArgs[1].trim())));
+ break;
+
+ case COOKIE:
+ String[] cookieArgs = gwpredicate.getArgs().split(",");
+ predicate = ctx.getBean(CookieRoutePredicateFactory.class).
+ applyAsync(c -> c.setName(cookieArgs[0].trim()).
+ setRegexp(cookieArgs[1].trim()));
+ break;
+
+ case HEADER:
+ predicate = ctx.getBean(HeaderRoutePredicateFactory.class).
+ applyAsync(c -> c.setHeader(gwpredicate.getArgs().trim()));
+ break;
+
+ case HOST:
+ String[] hostArgs = gwpredicate.getArgs().split(",");
+ predicate = ctx.getBean(HostRoutePredicateFactory.class).
+ applyAsync(c -> c.setPatterns(Arrays.asList(hostArgs)));
+ break;
+
+ case METHOD:
+ predicate = ctx.getBean(MethodRoutePredicateFactory.class).
+ applyAsync(c -> c.setMethod(HttpMethod.resolve(gwpredicate.getArgs().trim())));
+ break;
+
+ case PATH:
+ String[] pathArgs = gwpredicate.getArgs().split(",");
+ predicate = ctx.getBean(PathRoutePredicateFactory.class).
+ applyAsync(c -> c.setPatterns(Arrays.asList(pathArgs)));
+ break;
+
+ case QUERY:
+ String[] queryArgs = gwpredicate.getArgs().split(",");
+ predicate = ctx.getBean(QueryRoutePredicateFactory.class).
+ applyAsync(c -> c.setParam(queryArgs[0].trim()).
+ setRegexp(queryArgs[1].trim()));
+ break;
+
+ case REMOTE_ADDR:
+ String[] remoteAddrArgs = gwpredicate.getArgs().split(",");
+ predicate = ctx.getBean(RemoteAddrRoutePredicateFactory.class).
+ applyAsync(c -> c.setSources(Arrays.asList(remoteAddrArgs)));
+ break;
+
+ case CUSTOM:
+ String[] customArgs = gwpredicate.getArgs().split(";");
+ CustomRoutePredicateFactory factory;
+ if (ctx.getBeanFactory().containsSingleton(customArgs[0])) {
+ factory = (CustomRoutePredicateFactory) ctx.getBeanFactory().getSingleton(customArgs[0]);
+ } else {
+ factory = (CustomRoutePredicateFactory) ctx.getBeanFactory().
+ createBean(Class.forName(customArgs[0]), AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+ ctx.getBeanFactory().registerSingleton(customArgs[0], factory);
+ }
+ predicate = factory.applyAsync(c -> c.setData(customArgs[1]));
+ break;
+
+ default:
+ predicate = null;
+ }
+
+ if (predicate == null) {
+ throw new IllegalArgumentException("Could not translate " + gwpredicate);
+ }
+
+ if (negate) {
+ predicate.negate();
+ }
+ return predicate;
+ }
+
+ private Route.AsyncBuilder toRoute(final GatewayRouteTO gwroute) {
+ Route.AsyncBuilder builder = new Route.AsyncBuilder().
+ id(gwroute.getKey()).order(gwroute.getOrder()).uri(gwroute.getTarget());
+
+ if (gwroute.getPredicates().isEmpty()) {
+ builder.predicate(exchange -> true);
+ } else {
+ gwroute.getPredicates().forEach(gwpredicate -> {
+ if (builder.getPredicate() == null) {
+ try {
+ builder.asyncPredicate(toPredicate(gwpredicate, gwpredicate.isNegate()));
+ } catch (Exception e) {
+ LOG.error("Could not translate {}, skipping", gwpredicate, e);
+ }
+ } else {
+ try {
+ switch (gwpredicate.getCond()) {
+ case OR:
+ builder.or(toPredicate(gwpredicate, gwpredicate.isNegate()));
+ break;
+
+ case AND:
+ default:
+ builder.and(toPredicate(gwpredicate, gwpredicate.isNegate()));
+ }
+ } catch (Exception e) {
+ LOG.error("Could not translate {}, skipping", gwpredicate, e);
+ }
+ }
+ });
+ }
+
+ if (!gwroute.getFilters().isEmpty()) {
+ builder.filters(gwroute.getFilters().stream().
+ map(gwfilter -> {
+ try {
+ return toFilter(gwroute.getKey(), gwfilter);
+ } catch (Exception e) {
+ LOG.error("Could not translate {}, skipping", gwfilter, e);
+ return null;
+ }
+ }).
+ filter(Objects::nonNull).
+ collect(Collectors.toList()));
+ }
+
+ return builder;
+ }
+
+ public List<Route.AsyncBuilder> fetch() {
+ synchronized (this) {
+ if (client == null) {
+ try {
+ client = new SyncopeClientFactoryBean().
+ setAddress(serviceOps.get(NetworkService.Type.CORE).getAddress()).
+ setUseCompression(useGZIPCompression).
+ create(new AnonymousAuthenticationHandler(anonymousUser, anonymousKey));
+ } catch (Exception e) {
+ LOG.error("Could not init SyncopeClient", e);
+ return Collections.emptyList();
+ }
+ }
+ }
+
+ return client.getService(GatewayRouteService.class).list().stream().
+ filter(gwroute -> gwroute.getStatus() == GatewayRouteStatus.PUBLISHED).
+ map(gwroute -> toRoute(gwroute)).
+ collect(Collectors.toList());
+ }
+}
diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/init/IdMImplementationTypeLoader.java b/sra/src/main/java/org/apache/syncope/sra/RouteRefresher.java
similarity index 61%
copy from core/idm/logic/src/main/java/org/apache/syncope/core/logic/init/IdMImplementationTypeLoader.java
copy to sra/src/main/java/org/apache/syncope/sra/RouteRefresher.java
index b3a039c..8d19965 100644
--- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/init/IdMImplementationTypeLoader.java
+++ b/sra/src/main/java/org/apache/syncope/sra/RouteRefresher.java
@@ -16,23 +16,24 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.logic.init;
+package org.apache.syncope.sra;
-import org.apache.syncope.common.lib.types.IdMImplementationType;
-import org.apache.syncope.common.lib.types.ImplementationTypesHolder;
-import org.apache.syncope.core.persistence.api.SyncopeCoreLoader;
+import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
@Component
-public class IdMImplementationTypeLoader implements SyncopeCoreLoader {
+public class RouteRefresher implements ApplicationEventPublisherAware {
+
+ private ApplicationEventPublisher publisher;
@Override
- public int getOrder() {
- return Integer.MIN_VALUE;
+ public void setApplicationEventPublisher(final ApplicationEventPublisher publisher) {
+ this.publisher = publisher;
}
- @Override
- public void load() {
- ImplementationTypesHolder.getInstance().putAll(IdMImplementationType.values());
+ public void refresh() {
+ publisher.publishEvent(new RefreshRoutesEvent(this));
}
}
diff --git a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAApplication.java b/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAApplication.java
index 09c620d..c0bda58 100644
--- a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAApplication.java
+++ b/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAApplication.java
@@ -18,21 +18,66 @@
*/
package org.apache.syncope.sra;
+import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
+import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.env.Environment;
+import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
+import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
+import org.springframework.web.util.pattern.PathPatternParser;
+import reactor.core.publisher.Flux;
+@PropertySource("classpath:sra.properties")
+@PropertySource(value = "file:${conf.directory}/sra.properties", ignoreResourceNotFound = true)
+@EnableWebFluxSecurity
@SpringBootApplication
-public class SyncopeSRAApplication {
+public class SyncopeSRAApplication implements EnvironmentAware {
public static void main(final String[] args) {
SpringApplication.run(SyncopeSRAApplication.class, args);
}
+ @Autowired
+ private RouteProvider provider;
+
+ private Environment env;
+
+ @Override
+ public void setEnvironment(final Environment env) {
+ this.env = env;
+ }
+
@Bean
public RouteLocator routes(final RouteLocatorBuilder builder) {
- return builder.routes().build();
+ return () -> Flux.fromIterable(provider.fetch()).map(routeBuilder -> routeBuilder.build());
+ }
+
+ @Bean
+ public SecurityWebFilterChain springSecurityFilterChain(final ServerHttpSecurity http) {
+ http.csrf().disable().securityMatcher(
+ new PathPatternParserServerWebExchangeMatcher(new PathPatternParser().parse("/management/**"))).
+ authorizeExchange().anyExchange().hasRole(IdRepoEntitlement.ANONYMOUS).and().httpBasic();
+ return http.build();
+ }
+
+ @Bean
+ public MapReactiveUserDetailsService userDetailsService() {
+ UserDetails user = User.builder().
+ username(env.getProperty("anonymousUser")).
+ password("{noop}" + env.getProperty("anonymousKey")).
+ roles(IdRepoEntitlement.ANONYMOUS).
+ build();
+ return new MapReactiveUserDetailsService(user);
}
}
diff --git a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAShutdown.java b/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAShutdown.java
index d335836..4ed772c 100644
--- a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAShutdown.java
+++ b/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAShutdown.java
@@ -20,7 +20,9 @@ package org.apache.syncope.sra;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
+import org.springframework.stereotype.Component;
+@Component
public class SyncopeSRAShutdown extends SyncopeSRAStartStop
implements ApplicationListener<ContextClosedEvent> {
diff --git a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAStartup.java b/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAStartup.java
index 08ea447..cbc7b59 100644
--- a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAStartup.java
+++ b/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAStartup.java
@@ -20,7 +20,9 @@ package org.apache.syncope.sra;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.stereotype.Component;
+@Component
public class SyncopeSRAStartup extends SyncopeSRAStartStop
implements ApplicationListener<ContextRefreshedEvent> {
diff --git a/sra/src/main/resources/application.properties b/sra/src/main/resources/application.properties
index 98bc964..b50f4f9 100644
--- a/sra/src/main/resources/application.properties
+++ b/sra/src/main/resources/application.properties
@@ -20,12 +20,8 @@ spring.main.banner-mode=log
server.port=8080
-management.endpoint.gateway.enabled=true
-management.endpoints.web.exposure.include=gateway
-
spring.cloud.gateway.metrics.enabled=true
management.endpoint.metrics.enabled=true
-management.endpoints.web.exposure.include=*
management.endpoint.prometheus.enabled=true
management.metrics.export.prometheus.enabled=true
diff --git a/sra/src/main/resources/log4j2.xml b/sra/src/main/resources/log4j2.xml
index d7211f5..3e0fa0c 100644
--- a/sra/src/main/resources/log4j2.xml
+++ b/sra/src/main/resources/log4j2.xml
@@ -52,11 +52,18 @@ under the License.
<asyncLogger name="org.apache.syncope.client.lib" additivity="false" level="OFF">
<appender-ref ref="main"/>
</asyncLogger>
-
<asyncLogger name="org.apache.syncope.sra" additivity="false" level="INFO">
<appender-ref ref="main"/>
</asyncLogger>
+ <asyncLogger name="org.apache.cxf" additivity="false" level="ERROR">
+ <appender-ref ref="main"/>
+ </asyncLogger>
+
+ <asyncLogger name="org.springframework.cloud.gateway" additivity="false" level="INFO">
+ <appender-ref ref="main"/>
+ </asyncLogger>
+
<!-- Requires -Dreactor.netty.http.server.accessLogEnabled=true to work-->
<asyncLogger name="reactor.netty.http.server.AccessLog" additivity="false" level="INFO">
<appender-ref ref="access"/>
diff --git a/sra/src/main/resources/sra.properties b/sra/src/main/resources/sra.properties
index 6789cfd..77cb93e 100644
--- a/sra/src/main/resources/sra.properties
+++ b/sra/src/main/resources/sra.properties
@@ -14,8 +14,6 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-sra.directory=${conf.directory}
-
anonymousUser=${anonymousUser}
anonymousKey=${anonymousKey}
diff --git a/sra/src/test/java/org/apache/syncope/sra/BodyPropertyAddingGatewayFilterFactory.java b/sra/src/test/java/org/apache/syncope/sra/BodyPropertyAddingGatewayFilterFactory.java
new file mode 100644
index 0000000..aec3dcc
--- /dev/null
+++ b/sra/src/test/java/org/apache/syncope/sra/BodyPropertyAddingGatewayFilterFactory.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.sra;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.zookeeper.common.IOUtils;
+import org.reactivestreams.Publisher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
+import org.springframework.core.Ordered;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DataBufferFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseCookie;
+import org.springframework.http.client.reactive.ClientHttpResponse;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+/**
+ * Inspired by {@link org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory}.
+ */
+public class BodyPropertyAddingGatewayFilterFactory extends CustomGatewayFilterFactory {
+
+ private static final Logger LOG = LoggerFactory.getLogger(BodyPropertyAddingGatewayFilterFactory.class);
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ private static boolean isCompressed(final byte[] bytes) {
+ if ((bytes == null) || (bytes.length < 2)) {
+ return false;
+ } else {
+ return ((bytes[0] == (byte) (GZIPInputStream.GZIP_MAGIC))
+ && (bytes[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8)));
+ }
+ }
+
+ @Override
+ public GatewayFilter apply(final Config config) {
+ return new ModifyResponseGatewayFilter(config);
+ }
+
+ public class ModifyResponseGatewayFilter implements GatewayFilter, Ordered {
+
+ private final Config config;
+
+ public ModifyResponseGatewayFilter(final Config config) {
+ this.config = config;
+ }
+
+ @Override
+ public Mono<Void> filter(final ServerWebExchange exchange, final GatewayFilterChain chain) {
+ ServerHttpResponse originalResponse = exchange.getResponse();
+
+ DataBufferFactory bufferFactory = originalResponse.bufferFactory();
+ ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(originalResponse) {
+
+ @Override
+ public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
+ if (body instanceof Flux) {
+ Flux<? extends DataBuffer> flux = (Flux<? extends DataBuffer>) body;
+
+ return super.writeWith(flux.buffer().map(dataBuffers -> {
+ ByteArrayOutputStream payload = new ByteArrayOutputStream();
+ dataBuffers.forEach(buffer -> {
+ byte[] array = new byte[buffer.readableByteCount()];
+ buffer.read(array);
+ try {
+ payload.write(array);
+ } catch (IOException e) {
+ LOG.error("While reading original body content", e);
+ }
+ });
+
+ byte[] input = payload.toByteArray();
+
+ InputStream is = null;
+ boolean compressed = false;
+ byte[] output;
+ try {
+ if (isCompressed(input)) {
+ compressed = true;
+ is = new GZIPInputStream(new ByteArrayInputStream(input));
+ } else {
+ is = new ByteArrayInputStream(input);
+ }
+
+ ObjectNode content = (ObjectNode) MAPPER.readTree(is);
+ String[] kv = config.getData().split("=");
+ content.put(kv[0], kv[1]);
+
+ output = MAPPER.writeValueAsBytes(content);
+ } catch (IOException e) {
+ LOG.error("While (de)serializing as JSON", e);
+ output = ArrayUtils.clone(input);
+ } finally {
+ IOUtils.closeStream(is);
+ }
+
+ if (compressed) {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream(output.length);
+ GZIPOutputStream gzipos = new GZIPOutputStream(baos)) {
+
+ gzipos.write(output);
+ gzipos.close();
+ output = baos.toByteArray();
+ } catch (IOException e) {
+ LOG.error("While GZIP-encoding output", e);
+ }
+ }
+
+ return bufferFactory.wrap(output);
+ }));
+ }
+
+ return super.writeWith(body);
+ }
+ };
+
+ return chain.filter(exchange.mutate().response(responseDecorator).build());
+ }
+
+ @Override
+ public int getOrder() {
+ return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
+ }
+ }
+
+ public class ResponseAdapter implements ClientHttpResponse {
+
+ private final Flux<DataBuffer> flux;
+
+ private final HttpHeaders headers;
+
+ @SuppressWarnings("unchecked")
+ public ResponseAdapter(final Publisher<? extends DataBuffer> body, final HttpHeaders headers) {
+ this.headers = headers;
+ if (body instanceof Flux) {
+ flux = (Flux) body;
+ } else {
+ flux = ((Mono) body).flux();
+ }
+ }
+
+ @Override
+ public Flux<DataBuffer> getBody() {
+ return flux;
+ }
+
+ @Override
+ public HttpHeaders getHeaders() {
+ return headers;
+ }
+
+ @Override
+ public HttpStatus getStatusCode() {
+ return null;
+ }
+
+ @Override
+ public int getRawStatusCode() {
+ return 0;
+ }
+
+ @Override
+ public MultiValueMap<String, ResponseCookie> getCookies() {
+ return null;
+ }
+ }
+}
diff --git a/sra/src/test/java/org/apache/syncope/sra/BodyPropertyMatchingRoutePredicateFactory.java b/sra/src/test/java/org/apache/syncope/sra/BodyPropertyMatchingRoutePredicateFactory.java
new file mode 100644
index 0000000..bff3903
--- /dev/null
+++ b/sra/src/test/java/org/apache/syncope/sra/BodyPropertyMatchingRoutePredicateFactory.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.sra;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import java.util.List;
+import org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter;
+import org.springframework.cloud.gateway.handler.AsyncPredicate;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DataBufferUtils;
+import org.springframework.http.codec.HttpMessageReader;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
+import org.springframework.web.reactive.function.server.HandlerStrategies;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+/**
+ * Inspired by {@link org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory}.
+ */
+public class BodyPropertyMatchingRoutePredicateFactory extends CustomRoutePredicateFactory {
+
+ private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";
+
+ private static final List<HttpMessageReader<?>> MESSAGE_READERS =
+ HandlerStrategies.withDefaults().messageReaders();
+
+ @Override
+ public AsyncPredicate<ServerWebExchange> applyAsync(final Config config) {
+ return exchange -> {
+ JsonNode cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);
+ if (cachedBody == null) {
+ // Join all the DataBuffers so we have a single DataBuffer for the body
+ return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
+ // Update the retain counts so we can read the body twice, once to parse into an object
+ // that we can test the predicate against and a second time when the HTTP client sends
+ // the request downstream
+ // Note: if we end up reading the body twice we will run into a problem, but as of right
+ // now there is no good use case for doing this
+ DataBufferUtils.retain(dataBuffer);
+ // Make a slice for each read so each read has its own read/write indexes
+ Flux<DataBuffer> cachedFlux = Flux.defer(() -> Flux.just(
+ dataBuffer.slice(0, dataBuffer.readableByteCount())));
+
+ ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
+
+ @Override
+ public Flux<DataBuffer> getBody() {
+ return cachedFlux;
+ }
+ };
+ return ServerRequest.create(exchange.mutate().request(mutatedRequest).build(), MESSAGE_READERS).
+ bodyToMono(JsonNode.class).doOnNext(value -> {
+ exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, value);
+ exchange.getAttributes().put(AdaptCachedBodyGlobalFilter.CACHED_REQUEST_BODY_KEY, cachedFlux);
+ }).map(objectValue -> objectValue.has(config.getData()));
+ });
+ } else {
+ return Mono.just(cachedBody.has(config.getData()));
+ }
+ };
+ }
+}
diff --git a/sra/src/test/java/org/apache/syncope/sra/SyncopeSRATest.java b/sra/src/test/java/org/apache/syncope/sra/SyncopeSRATest.java
new file mode 100644
index 0000000..3b16c64
--- /dev/null
+++ b/sra/src/test/java/org/apache/syncope/sra/SyncopeSRATest.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.sra;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.post;
+import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.net.URI;
+import org.apache.syncope.common.lib.to.GatewayRouteTO;
+import org.apache.syncope.common.lib.types.FilterFactory;
+import org.apache.syncope.common.lib.types.GatewayRouteFilter;
+import org.apache.syncope.common.lib.types.GatewayRoutePredicate;
+import org.apache.syncope.common.lib.types.GatewayRouteStatus;
+import org.apache.syncope.common.lib.types.PredicateCond;
+import org.apache.syncope.common.lib.types.PredicateFactory;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import org.springframework.web.reactive.function.BodyInserters;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@AutoConfigureWireMock(port = 0)
+public class SyncopeSRATest {
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @Autowired
+ private RouteRefresher routeRefresher;
+
+ @Value("${wiremock.server.port}")
+ private int wiremockPort;
+
+ @BeforeEach
+ public void clearRoutes() {
+ SyncopeSRATestCoreStartup.ROUTES.clear();
+ }
+
+ @Test
+ public void root() {
+ webClient.get().exchange().expectStatus().isNotFound();
+ }
+
+ @Test
+ public void getAddResponseHeader() {
+ // 1. no mapping for URL
+ webClient.get().uri("/getAddResponseHeader").exchange().expectStatus().isNotFound();
+
+ // 2. stub for proxied URL
+ stubFor(get(urlEqualTo("/getAddResponseHeader")).willReturn(aResponse()));
+
+ // 3. create route configuration
+ GatewayRouteTO routeTO = new GatewayRouteTO();
+ routeTO.setKey("getAddResponseHeader");
+ routeTO.setStatus(GatewayRouteStatus.PUBLISHED);
+ routeTO.setTarget(URI.create("http://localhost:" + wiremockPort));
+ routeTO.getPredicates().add(new GatewayRoutePredicate.Builder().
+ factory(PredicateFactory.METHOD).args("GET").build());
+ routeTO.getPredicates().add(new GatewayRoutePredicate.Builder().
+ factory(PredicateFactory.PATH).args("/getAddResponseHeader").cond(PredicateCond.AND).build());
+ routeTO.getFilters().add(new GatewayRouteFilter.Builder().
+ factory(FilterFactory.ADD_RESPONSE_HEADER).args("Hello,World").build());
+
+ SyncopeSRATestCoreStartup.ROUTES.put(routeTO.getKey(), routeTO);
+
+ routeRefresher.refresh();
+
+ // 4. now mapping works for URL
+ webClient.get().uri("/getAddResponseHeader").exchange().
+ expectStatus().isOk().
+ expectHeader().valueEquals("Hello", "World");
+
+ // 5. update route configuration
+ routeTO.getFilters().clear();
+ routeTO.getFilters().add(new GatewayRouteFilter.Builder().
+ factory(FilterFactory.ADD_RESPONSE_HEADER).args("Hello,WorldZ").build());
+
+ routeRefresher.refresh();
+
+ // 6. mapping for URL is updated too
+ webClient.get().uri("/getAddResponseHeader").exchange().
+ expectStatus().isOk().
+ expectHeader().valueEquals("Hello", "WorldZ");
+
+ // 7. update route configuration again
+ routeTO.getFilters().clear();
+
+ routeRefresher.refresh();
+
+ // 8. mapping for URL is updated again
+ webClient.get().uri("/getAddResponseHeader").exchange().
+ expectStatus().isOk().
+ expectHeader().doesNotExist("Hello");
+ }
+
+ @Test
+ public void hystrix() {
+ webClient.get().uri("/fallback").exchange().
+ expectStatus().isOk().
+ expectBody().
+ consumeWith(response -> assertThat(response.getResponseBody()).isEqualTo("fallback".getBytes()));
+
+ stubFor(get(urlEqualTo("/delay/3")).
+ willReturn(aResponse().
+ withBody("no fallback").
+ withFixedDelay(3000)));
+
+ GatewayRouteTO routeTO = new GatewayRouteTO();
+ routeTO.setKey("hystrix");
+ routeTO.setStatus(GatewayRouteStatus.PUBLISHED);
+ routeTO.setTarget(URI.create("http://localhost:" + wiremockPort));
+ routeTO.getPredicates().add(new GatewayRoutePredicate.Builder().
+ factory(PredicateFactory.HOST).args("*.hystrix.com").build());
+ routeTO.getFilters().add(new GatewayRouteFilter.Builder().
+ factory(FilterFactory.HYSTRIX).args("fallbackcmd,forward:/fallback").build());
+
+ SyncopeSRATestCoreStartup.ROUTES.put(routeTO.getKey(), routeTO);
+
+ routeRefresher.refresh();
+
+ webClient.get().uri("/delay/3").
+ header(HttpHeaders.HOST, "www.hystrix.com").
+ exchange().
+ expectStatus().isOk().
+ expectBody().
+ consumeWith(response -> assertThat(response.getResponseBody()).isEqualTo("fallback".getBytes()));
+ }
+
+ @Test
+ public void custom() {
+ stubFor(post(urlEqualTo("/custom")).
+ willReturn(aResponse().
+ withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).
+ withBody("{\"data\": \"data\"}")));
+
+ GatewayRouteTO routeTO = new GatewayRouteTO();
+ routeTO.setKey("custom");
+ routeTO.setStatus(GatewayRouteStatus.PUBLISHED);
+ routeTO.setTarget(URI.create("http://localhost:" + wiremockPort));
+ routeTO.getPredicates().add(new GatewayRoutePredicate.Builder().
+ factory(PredicateFactory.CUSTOM).
+ args(BodyPropertyMatchingRoutePredicateFactory.class.getName() + ";cool").build());
+ routeTO.getFilters().add(new GatewayRouteFilter.Builder().
+ factory(FilterFactory.ADD_RESPONSE_HEADER).args("Custom,matched").build());
+ routeTO.getFilters().add(new GatewayRouteFilter.Builder().
+ factory(FilterFactory.CUSTOM).
+ args(BodyPropertyAddingGatewayFilterFactory.class.getName() + ";customized=true").build());
+
+ SyncopeSRATestCoreStartup.ROUTES.put(routeTO.getKey(), routeTO);
+
+ routeRefresher.refresh();
+
+ webClient.post().uri("/custom").
+ body(BodyInserters.fromObject(MAPPER.createObjectNode().put("other", true))).
+ exchange().
+ expectStatus().isNotFound();
+
+ webClient.post().uri("/custom").
+ body(BodyInserters.fromObject(MAPPER.createObjectNode().put("cool", true))).
+ exchange().
+ expectStatus().isOk().
+ expectHeader().valueEquals("Custom", "matched").
+ expectBody().
+ consumeWith(response -> {
+ try {
+ JsonNode body = MAPPER.readTree(response.getResponseBody());
+ assertTrue(body.has("customized"));
+ assertTrue(body.get("customized").asBoolean());
+ } catch (IOException e) {
+ fail(e.getMessage(), e);
+ }
+ });
+ }
+}
diff --git a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAShutdown.java b/sra/src/test/java/org/apache/syncope/sra/SyncopeSRATestController.java
similarity index 69%
copy from sra/src/main/java/org/apache/syncope/sra/SyncopeSRAShutdown.java
copy to sra/src/test/java/org/apache/syncope/sra/SyncopeSRATestController.java
index d335836..c56490f 100644
--- a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAShutdown.java
+++ b/sra/src/test/java/org/apache/syncope/sra/SyncopeSRATestController.java
@@ -18,14 +18,15 @@
*/
package org.apache.syncope.sra;
-import org.springframework.context.ApplicationListener;
-import org.springframework.context.event.ContextClosedEvent;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
-public class SyncopeSRAShutdown extends SyncopeSRAStartStop
- implements ApplicationListener<ContextClosedEvent> {
+@RestController
+public class SyncopeSRATestController {
- @Override
- public void onApplicationEvent(final ContextClosedEvent event) {
- serviceOps.unregister(getNetworkService());
+ @RequestMapping("/fallback")
+ public Mono<String> fallback() {
+ return Mono.just("fallback");
}
}
diff --git a/sra/src/test/java/org/apache/syncope/sra/SyncopeSRATestCoreStartup.java b/sra/src/test/java/org/apache/syncope/sra/SyncopeSRATestCoreStartup.java
new file mode 100644
index 0000000..9a12b66
--- /dev/null
+++ b/sra/src/test/java/org/apache/syncope/sra/SyncopeSRATestCoreStartup.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.sra;
+
+import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
+import org.apache.syncope.common.lib.to.GatewayRouteTO;
+import org.apache.syncope.common.rest.api.service.GatewayRouteService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.core.Ordered;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SyncopeSRATestCoreStartup extends SyncopeSRAStartStop
+ implements ApplicationListener<ContextRefreshedEvent>, Ordered {
+
+ public static final String ADDRESS = "http://localhost:9080/syncope/rest";
+
+ public static final Map<String, GatewayRouteTO> ROUTES = new ConcurrentHashMap<>();
+
+ @Autowired
+ private RouteRefresher routeRefresher;
+
+ @Override
+ public int getOrder() {
+ return Ordered.LOWEST_PRECEDENCE;
+ }
+
+ @Override
+ public void onApplicationEvent(final ContextRefreshedEvent event) {
+ // 1. start (mocked) Core as embedded CXF
+ JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+ sf.setAddress(ADDRESS);
+ sf.setResourceClasses(GatewayRouteService.class);
+ sf.setResourceProvider(
+ GatewayRouteService.class,
+ new SingletonResourceProvider(new StubGatewayRouteService(), true));
+ sf.setProviders(Collections.singletonList(new JacksonJsonProvider()));
+ sf.create();
+
+ // 2. register Core in Keymaster
+ NetworkService core = new NetworkService();
+ core.setType(NetworkService.Type.CORE);
+ core.setAddress(SyncopeSRATestCoreStartup.ADDRESS);
+ serviceOps.register(core);
+ }
+
+ public class StubGatewayRouteService implements GatewayRouteService {
+
+ @Override
+ public List<GatewayRouteTO> list() {
+ return ROUTES.values().stream().
+ sorted(Comparator.comparing(GatewayRouteTO::getKey)).
+ collect(Collectors.toList());
+ }
+
+ @Override
+ public Response create(final GatewayRouteTO routeTO) {
+ ROUTES.putIfAbsent(routeTO.getKey(), routeTO);
+ return Response.noContent().build();
+ }
+
+ @Override
+ public GatewayRouteTO read(final String key) {
+ GatewayRouteTO route = ROUTES.get(key);
+ if (route == null) {
+ throw new NotFoundException();
+ }
+ return route;
+ }
+
+ @Override
+ public void update(final GatewayRouteTO routeTO) {
+ read(routeTO.getKey());
+ ROUTES.put(routeTO.getKey(), routeTO);
+ }
+
+ @Override
+ public void delete(final String key) {
+ ROUTES.remove(key);
+ }
+
+ @Override
+ public void pushToSRA() {
+ routeRefresher.refresh();
+ }
+ }
+}
diff --git a/sra/src/test/java/org/apache/syncope/sra/SyncopeSRATestKeymasterStartup.java b/sra/src/test/java/org/apache/syncope/sra/SyncopeSRATestKeymasterStartup.java
new file mode 100644
index 0000000..c5649f6
--- /dev/null
+++ b/sra/src/test/java/org/apache/syncope/sra/SyncopeSRATestKeymasterStartup.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.sra;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import org.apache.curator.test.InstanceSpec;
+import org.apache.curator.test.TestingServer;
+import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.core.Ordered;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SyncopeSRATestKeymasterStartup extends SyncopeSRAStartStop
+ implements ApplicationListener<ContextRefreshedEvent>, Ordered {
+
+ @Override
+ public int getOrder() {
+ return Ordered.HIGHEST_PRECEDENCE;
+ }
+
+ @Override
+ public void onApplicationEvent(final ContextRefreshedEvent event) {
+ // 1. start Zookeeper for Keymaster
+ AtomicReference<String> username = new AtomicReference<>();
+ AtomicReference<String> password = new AtomicReference<>();
+ try (InputStream propStream = getClass().getResourceAsStream("/keymaster.properties")) {
+ Properties props = new Properties();
+ props.load(propStream);
+
+ username.set(props.getProperty("keymaster.username"));
+ password.set(props.getProperty("keymaster.password"));
+ } catch (Exception e) {
+ throw new IllegalStateException("Could not load /keymaster.properties", e);
+ }
+
+ Configuration.setConfiguration(new Configuration() {
+
+ private final AppConfigurationEntry[] entries = {
+ new AppConfigurationEntry(
+ "org.apache.zookeeper.server.auth.DigestLoginModule",
+ AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+ Map.of(
+ "user_" + username.get(), password.get()
+ ))
+ };
+
+ @Override
+ public AppConfigurationEntry[] getAppConfigurationEntry(final String name) {
+ return entries;
+ }
+ });
+
+ Map<String, Object> customProperties = new HashMap<>();
+ customProperties.put("authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider");
+ InstanceSpec spec = new InstanceSpec(null, 2181, -1, -1, true, 1, -1, -1, customProperties);
+ try {
+ new TestingServer(spec, true).start();
+ } catch (Exception e) {
+ throw new IllegalStateException("Could not start Zookeeper", e);
+ }
+
+ // 2. register Core in Keymaster
+ NetworkService core = new NetworkService();
+ core.setType(NetworkService.Type.CORE);
+ core.setAddress(SyncopeSRATestCoreStartup.ADDRESS);
+ serviceOps.register(core);
+ }
+}
diff --git a/sra/src/main/resources/keymaster.properties b/sra/src/test/resources/keymaster.properties
similarity index 93%
rename from sra/src/main/resources/keymaster.properties
rename to sra/src/test/resources/keymaster.properties
index 033fe3b..f374d8c 100644
--- a/sra/src/main/resources/keymaster.properties
+++ b/sra/src/test/resources/keymaster.properties
@@ -14,6 +14,6 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
+keymaster.address=127.0.0.1:2181
keymaster.username=${anonymousUser}
keymaster.password=${anonymousKey}