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;
+
+}

Reply via email to