You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by rm...@apache.org on 2018/08/28 19:20:34 UTC

[2/3] metron git commit: METRON-1665 Move hosting of Alerts and Config UIs from Nodejs to Spring Boot (simonellistonball via merrimanr) closes apache/metron#1111

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-rest/README.md
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/README.md b/metron-interface/metron-rest/README.md
index 44594f7..42c3d0b 100644
--- a/metron-interface/metron-rest/README.md
+++ b/metron-interface/metron-rest/README.md
@@ -25,7 +25,6 @@ This module provides a RESTful API for interacting with Metron.
 * A running real-time store, either Elasticsearch or Solr depending on which one is enabled
 * Java 8 installed
 * Storm CLI and Metron topology scripts (start_parser_topology.sh, start_enrichment_topology.sh, start_elasticsearch_topology.sh) installed
-* A relational database
 
 ## Installation
 
@@ -66,10 +65,6 @@ No optional parameter has a default.
 
 | Environment Variable                  | Description
 | ------------------------------------- | -----------
-| METRON_JDBC_DRIVER                    | JDBC driver class
-| METRON_JDBC_URL                       | JDBC url
-| METRON_JDBC_USERNAME                  | JDBC username
-| METRON_JDBC_PLATFORM                  | JDBC platform (one of h2, mysql, postgres, oracle
 | ZOOKEEPER                             | Zookeeper quorum (ex. node1:2181,node2:2181)
 | BROKERLIST                            | Kafka Broker list (ex. node1:6667,node2:6667)
 | HDFS_URL                              | HDFS url or `fs.defaultFS` Hadoop setting (ex. hdfs://node1:8020)
@@ -80,7 +75,6 @@ No optional parameter has a default.
 | METRON_LOG_DIR                        | Directory where the log file is written                           | Optional | /var/log/metron/
 | METRON_PID_FILE                       | File where the pid is written                                     | Optional | /var/run/metron/
 | METRON_REST_PORT                      | REST application port                                             | Optional | 8082
-| METRON_JDBC_CLIENT_PATH               | Path to JDBC client jar                                           | Optional | H2 is bundled
 | METRON_TEMP_GROK_PATH                 | Temporary directory used to test grok statements                  | Optional | ./patterns/temp
 | METRON_DEFAULT_GROK_PATH              | Defaults HDFS directory used to store grok statements             | Optional | /apps/metron/patterns
 | SECURITY_ENABLED                      | Enables Kerberos support                                          | Optional | false
@@ -96,27 +90,6 @@ No optional parameter has a default.
 
 These are set in the `/etc/default/metron` file.
 
-## Database setup
-
-The REST application persists data in a relational database and requires a dedicated database user and database (see https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html for more detail).  
-Spring uses Hibernate as the default ORM framework but another framework is needed becaused Hibernate is not compatible with the Apache 2 license.  For this reason Metron uses [EclipseLink](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html#boot-features-embedded-database-support).  See the [Spring Data JPA - EclipseLink](https://github.com/spring-projects/spring-data-examples/tree/master/jpa/eclipselink) project for an example on how to configure EclipseLink in Spring.
-
-### Development
-
-The REST application comes with [embedded database support](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html#boot-features-embedded-database-support) for development purposes.
-
-For example, edit these variables in `/etc/default/metron` before starting the application to configure H2:
-```
-METRON_JDBC_DRIVER="org.h2.Driver"
-METRON_JDBC_URL="jdbc:h2:file:~/metrondb"
-METRON_JDBC_USERNAME="root"
-METRON_JDBC_PLATFORM="h2"
-```
-
-### Production
-
-The REST application should be configured with a production-grade database outside of development.
-
 #### Ambari Install
 
 Installing with Ambari is recommended for production deployments.
@@ -125,48 +98,17 @@ This includes managing the PID file, directing logging, etc.
 
 #### Manual Install
 
-The following configures the application for MySQL:
-
-1. Install MySQL if not already available (this example uses version 5.7, installation instructions can be found [here](https://dev.mysql.com/doc/refman/5.7/en/linux-installation-yum-repo.html))
-
-1. Create a metron user and REST database and permission the user for that database:
-    ```
-    CREATE USER 'metron'@'node1' IDENTIFIED BY 'Myp@ssw0rd';
-    CREATE DATABASE IF NOT EXISTS metronrest;
-    GRANT ALL PRIVILEGES ON metronrest.* TO 'metron'@'node1';
-    ```
-
-1. Create the security tables as described in the [Spring Security Guide](https://docs.spring.io/spring-security/site/docs/5.0.4.RELEASE/reference/htmlsingle/#user-schema).
-
-1. Install the MySQL JDBC client onto the REST application host and configurate the METRON_JDBC_CLIENT_PATH variable:
-    ```
-    cd $METRON_HOME/lib
-    wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.41.tar.gz
-    tar xf mysql-connector-java-5.1.41.tar.gz
-    ```
-
-1. Edit these variables in `/etc/default/metron` to configure the REST application for MySQL:
-    ```
-    METRON_JDBC_DRIVER="com.mysql.jdbc.Driver"
-    METRON_JDBC_URL="jdbc:mysql://mysql_host:3306/metronrest"
-    METRON_JDBC_USERNAME="metron"
-    METRON_JDBC_PLATFORM="mysql"
-    METRON_JDBC_CLIENT_PATH=$METRON_HOME/lib/mysql-connector-java-5.1.41/mysql-connector-java-5.1.41-bin.jar
-    ```
-
 1. Switch to the metron user
     ```
     sudo su - metron
     ```
 
-1. Start the REST API. Adjust the password as necessary.
+1. Start the REST API.
     ```
     set -o allexport;
     source /etc/default/metron;
     set +o allexport;
-    export METRON_JDBC_PASSWORD='Myp@ssw0rd';
     $METRON_HOME/bin/metron-rest.sh
-    unset METRON_JDBC_PASSWORD;
     ```
 
 ## Usage
@@ -177,13 +119,40 @@ The REST application can be accessed with the Swagger UI at http://host:port/swa
 
 ### Authentication
 
-The metron-rest module uses [Spring Security](http://projects.spring.io/spring-security/) for authentication and stores user credentials in the relational database configured above.  The required tables are created automatically the first time the application is started so that should be done first.  For example (continuing the MySQL example above), users can be added by connecting to MySQL and running:
+The metron-rest module uses [Spring Security](http://projects.spring.io/spring-security/) for authentication, and supports LDAP based authentication and [Knox SSO](https://knox.apache.org/books/knox-1-1-0/user-guide.html#KnoxSSO+Setup+and+Configuration) based authentication using jwt tokens.
+
+To configure LDAP based application add the following to the rest_application.yml file (note, this would usually be done via the Ambari configuration interface):
+
 ```
-use metronrest;
-insert into users (username, password, enabled) values ('your_username','your_password',1);
-insert into authorities (username, authority) values ('your_username', 'ROLE_USER');
+ldap:
+  provider:
+    url: ldap://node1:33389
+    userdn: uid=admin,ou=people,dc=hadoop,dc=apache,dc=org
+    password: admin-password
+  user: 
+    dn.patterns: uid={0},ou=people,dc=hadoop,dc=apache,dc=org
+    passwordAttribute: userPassword
+    searchBase: ou=people,dc=hadoop,dc=apache,dc=org
+    searchFilter: ""
+  group:
+    searchBase: ou=groups,dc=hadoop,dc=apache,dc=org
+    searchFilter: "member={0}"
+    roleAttribute: "cn"
 ```
 
+This example assumes you are using the Demo LDAP server provided by the Knox project, running on a host call node1 (as in full dev) on port 33389.
+
+To configure the use of Knox SSO, add: 
+
+```
+knox:
+  sso:
+    url: 'https://{gateway_host}:{gateway_port}/gateway/knoxsso/api/v1/websso'
+    pubkey: '<public key from your Knox gateway server>'
+```
+
+This would usually be done with through Ambari.
+
 ### Kerberos
 
 Metron REST can be configured for a cluster with Kerberos enabled.  A client JAAS file is required for Kafka and Zookeeper and a Kerberos keytab for the metron user principal is required for all other services.  Configure these settings in the `/etc/default/metron` file:

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-rest/pom.xml
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/pom.xml b/metron-interface/metron-rest/pom.xml
index 543d5b4..d9d4bfe 100644
--- a/metron-interface/metron-rest/pom.xml
+++ b/metron-interface/metron-rest/pom.xml
@@ -29,7 +29,6 @@
         <antlr.version>4.5</antlr.version>
         <curator.version>2.7.1</curator.version>
         <powermock.version>1.6.4</powermock.version>
-        <spring.boot.version>2.0.1.RELEASE</spring.boot.version>
         <spring.kerberos.version>1.0.1.RELEASE</spring.kerberos.version>
         <swagger.version>2.5.0</swagger.version>
         <mysql.client.version>5.1.40</mysql.client.version>
@@ -91,8 +90,9 @@
             </exclusions>
         </dependency>
         <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-security</artifactId>
+            <groupId>org.apache.metron</groupId>
+            <artifactId>metron-ui-security</artifactId>
+            <version>${project.parent.version}</version>
         </dependency>
         <dependency>
             <groupId>org.springframework.security.kerberos</groupId>

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestApplication.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestApplication.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestApplication.java
index 52cdf8f..b315de6 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestApplication.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestApplication.java
@@ -18,21 +18,23 @@
 package org.apache.metron.rest;
 
 import org.apache.metron.rest.util.ParserIndex;
+import org.apache.metron.ui.MetronSecurityConfig;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
 import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
-
-import static org.apache.metron.rest.MetronRestConstants.LOGGING_SYSTEM_PROPERTY;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.ComponentScans;
 
 @SpringBootApplication
 @EnableAutoConfiguration(exclude = { GsonAutoConfiguration.class, KafkaAutoConfiguration.class })
+@ComponentScans(value = { @ComponentScan, @ComponentScan(basePackageClasses = MetronSecurityConfig.class) })
 public class MetronRestApplication {
 
-  public static void main(String[] args) {
-    ParserIndex.reload();
-    System.setProperty(LOGGING_SYSTEM_PROPERTY, "none");
-    SpringApplication.run(MetronRestApplication.class, args);
-  }
+    public static void main(String[] args) {
+        ParserIndex.reload();
+        System.setProperty(MetronRestConstants.LOGGING_SYSTEM_PROPERTY, "none");
+        SpringApplication.run(MetronRestApplication.class, args);
+    }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/WebSecurityConfig.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/WebSecurityConfig.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/WebSecurityConfig.java
deleted file mode 100644
index f84cdfa..0000000
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/WebSecurityConfig.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/**
- * 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.metron.rest.config;
-
-import static org.apache.metron.rest.MetronRestConstants.SECURITY_ROLE_ADMIN;
-import static org.apache.metron.rest.MetronRestConstants.SECURITY_ROLE_USER;
-
-import org.apache.metron.rest.MetronRestConstants;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.core.env.Environment;
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-import org.springframework.security.core.userdetails.User;
-import org.springframework.security.crypto.password.NoOpPasswordEncoder;
-import org.springframework.security.crypto.password.PasswordEncoder;
-import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
-import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-
-import javax.sql.DataSource;
-import java.util.Arrays;
-import java.util.List;
-
-@Configuration
-@EnableWebSecurity
-@EnableGlobalMethodSecurity(securedEnabled = true)
-@Controller
-public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
-
-    @Autowired
-    private Environment environment;
-
-    @RequestMapping(value = {"/login", "/logout", "/sensors", "/sensors*/**"}, method = RequestMethod.GET)
-    public String handleNGRequests() {
-        return "forward:/index.html";
-    }
-
-    @Override
-    protected void configure(HttpSecurity http) throws Exception {
-        http
-                .authorizeRequests()
-                .antMatchers("/", "/home", "/login").permitAll()
-                .antMatchers("/app/**").permitAll()
-                .antMatchers("/vendor/**").permitAll()
-                .antMatchers("/fonts/**").permitAll()
-                .antMatchers("/assets/images/**").permitAll()
-                .antMatchers("/*.js").permitAll()
-                .antMatchers("/*.ttf").permitAll()
-                .antMatchers("/*.woff2").permitAll()
-                .anyRequest().authenticated()
-                .and().httpBasic()
-                .and()
-                .logout()
-                .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())
-                .invalidateHttpSession(true)
-                .deleteCookies("JSESSIONID");
-        if (Arrays.asList(environment.getActiveProfiles()).contains(MetronRestConstants.CSRF_ENABLE_PROFILE)) {
-            http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
-        } else {
-            http.csrf().disable();
-        }
-    }
-
-    @Autowired
-    private DataSource dataSource;
-
-    @Autowired
-    public void configureJdbc(AuthenticationManagerBuilder auth) throws Exception {
-        List<String> activeProfiles = Arrays.asList(environment.getActiveProfiles());
-        if (activeProfiles.contains(MetronRestConstants.DEV_PROFILE) ||
-                activeProfiles.contains(MetronRestConstants.TEST_PROFILE)) {
-          auth.jdbcAuthentication().dataSource(dataSource)
-                  .withUser("user").password("password").roles(SECURITY_ROLE_USER).and()
-                  .withUser("user1").password("password").roles(SECURITY_ROLE_USER).and()
-                  .withUser("user2").password("password").roles(SECURITY_ROLE_USER).and()
-                  .withUser("admin").password("password").roles(SECURITY_ROLE_USER, SECURITY_ROLE_ADMIN);
-        } else {
-            auth.jdbcAuthentication().dataSource(dataSource);
-        }
-    }
-
-    @Bean
-    public PasswordEncoder passwordEncoder() {
-        return NoOpPasswordEncoder.getInstance();
-    }
-}

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/AlertsUIController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/AlertsUIController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/AlertsUIController.java
index fe2968f..660eb18 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/AlertsUIController.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/AlertsUIController.java
@@ -34,6 +34,8 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.access.annotation.Secured;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.parameters.P;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -103,7 +105,7 @@ public class AlertsUIController {
     return responseEntity;
   }
 
-  @Secured({"ROLE_" + SECURITY_ROLE_ADMIN})
+  @PreAuthorize("hasRole('ROLE_" + SECURITY_ROLE_ADMIN + "') or #user == authentication.principal.username")
   @ApiOperation(value = "Deletes a user's settings.  Only users that are part of "
           + "the \"ROLE_ADMIN\" role are allowed to delete user settings.")
   @ApiResponses(value = {@ApiResponse(message = "User settings were deleted", code = 200),
@@ -113,7 +115,7 @@ public class AlertsUIController {
   @RequestMapping(value = "/settings/{user}", method = RequestMethod.DELETE)
   ResponseEntity<Void> delete(
           @ApiParam(name = "user", value = "The user whose settings will be deleted", required = true)
-          @PathVariable String user)
+          @P("user") @PathVariable String user)
           throws RestException {
     if (alertsUIService.deleteAlertsUIUserSettings(user)) {
       return new ResponseEntity<>(HttpStatus.OK);

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-rest/src/main/resources/application-test.yml
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/resources/application-test.yml b/metron-interface/metron-rest/src/main/resources/application-test.yml
index 0e794cb..e6532fa 100644
--- a/metron-interface/metron-rest/src/main/resources/application-test.yml
+++ b/metron-interface/metron-rest/src/main/resources/application-test.yml
@@ -59,3 +59,22 @@ meta:
   dao:
   # By default, we use the InMemoryMetaAlertDao for our tests
     impl: org.apache.metron.indexing.dao.InMemoryMetaAlertDao
+
+knox:
+  sso:
+    url: 
+
+ldap:
+  provider:
+    url: ldap://localhost:33389
+    userdn: uid=admin,ou=people,dc=hadoop,dc=apache,dc=org
+    password: password
+  user:
+    dn.patterns: uid={0},ou=people,dc=hadoop,dc=apache,dc=org
+    passwordAttribute: userPassword
+    searchBase: ou=people,dc=hadoop,dc=apache,dc=org
+    searchFilter: ""
+  group:
+    searchBase: ou=groups,dc=hadoop,dc=apache,dc=org
+    searchFilter: "member={0}"
+    roleAttribute: "cn"

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-rest/src/main/scripts/metron-rest.sh
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/scripts/metron-rest.sh b/metron-interface/metron-rest/src/main/scripts/metron-rest.sh
index c0c9fac..728b60b 100644
--- a/metron-interface/metron-rest/src/main/scripts/metron-rest.sh
+++ b/metron-interface/metron-rest/src/main/scripts/metron-rest.sh
@@ -81,10 +81,15 @@ echo "METRON_SPRING_PROFILES_ACTIVE=${METRON_SPRING_PROFILES_ACTIVE}"
 
 # the vagrant Spring profile provides configuration values, otherwise configuration is provided by rest_application.yml
 if [[ !(${METRON_SPRING_PROFILES_ACTIVE} == *"vagrant"*) ]]; then
-    METRON_CONFIG_LOCATION=" --spring.config.location=$METRON_HOME/config/rest_application.yml,classpath:/application.yml"
+    METRON_CONFIG_LOCATION=" --spring.config.location=classpath:/application.yml,$METRON_HOME/config/rest_application.yml,$METRON_HOME/config/rest_security.yml"
+    echo "METRON_CONFIG_LOCATION=${METRON_CONFIG_LOCATION}"
+    METRON_SPRING_OPTIONS+=${METRON_CONFIG_LOCATION}
+else
+	METRON_CONFIG_LOCATION=" --spring.config.location=classpath:/application-vagrant.yml,$METRON_HOME/config/rest_application.yml,$METRON_HOME/config/rest_security.yml"
     echo "METRON_CONFIG_LOCATION=${METRON_CONFIG_LOCATION}"
     METRON_SPRING_OPTIONS+=${METRON_CONFIG_LOCATION}
 fi
+
 METRON_SPRING_OPTIONS+=" --server.port=$METRON_REST_PORT"
 if [ ${METRON_SPRING_PROFILES_ACTIVE} ]; then
     METRON_PROFILES_ACTIVE=" --spring.profiles.active=${METRON_SPRING_PROFILES_ACTIVE}"

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestSecurityConfig.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestSecurityConfig.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestSecurityConfig.java
new file mode 100644
index 0000000..04e82b9
--- /dev/null
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestSecurityConfig.java
@@ -0,0 +1,44 @@
+/**
+ * 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.metron.rest.config;
+
+import static org.apache.metron.rest.MetronRestConstants.SECURITY_ROLE_ADMIN;
+import static org.apache.metron.rest.MetronRestConstants.SECURITY_ROLE_USER;
+import static org.apache.metron.rest.MetronRestConstants.TEST_PROFILE;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.test.context.ActiveProfiles;
+
+@Configuration
+@ActiveProfiles(TEST_PROFILE)
+@Order(99)
+public class TestSecurityConfig extends WebSecurityConfigurerAdapter {
+  @Override
+  public void configure(AuthenticationManagerBuilder auth) throws Exception {
+    // @formatter:off
+    auth.inMemoryAuthentication()
+        .withUser("user").password("password").roles(SECURITY_ROLE_USER).and()
+        .withUser("user1").password("password").roles(SECURITY_ROLE_USER).and()
+        .withUser("user2").password("password").roles(SECURITY_ROLE_USER).and()
+        .withUser("admin").password("password").roles(SECURITY_ROLE_USER, SECURITY_ROLE_ADMIN);
+    // @formatter:on
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/AlertsUIControllerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/AlertsUIControllerIntegrationTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/AlertsUIControllerIntegrationTest.java
index 49863d6..98b4819 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/AlertsUIControllerIntegrationTest.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/AlertsUIControllerIntegrationTest.java
@@ -157,7 +157,7 @@ public class AlertsUIControllerIntegrationTest {
             .andExpect(status().isUnauthorized());
     this.mockMvc.perform(get(alertUrl + "/settings/all").with(httpBasic(user1, password)).with(csrf()))
             .andExpect(status().isForbidden());
-    this.mockMvc.perform(delete(alertUrl + "/settings/user1").with(httpBasic(user1, password)).with(csrf()))
+    this.mockMvc.perform(delete(alertUrl + "/settings/user1").with(httpBasic(user2, password)).with(csrf()))
             .andExpect(status().isForbidden());
   }
 
@@ -275,13 +275,16 @@ public class AlertsUIControllerIntegrationTest {
    * @throws Exception
    */
   private void alertsProfilesShouldBeProperlyDeleted() throws Exception {
-
     // user1 deletes their profile
-    this.mockMvc.perform(delete(alertUrl + "/settings/user1").with(httpBasic(admin, password)))
+    this.mockMvc.perform(delete(alertUrl + "/settings/user1")
+        .with(httpBasic(admin, password))
+        .with(csrf()))
             .andExpect(status().isOk());
 
     // user1 should get a 404 when trying to delete an alerts profile that doesn't exist
-    this.mockMvc.perform(delete(alertUrl + "/settings/user1").with(httpBasic(admin, password)))
+    this.mockMvc.perform(delete(alertUrl + "/settings/user1")
+        .with(httpBasic(admin, password))
+        .with(csrf()))
             .andExpect(status().isNotFound());
 
     // user1 should get a 404 when trying to retrieve their alerts profile
@@ -289,7 +292,8 @@ public class AlertsUIControllerIntegrationTest {
             .andExpect(status().isNotFound());
 
     // user2's alerts profile should still exist
-    this.mockMvc.perform(get(alertUrl + "/settings").with(httpBasic(user2, password)))
+    this.mockMvc.perform(get(alertUrl + "/settings")
+        .with(httpBasic(user2, password)))
             .andExpect(status().isOk())
             .andExpect(
                     content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
@@ -303,7 +307,9 @@ public class AlertsUIControllerIntegrationTest {
             .andExpect(content().json("{\"" + user2 + "\": " + user2AlertUserSettingsJson + "}"));
 
     // user2 deletes their profile
-    this.mockMvc.perform(delete(alertUrl + "/settings/user2").with(httpBasic(admin, password)))
+    this.mockMvc.perform(delete(alertUrl + "/settings/user2")
+        .with(httpBasic(admin, password))
+        .with(csrf()))
             .andExpect(status().isOk());
 
     // user2 should get a 404 when trying to delete an alerts profile that doesn't exist

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-host/pom.xml
----------------------------------------------------------------------
diff --git a/metron-interface/metron-ui-host/pom.xml b/metron-interface/metron-ui-host/pom.xml
new file mode 100644
index 0000000..82ee1f3
--- /dev/null
+++ b/metron-interface/metron-ui-host/pom.xml
@@ -0,0 +1,138 @@
+<?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>
+
+    <artifactId>metron-ui-host</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Metron Generic UI Host</name>
+    <description>Spring Server to host config ui</description>
+
+    <parent>
+        <groupId>org.apache.metron</groupId>
+        <artifactId>metron-interface</artifactId>
+        <version>0.5.1</version>
+    </parent>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <java.version>1.8</java.version>
+        <jwt.version>4.41.2</jwt.version>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.apache.metron</groupId>
+            <artifactId>metron-ui-security</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>ch.qos.logback</groupId>
+                    <artifactId>logback-classic</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-test-autoconfigure</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.directory.server</groupId>
+            <artifactId>apacheds-all</artifactId>
+            <version>1.5.4</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.metron</groupId>
+            <artifactId>metron-ui-security</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+    </dependencies>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>${spring-cloud.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <!-- Import dependency management from Spring Boot -->
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring.boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring.boot.version}</version>
+                <configuration>
+                    <executable>true</executable>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+
+</project>

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/AbstractHostApplication.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/AbstractHostApplication.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/AbstractHostApplication.java
new file mode 100644
index 0000000..fd7095d
--- /dev/null
+++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/AbstractHostApplication.java
@@ -0,0 +1,28 @@
+/**
+ * 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.metron.ui;
+
+import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
+import org.springframework.context.annotation.Configuration;
+
+public abstract class AbstractHostApplication {
+    @Configuration
+    @EnableZuulProxy
+    public static class ZuulConfig {
+    }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java
new file mode 100644
index 0000000..d11d16f
--- /dev/null
+++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java
@@ -0,0 +1,71 @@
+/**
+ * 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.metron.ui;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.security.Principal;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.access.annotation.Secured;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.view.RedirectView;
+
+/**
+ * A trivial endpoint to ping for currently authenticated user principal
+ * 
+ */
+@RestController
+public class UserController {
+    @Value("${knox.sso.url}")
+    private String knoxSSOUrl;
+
+    @RequestMapping(path = "/whoami", method = RequestMethod.GET)
+    public String user(Principal user) {
+        return user.getName();
+    }
+    
+    @Secured("IS_AUTHENTICATED_FULLY")
+    @RequestMapping(path = "/whoami/roles", method = RequestMethod.GET)
+    public List<String> user() {
+      UserDetails userDetails = (UserDetails)SecurityContextHolder.getContext().
+          getAuthentication().getPrincipal();
+      return userDetails.getAuthorities().stream().map(ga -> ga.getAuthority()).collect(Collectors.toList());
+    }
+
+    @RequestMapping(path = "/logout", method = RequestMethod.GET)
+    public RedirectView logout(Principal user, HttpServletResponse httpServletResponse, @RequestParam("originalUrl") String originalUrl) throws UnsupportedEncodingException {
+        StringBuilder redirect = new StringBuilder();
+        redirect.append(knoxSSOUrl.replaceAll("websso", "webssout"));
+        redirect.append(knoxSSOUrl.contains("?") ? "&": "?");
+        redirect.append("originalUrl=");
+        redirect.append(URLEncoder.encode(originalUrl, StandardCharsets.UTF_8.name()));
+
+        return new RedirectView(redirect.toString());
+    }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulAuthenticationFilter.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulAuthenticationFilter.java
new file mode 100644
index 0000000..a322612
--- /dev/null
+++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulAuthenticationFilter.java
@@ -0,0 +1,77 @@
+/**
+ * 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.metron.ui;
+
+import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.stereotype.Component;
+
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import com.netflix.zuul.exception.ZuulException;
+
+@Component
+public class ZuulAuthenticationFilter extends ZuulFilter {
+
+    private static final String AUTHORIZATION_HEADER = "Authorization";
+    public static final String COOKIE_NAME = "hadoop-jwt";
+
+    /**
+     * Only filter if we have no Authorization header already
+     */
+    @Override
+    public boolean shouldFilter() {
+        RequestContext currentContext = RequestContext.getCurrentContext();
+        String currentAuth = currentContext.getRequest().getHeader(AUTHORIZATION_HEADER);
+        if (currentAuth == null || currentAuth.isEmpty()) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public Object run() throws ZuulException {
+        RequestContext ctx = RequestContext.getCurrentContext();
+        HttpServletRequest request = ctx.getRequest();
+        Cookie[] cookies = request.getCookies();
+        for (Cookie cookie : cookies) {
+            if (COOKIE_NAME.equals(cookie.getName())) {
+                // add this cookie to the Authorization header
+                String newAuthHeader = "Bearer " + cookie.getValue();
+                ctx.getZuulRequestHeaders().put(AUTHORIZATION_HEADER, newAuthHeader);
+                break;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String filterType() {
+        return PRE_TYPE;
+    }
+
+    @Override
+    public int filterOrder() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulError.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulError.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulError.java
new file mode 100644
index 0000000..c5ae65e
--- /dev/null
+++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulError.java
@@ -0,0 +1,34 @@
+/**
+ * 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.metron.ui;
+
+public class ZuulError {
+    private String error;
+
+    public ZuulError(Throwable throwable) {
+        this.setError(throwable.getMessage());
+    }
+
+    public String getError() {
+        return error;
+    }
+
+    public void setError(String error) {
+        this.error = error;
+    }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulErrorFilter.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulErrorFilter.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulErrorFilter.java
new file mode 100644
index 0000000..9d58c16
--- /dev/null
+++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulErrorFilter.java
@@ -0,0 +1,67 @@
+/**
+ * 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.metron.ui;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
+import org.springframework.cloud.netflix.zuul.util.ZuulRuntimeException;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ReflectionUtils;
+
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import com.netflix.zuul.exception.ZuulException;
+
+@Component
+public class ZuulErrorFilter extends ZuulFilter {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ZuulErrorFilter.class);
+
+	@Override
+	public boolean shouldFilter() {
+		return true;
+	}
+
+	@Override
+	public Object run() throws ZuulException {
+		try {
+            RequestContext ctx = RequestContext.getCurrentContext();
+            Throwable throwable = ctx.getThrowable();
+            if (throwable != null && throwable instanceof ZuulException) {
+                LOG.error("Zuul failure: " + throwable.getMessage(), throwable);
+                ctx.setThrowable(new ZuulRuntimeException((ZuulException) throwable));
+            }
+        } catch (Exception ex) {
+            LOG.error("Exception in custom error filter", ex);
+            ReflectionUtils.rethrowRuntimeException(ex);
+        }
+		return null;
+	}
+
+	@Override
+	public String filterType() {
+		return FilterConstants.ERROR_TYPE;
+	}
+
+	@Override
+	public int filterOrder() {
+		return FilterConstants.SEND_ERROR_FILTER_ORDER - 1;
+	}
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-host/src/main/resources/application.yml
----------------------------------------------------------------------
diff --git a/metron-interface/metron-ui-host/src/main/resources/application.yml b/metron-interface/metron-ui-host/src/main/resources/application.yml
new file mode 100644
index 0000000..eebb04b
--- /dev/null
+++ b/metron-interface/metron-ui-host/src/main/resources/application.yml
@@ -0,0 +1,59 @@
+# 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.
+metron:
+  version: 0.5.0
+
+logging:
+  level:
+    root: WARN
+
+server:
+  port: 4201
+
+zuul:
+  routes:
+    rest: 
+      path: /api/v1/**
+      url: http://localhost:8082/api/v1
+
+proxy:
+  auth:
+    routes:
+      rest: passthru
+
+ldap:
+  provider:
+    url: ldap://localhost:33389
+    userdn: uid=admin,ou=people,dc=hadoop,dc=apache,dc=org
+    password: password
+  user: 
+    dn.patterns: uid={0},ou=people,dc=hadoop,dc=apache,dc=org
+    passwordAttribute: userPassword
+    searchBase: ou=people,dc=hadoop,dc=apache,dc=org
+    searchFilter: ""
+  group:
+    searchBase: ou=groups,dc=hadoop,dc=apache,dc=org
+    searchFilter: "member={0}"
+    roleAttribute: "cn"
+
+knox:
+  sso:
+    url: ''
+    pubkey: ''
+    
+ribbon:
+  ConnectTimeout: 3000
+  ReadTimeout: 60000
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/TestHostApplication.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/TestHostApplication.java b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/TestHostApplication.java
new file mode 100644
index 0000000..04feb53
--- /dev/null
+++ b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/TestHostApplication.java
@@ -0,0 +1,25 @@
+/**
+ * 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.metron.ui;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class TestHostApplication extends AbstractHostApplication {
+
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/WhoamiTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/WhoamiTest.java b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/WhoamiTest.java
new file mode 100644
index 0000000..98c4a9f
--- /dev/null
+++ b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/WhoamiTest.java
@@ -0,0 +1,120 @@
+/**
+ * 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.metron.ui;
+
+import static org.apache.metron.ui.EmbeddedLdap.EMBEDDED_LDAP_PROFILE;
+import static org.hamcrest.Matchers.containsString;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
+import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+
+import javax.servlet.http.Cookie;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.ResultActions;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.JWSObject;
+import com.nimbusds.jose.Payload;
+import com.nimbusds.jose.crypto.RSASSASigner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ActiveProfiles({"test", EMBEDDED_LDAP_PROFILE})
+public class WhoamiTest {
+
+  @Autowired
+  private WebApplicationContext context;
+
+  private MockMvc mockMvc;
+
+  private String username = "admin";
+  private String password = "password";
+
+  @Value("${knox.sso.pubkey}")
+  private String publickey;
+
+  @Value("${knox.sso.privatekey}")
+  private String privatekey;
+
+  @Value("${knox.sso.url}")
+  private String knoxUrl;
+
+  @Before
+  public void setup() {
+    mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
+  }
+
+  @Test
+  public void testWhoamiNoAuth() throws Exception {
+    mockMvc.perform(get("/whoami")).andExpect(status().is3xxRedirection())
+        .andExpect(redirectedUrl(knoxUrl + "?originalUrl=http://localhost/whoami"));
+  }
+
+  @Test
+  public void testWhoamiBasicAuth() throws Exception {
+    assertLoginCorrect(mockMvc.perform(get("/whoami").with(httpBasic(username, password))));
+  }
+
+  private ResultActions assertLoginCorrect(ResultActions actions) throws Exception {
+    return actions.andExpect(status().isOk()).andExpect(content().string(containsString(username)));
+  }
+
+  @Test
+  public void testWhoamiJwtAuth() throws Exception {
+    String keyStr = String.join("", privatekey.split("\\s*|\\r|\\n"));
+    byte[] dec = Base64.getDecoder().decode(keyStr);
+    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(dec);
+    KeyFactory kf = KeyFactory.getInstance("RSA");
+    PrivateKey privKey = kf.generatePrivate(keySpec);
+
+    JWSObject jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
+        new Payload("{ \"sub\": \"" + username + "\" }"));
+    jwsObject.sign(new RSASSASigner(privKey));
+    String token = jwsObject.serialize();
+
+    assertLoginCorrect(mockMvc.perform(get("/whoami").cookie(new Cookie("hadoop-jwt", token))));
+  }
+
+  @Test
+  public void testWhoamiRoles() throws Exception {
+    mockMvc.perform(get("/whoami/roles").with(httpBasic(username, password))).andExpect(status().isOk())
+        .andExpect(
+            content().string("[\"ROLE_USER\",\"ROLE_ADMIN\"]"));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/ZuulAuthorizationHeaderProxyTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/ZuulAuthorizationHeaderProxyTest.java b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/ZuulAuthorizationHeaderProxyTest.java
new file mode 100644
index 0000000..8f4c055
--- /dev/null
+++ b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/ZuulAuthorizationHeaderProxyTest.java
@@ -0,0 +1,103 @@
+/**
+ * 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.metron.ui;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.security.SecureRandom;
+
+import javax.servlet.http.Cookie;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+import com.netflix.zuul.context.RequestContext;
+import com.netflix.zuul.exception.ZuulException;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.JWSObject;
+import com.nimbusds.jose.KeyLengthException;
+import com.nimbusds.jose.Payload;
+import com.nimbusds.jose.crypto.MACSigner;
+
+public class ZuulAuthorizationHeaderProxyTest {
+
+    private static final String BASIC_AUTH_HEADER = "Basic dGVzdDp0ZXN0";
+
+    private RequestContext context;
+
+    private byte[] sharedKey = new byte[32];
+
+    private String validToken;
+
+    private boolean keyInited = false;
+
+    @Before
+    public void setTestRequestcontext() {
+        context = new RequestContext();
+        context.setResponse(new MockHttpServletResponse());
+        context.setResponseGZipped(false);
+
+        RequestContext.testSetCurrentContext(context);
+    }
+
+    @Test
+    public void testThatZuulPassesCookiesToAuthorization() throws ZuulException, KeyLengthException, JOSEException {
+        ZuulAuthenticationFilter zuulAuthenticationFilter = new ZuulAuthenticationFilter();
+
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        request.setCookies(validCookie());
+        context.setRequest(request);
+        zuulAuthenticationFilter.run();
+
+        String header = context.getZuulRequestHeaders().get("Authorization");
+        assertTrue("Authorization contains bearer", header.startsWith("Bearer "));
+        assertTrue("Authorization contains cookie value", header.endsWith(validToken()));
+    }
+
+    @Test
+    public void testDoesNotReplaceAuthorizationHeader() throws ZuulException, KeyLengthException, JOSEException {
+        ZuulAuthenticationFilter zuulAuthenticationFilter = new ZuulAuthenticationFilter();
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        request.setCookies(validCookie());
+        request.addHeader("Authorization", BASIC_AUTH_HEADER);
+        context.setRequest(request);
+        assertFalse(zuulAuthenticationFilter.shouldFilter());
+    }
+
+    private Cookie validCookie() throws KeyLengthException, JOSEException {
+        return new Cookie(ZuulAuthenticationFilter.COOKIE_NAME, validToken());
+    }
+
+    private String validToken() throws KeyLengthException, JOSEException {
+        if (!this.keyInited ) {
+            new SecureRandom().nextBytes(sharedKey);
+            this.keyInited = true;
+        }
+        if (this.validToken == null) {
+            JWSObject jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.HS256), new Payload("Test"));
+            jwsObject.sign(new MACSigner(sharedKey));
+            this.validToken = jwsObject.serialize();
+        }
+        return this.validToken;
+    }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-host/src/test/resources/application-test.yml
----------------------------------------------------------------------
diff --git a/metron-interface/metron-ui-host/src/test/resources/application-test.yml b/metron-interface/metron-ui-host/src/test/resources/application-test.yml
new file mode 100644
index 0000000..f89a618
--- /dev/null
+++ b/metron-interface/metron-ui-host/src/test/resources/application-test.yml
@@ -0,0 +1,77 @@
+# 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.
+
+spring:
+  logging:
+    level:
+      root: debug
+
+knox:
+  sso:
+    url: https://localhost:8443/gateway/knoxsso/api/v1/websso
+    pubkey: |
+      MIIEqTCCApGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCVVMx
+      CzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZBcGFjaGUxGDAWBgNVBAMMD0NBIGludGVy
+      bWVkaWF0ZTAeFw0xODA3MTExOTA0NTRaFw0yODEwMTYxOTA0NTRaMDsxCzAJBgNV
+      BAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UECgwGQXBhY2hlMQ4wDAYDVQQDDAVu
+      b2RlMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6j8MIkAi4hZXd8
+      rD+lmcQ6SmmUX8DrWKOu3cywUTcUR6ZfJQ+Lt0kZH2yDOPhpmr5TIEx7aTzAmloz
+      ZUVoArcoqjGan7Np5Hy2vC1rDeqMbueQ7m4LSpwFRzqb9ZnFycq+U1Jo5nrHwVdy
+      xfvo5yOYOgyWQT/upEsStiR0ADjyLPzTVQlErdAAVxKbRHF3ikWSvHzu8yoKcWAG
+      n7CbShuOF0bIMOR9e7GtlSQH6JMxH17oEU98KiVRvJV52RKHpHZpPfDb36YvsgTy
+      7ZczDmCQPNpU9hfV+vcgZEXFyrpxLgG7pHJBXPXWv8cw9rQLLim0297LNRpGAAz2
+      Gc2todECAwEAAaOBrDCBqTAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIGQDAz
+      BglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmlj
+      YXRlMB0GA1UdDgQWBBQWMdyJLWA4vgE90pAuRa4/z4S4kDAOBgNVHQ8BAf8EBAMC
+      BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwEAYDVR0RBAkwB4IFbm9kZTEwDQYJKoZI
+      hvcNAQELBQADggIBAMdFhC4xDxGs7i/gKhgBCv1JicNqk6Y2OQsS8ohk64ansx1r
+      uU0Rbx/pOsuD+d3ZuYeBaOYnHSLhExCcSxFjUlBkjS7aEigMxlHf2D+tYOgdcwlc
+      SjMaqyFDke+mR0rm8I15HviLjjZy1bDLnb8UsozLtdU040/MAtx9uF7SqvYUsTqy
+      alyfPeYZGpHZiFAmTcZ33uF3EByaSLACMVje0O1C9Xi/1v5Smp64NF15EF2DlHIv
+      TAj88oG7eEivVWie41mx8s/8WpR6XE3WFuZSc+j4qndtzwvmzlaO/e/v64ZzTPTL
+      SnrV424gtfZahjCb7+rSLQnSZShPeQessa1uF00xkCwlXuA7WXP9dAtOycySRsI+
+      qy7vwD9Y5ZkZwFK8+8UnvySwwCSEHmy4zM0irA/XIKIRw7ahU3rxbkHgVCGh6Pyu
+      kGfv/+Wy9yW461w0aYUTMrUrS429CBDY0ek3T9eQ5bieJRjOYOl/uuPH+L4VSCOS
+      p2WIuXqqDMXqmxMUFNuaLYEg4Y51aLD0lkB+SH+tnOP5CZdufIKZRQhYiC+xcs2E
+      2/VvbqjAMe9vzF6d7a5EqbTkdS9k8CNnmxCfN+FlSl/iqUI3HKLVxNs+2Sux+Dhl
+      Nkt9qMcG2Gj0TxlqU43HrGeruVIxgC6Lj/QcIrc3Ddb1u7dccuNtF5UoqnVD
+    privatekey: |
+      MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDOo/DCJAIuIWV3
+      fKw/pZnEOkpplF/A61ijrt3MsFE3FEemXyUPi7dJGR9sgzj4aZq+UyBMe2k8wJpa
+      M2VFaAK3KKoxmp+zaeR8trwtaw3qjG7nkO5uC0qcBUc6m/WZxcnKvlNSaOZ6x8FX
+      csX76OcjmDoMlkE/7qRLErYkdAA48iz801UJRK3QAFcSm0Rxd4pFkrx87vMqCnFg
+      Bp+wm0objhdGyDDkfXuxrZUkB+iTMR9e6BFPfColUbyVedkSh6R2aT3w29+mL7IE
+      8u2XMw5gkDzaVPYX1fr3IGRFxcq6cS4Bu6RyQVz11r/HMPa0Cy4ptNveyzUaRgAM
+      9hnNraHRAgMBAAECggEBAJ2MMvDiIWNohQMf4/g221DYHIn43TSqew95MJRyTcmP
+      xb0cR5ZdsOWjqOjD97i2U4wOts55PVhbhJOHIgxT69YXxAND38Ub1GAdtsVuHNMa
+      NSiKwK7YHw9rms4dwJh4S40vpTlsz2UHTerNkBOrlCb4VjHokWEcItk2L/cFFnJT
+      Gymql1ZpD3BMprZW12yjLX38utCsuH2uFdYj2l53BtNNt8Su4ToXmvB4+rqW+ktF
+      rHINUe64EoKLnbBKpjzDlYH16uGVGAt6VBHU8Gr6n70gdWJvrLIaFy8ZNxRiER+x
+      qwgFad/aQWCGedb5PRtkQmvB+EIGRBbJ0zeZV+IrqMECgYEA8uWwcfAIOo9mZIUP
+      vlUCODO7oCIB7n1J5OZQlkKgCWpiIKgvSpGW/+dBaSKCspLfmlvFsb5XwKio26io
+      xGJ8gBnwexY4vuHsdA3DZWDcC1IHBf/chix5sRlYAqh6NRfgmhoUq05fYJTpr+A5
+      EWXZs1Edt0wj0mswPdNgkevHX9kCgYEA2cmPLhRKP4zrNZj3ojChjiFXrYtryQVv
+      Vypd6sznyuiyN1DGYBN1VrEg+ukpAYv6Ez0clX7Xa/03BUAUhwTYve/ZRrZC5w3P
+      4EYCRAaNzDZWqhQrb+z3GFz+h4eOVZ6dnkAmuFqIf+ws11pocM04dCjy2e97UZZA
+      naGu54iHjrkCgYEAhtR/RE/kkXUmdmfyXEnd6Iq2/OXDwrnjed9rHm2vXmqiO9SA
+      I9l7Q2QASDby6+NhodKNg+PP3E8DJKOTwyeUSpubhQfJyhOo6Kb3LuA8ZUBMS8VC
+      iWxIxMj3tMoGxFATyhbuIEVp5jfjHFDP/NtXpBVD9IqcW+JKLheWxIln68kCgYAq
+      z8+AnGZ4FaiLEbXkQTEQ8ob8y4J1ssbPWLm7lWofXhzieNN2QXz4fLth94GjFzQi
+      ognDbXrFdLJjKtSeMhq1Q7fviZafOvzZNonte2hWc3wX1P0w9GEife1fEQuu0w5i
+      9HNoHAvnMbMi5lfPjNgDJaWPp98TC7lKA2WRiCo1qQKBgErhfGKRxU13ZTPsKXZf
+      7i0vMC7n5CPJajrFDml/NZLODHwif2KqZE+gHlxtgWayU1UigSdUVDXdRZbS0RmA
+      yrmpu5zIdozX3pFmBsObpvd5TtLvq9er+HF93gedBotRqqVuK8yX7VY/M9/nBJnc
+      FITLrkrz6SPpr9Wm+ufjKxn2

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/pom.xml
----------------------------------------------------------------------
diff --git a/metron-interface/metron-ui-security/pom.xml b/metron-interface/metron-ui-security/pom.xml
new file mode 100644
index 0000000..dc980d4
--- /dev/null
+++ b/metron-interface/metron-ui-security/pom.xml
@@ -0,0 +1,135 @@
+<?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>
+
+
+    <artifactId>metron-ui-security</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Metron SSO Security Configs</name>
+    <description>Spring Security setup to Metron SSO</description>
+
+    <parent>
+        <groupId>org.apache.metron</groupId>
+        <artifactId>metron-interface</artifactId>
+        <version>0.5.1</version>
+    </parent>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <java.version>1.8</java.version>
+        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
+        <spring.boot.version>2.0.1.RELEASE</spring.boot.version>
+        <jwt.version>4.41.2</jwt.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>ch.qos.logback</groupId>
+                    <artifactId>logback-classic</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.ldap</groupId>
+            <artifactId>spring-ldap-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-ldap</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.nimbusds</groupId>
+            <artifactId>nimbus-jose-jwt</artifactId>
+            <version>${jwt.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.5</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.directory.server</groupId>
+            <artifactId>apacheds-all</artifactId>
+            <version>1.5.4</version>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>${spring-cloud.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <!-- Import dependency management from Spring Boot -->
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring.boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+  <build>
+  <plugins>
+  <plugin>
+    <groupId>org.apache.maven.plugins</groupId>
+    <artifactId>maven-jar-plugin</artifactId>
+    <version>${global_jar_version}</version>
+    <executions>
+      <execution>
+        <goals>
+          <goal>test-jar</goal>
+        </goals>
+      </execution>
+    </executions>
+  </plugin>
+  </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/KnoxSSOAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/KnoxSSOAuthenticationFilter.java b/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/KnoxSSOAuthenticationFilter.java
new file mode 100644
index 0000000..8cd64d0
--- /dev/null
+++ b/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/KnoxSSOAuthenticationFilter.java
@@ -0,0 +1,289 @@
+/**
+ * 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.metron.ui;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPublicKey;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.List;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.web.authentication.WebAuthenticationDetails;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSObject;
+import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.RSASSAVerifier;
+import com.nimbusds.jwt.SignedJWT;
+
+public class KnoxSSOAuthenticationFilter implements Filter {
+    private static final Logger LOG = LoggerFactory.getLogger(KnoxSSOAuthenticationFilter.class);
+
+    private final String knoxUrl;
+    private final RSAPublicKey publicKey;
+    private final MetronAuthenticationProvider authenticationProvider;
+    private String knoxCookie;
+    private String knoxOriginalUrl;
+
+    public KnoxSSOAuthenticationFilter(MetronAuthenticationProvider authenticationProvider, String knoxUrl,
+            String knoxCookie, String knoxOriginalUrl, RSAPublicKey publicKey) {
+        super();
+        this.authenticationProvider = authenticationProvider;
+        this.knoxUrl = knoxUrl;
+        if (knoxCookie == null) {
+            this.knoxCookie = "hadoop-jwt";
+        } else {
+            this.knoxCookie = knoxCookie;
+        }
+        if (knoxOriginalUrl == null) {
+            this.knoxOriginalUrl = "originalUrl";
+        } else {
+            this.knoxOriginalUrl = knoxOriginalUrl;
+        }
+        this.publicKey = publicKey;
+    }
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+
+    @Override
+    public void destroy() {
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+            throws IOException, ServletException {
+        HttpServletRequest httpRequest = (HttpServletRequest) request;
+        HttpServletResponse httpResponse = (HttpServletResponse) response;
+        String authHeader = httpRequest.getHeader("Authorization");
+        // if SSO is not enabled, skip this filter
+        if (this.knoxUrl.isEmpty() || (authHeader != null && authHeader.startsWith("Basic"))) {
+            chain.doFilter(request, response);
+        } else {
+            String serializedJWT = getJWTFromAuthorization(httpRequest);
+            if (serializedJWT == null) {
+                serializedJWT = getJWTFromCookie(httpRequest);
+            }
+
+            if (serializedJWT != null) {
+                SignedJWT jwtToken = null;
+                try {
+                    jwtToken = SignedJWT.parse(serializedJWT);
+                    boolean valid = validateToken(jwtToken);
+                    // if the public key provide is correct and also token is not expired the
+                    // process token
+                    if (valid) {
+                        String userName = jwtToken.getJWTClaimsSet().getSubject();
+                        LOG.info("SSO login user : {} ", userName);
+                        if (userName != null && !userName.trim().isEmpty()) {
+                            List<GrantedAuthority> grantedAuths = MetronAuthenticationProvider
+                                    .getAuthoritiesFromUGI(userName);
+                            final UserDetails principal = new User(userName, "", grantedAuths);
+                            final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken(
+                                    principal, "", grantedAuths);
+                            WebAuthenticationDetails webDetails = new WebAuthenticationDetails(httpRequest);
+                            ((AbstractAuthenticationToken) finalAuthentication).setDetails(webDetails);
+                            Authentication authentication = authenticationProvider.authenticate(finalAuthentication);
+                            SecurityContextHolder.getContext().setAuthentication(authentication);
+                        }
+                        Date expirationTime = jwtToken.getJWTClaimsSet().getExpirationTime();
+                        Date notBeforeTime = jwtToken.getJWTClaimsSet().getNotBeforeTime();
+                        Date now = new Date();
+                        if (expirationTime != null && now.after(expirationTime)) {
+                          LOG.info("SSO token expired: {} ", userName);
+                          redirectToKnox(httpRequest, httpResponse, chain);
+                        } 
+                        if (notBeforeTime != null && now.before(notBeforeTime)) {
+                          LOG.info("SSO token not yet valid: {} ", userName);
+                          redirectToKnox(httpRequest, httpResponse, chain);
+                        }
+                        chain.doFilter(request, response);
+                    } else { // if the token is not valid then redirect to knox sso
+                        redirectToKnox(httpRequest, httpResponse, chain);
+                    }
+                } catch (ParseException e) {
+                    LOG.warn("Unable to parse the JWT token", e);
+                    redirectToKnox(httpRequest, httpResponse, chain);
+                }
+            } else { // if there is no token, redirect
+                redirectToKnox(httpRequest, httpResponse, chain);
+            }
+        }
+    }
+
+    private void redirectToKnox(HttpServletRequest httpRequest, HttpServletResponse httpResponse, FilterChain chain)
+            throws IOException, ServletException {
+        // should probably check it's a browser
+        String ssourl = constructLoginURL(httpRequest);
+        httpResponse.sendRedirect(ssourl);
+    }
+
+    /**
+     * Create the URL to be used for authentication of the user in the absence of a
+     * JWT token within the incoming request.
+     *
+     * @param request
+     *            for getting the original request URL
+     * @return url to use as login url for redirect
+     */
+    protected String constructLoginURL(HttpServletRequest request) {
+        String delimiter = "?";
+        if (knoxUrl.contains("?")) {
+            delimiter = "&";
+        }
+        String loginURL = knoxUrl + delimiter + knoxOriginalUrl + "=" + request.getRequestURL().toString()
+                + getOriginalQueryString(request);
+        return loginURL;
+    }
+
+    private String getOriginalQueryString(HttpServletRequest request) {
+        String originalQueryString = request.getQueryString();
+        return (originalQueryString == null) ? "" : "?" + originalQueryString;
+    }
+
+    /**
+     * Verify the signature of the JWT token in this method. This method depends on
+     * the public key that was established during init based upon the provisioned
+     * public key. Override this method in subclasses in order to customize the
+     * signature verification behavior.
+     *
+     * @param jwtToken
+     *            the token that contains the signature to be validated
+     * @return valid true if signature verifies successfully; false otherwise
+     */
+    protected boolean validateToken(SignedJWT jwtToken) {
+        boolean valid = false;
+        if (JWSObject.State.SIGNED == jwtToken.getState()) {
+            LOG.debug("SSO token is in a SIGNED state");
+            if (jwtToken.getSignature() != null) {
+                LOG.debug("SSO token signature is not null");
+                try {
+                    JWSVerifier verifier = new RSASSAVerifier(publicKey);
+                    if (jwtToken.verify(verifier)) {
+                        valid = true;
+                        LOG.debug("SSO token has been successfully verified");
+                    } else {
+                        LOG.warn("SSO signature verification failed.Please check the public key");
+                    }
+                } catch (JOSEException je) {
+                    LOG.warn("Error while validating signature", je);
+                } catch (Exception e) {
+                    LOG.warn("Error while validating signature", e);
+                }
+            }
+            // Now check that the signature algorithm was as expected
+            if (valid) {
+                String receivedSigAlg = jwtToken.getHeader().getAlgorithm().getName();
+                if (!receivedSigAlg.equals("RS256")) {
+                    valid = false;
+                }
+            }
+        }
+        return valid;
+    }
+
+    private String getJWTFromAuthorization(HttpServletRequest httpRequest) {
+        String header = httpRequest.getHeader("Authorization");
+        return (header != null && header.matches("Bearer (.*)")) ? header.substring(7) : null;
+    }
+
+    /**
+     * Encapsulate the acquisition of the JWT token from HTTP cookies within the
+     * request.
+     *
+     * Taken from
+     *
+     * @param req
+     *            servlet request to get the JWT token from
+     * @return serialized JWT token
+     */
+    protected String getJWTFromCookie(HttpServletRequest req) {
+        String serializedJWT = null;
+        Cookie[] cookies = req.getCookies();
+        if (cookies != null) {
+            for (Cookie cookie : cookies) {
+                LOG.debug(String.format("Found cookie: %s [%s]", cookie.getName(), cookie.getValue()));
+                if (knoxCookie.equals(cookie.getName())) {
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug(knoxCookie + " cookie has been found and is being processed");
+                    }
+                    serializedJWT = cookie.getValue();
+                    break;
+                }
+            }
+        } else {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug(knoxCookie + " not found");
+            }
+        }
+        return serializedJWT;
+    }
+
+    public static RSAPublicKey parseRSAPublicKey(String pem)
+            throws CertificateException, UnsupportedEncodingException, ServletException {
+        String PEM_HEADER = "-----BEGIN CERTIFICATE-----\n";
+        String PEM_FOOTER = "\n-----END CERTIFICATE-----";
+        String fullPem = (pem.startsWith(PEM_HEADER) && pem.endsWith(PEM_FOOTER)) ? pem : PEM_HEADER + pem + PEM_FOOTER;
+        PublicKey key = null;
+        try {
+            CertificateFactory fact = CertificateFactory.getInstance("X.509");
+            ByteArrayInputStream is = new ByteArrayInputStream(fullPem.getBytes("UTF8"));
+            X509Certificate cer = (X509Certificate) fact.generateCertificate(is);
+            key = cer.getPublicKey();
+        } catch (CertificateException ce) {
+            String message = null;
+            if (pem.startsWith(PEM_HEADER)) {
+                message = "CertificateException - be sure not to include PEM header "
+                        + "and footer in the PEM configuration element.";
+            } else {
+                message = "CertificateException - PEM may be corrupt";
+            }
+            throw new ServletException(message, ce);
+        } catch (UnsupportedEncodingException uee) {
+            throw new ServletException(uee);
+        }
+        return (RSAPublicKey) key;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronAuthenticationException.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronAuthenticationException.java b/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronAuthenticationException.java
new file mode 100644
index 0000000..ddf177b
--- /dev/null
+++ b/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronAuthenticationException.java
@@ -0,0 +1,29 @@
+/**
+ * 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.metron.ui;
+
+import org.springframework.security.core.AuthenticationException;
+
+public class MetronAuthenticationException extends AuthenticationException{
+    public MetronAuthenticationException(String msg) {
+        super(msg);
+    }
+
+    private static final long serialVersionUID = 1L;
+
+}