http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
----------------------------------------------------------------------
diff --cc 
gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
index eba98a4,48616c0..da55422
--- 
a/gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
+++ 
b/gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
@@@ -21,13 -21,14 +21,15 @@@ import java.io.File
  import java.security.Principal;
  import java.util.HashMap;
  
 -import org.apache.hadoop.gateway.config.GatewayConfig;
 -import org.apache.hadoop.gateway.services.security.AliasService;
 -import org.apache.hadoop.gateway.services.security.KeystoreService;
 -import org.apache.hadoop.gateway.services.security.MasterService;
 -import 
org.apache.hadoop.gateway.services.security.impl.DefaultKeystoreService;
 -import org.apache.hadoop.gateway.services.security.token.JWTokenAuthority;
 -import 
org.apache.hadoop.gateway.services.security.token.TokenServiceException;
 -import org.apache.hadoop.gateway.services.security.token.impl.JWT;
 +import org.apache.knox.gateway.config.GatewayConfig;
 +import org.apache.knox.gateway.services.security.AliasService;
 +import org.apache.knox.gateway.services.security.KeystoreService;
 +import org.apache.knox.gateway.services.security.MasterService;
 +import org.apache.knox.gateway.services.security.impl.DefaultKeystoreService;
 +import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
 +import org.apache.knox.gateway.services.security.token.impl.JWT;
++import org.apache.knox.gateway.services.security.token.TokenServiceException;
++
  import org.easymock.EasyMock;
  import org.junit.Test;
  

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorFactoryTest.java
----------------------------------------------------------------------
diff --cc 
gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorFactoryTest.java
index efee1d8,0000000..41a7c10
mode 100644,000000..100644
--- 
a/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorFactoryTest.java
+++ 
b/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorFactoryTest.java
@@@ -1,218 -1,0 +1,422 @@@
 +/**
 + * 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.knox.gateway.topology.simple;
 +
 +import java.io.File;
 +import java.io.FileWriter;
 +import java.io.Writer;
 +import java.util.*;
 +
 +import org.junit.Test;
 +import static org.junit.Assert.*;
 +
 +
 +public class SimpleDescriptorFactoryTest {
 +
 +
 +    @Test
 +    public void testParseJSONSimpleDescriptor() throws Exception {
 +
 +        final String   discoveryType    = "AMBARI";
 +        final String   discoveryAddress = 
"http://c6401.ambari.apache.org:8080";;
 +        final String   discoveryUser    = "admin";
 +        final String   providerConfig   = "ambari-cluster-policy.xml";
 +        final String   clusterName      = "myCluster";
 +
 +        final Map<String, List<String>> services = new HashMap<>();
 +        services.put("NODEMANAGER", null);
 +        services.put("JOBTRACKER", null);
 +        services.put("RESOURCEMANAGER", null);
 +        services.put("HIVE", Arrays.asList("http://c6401.ambari.apache.org";, 
"http://c6402.ambari.apache.org";, "http://c6403.ambari.apache.org";));
 +        services.put("AMBARIUI", 
Arrays.asList("http://c6401.ambari.apache.org:8080";));
 +
 +        String fileName = "test-topology.json";
 +        File testJSON = null;
 +        try {
 +            testJSON = writeJSON(fileName, discoveryType, discoveryAddress, 
discoveryUser, providerConfig, clusterName, services);
 +            SimpleDescriptor sd = 
SimpleDescriptorFactory.parse(testJSON.getAbsolutePath());
 +            validateSimpleDescriptor(sd, discoveryType, discoveryAddress, 
providerConfig, clusterName, services);
 +        } catch (Exception e) {
 +            e.printStackTrace();
 +        } finally {
 +            if (testJSON != null) {
 +                try {
 +                    testJSON.delete();
 +                } catch (Exception e) {
 +                    // Ignore
 +                }
 +            }
 +        }
 +    }
 +
 +    @Test
++    public void testParseJSONSimpleDescriptorWithServiceParams() throws 
Exception {
++
++        final String   discoveryType    = "AMBARI";
++        final String   discoveryAddress = 
"http://c6401.ambari.apache.org:8080";;
++        final String   discoveryUser    = "admin";
++        final String   providerConfig   = "ambari-cluster-policy.xml";
++        final String   clusterName      = "myCluster";
++
++        final Map<String, List<String>> services = new HashMap<>();
++        services.put("NODEMANAGER", null);
++        services.put("JOBTRACKER", null);
++        services.put("RESOURCEMANAGER", null);
++        services.put("HIVE", Arrays.asList("http://c6401.ambari.apache.org";, 
"http://c6402.ambari.apache.org";, "http://c6403.ambari.apache.org";));
++        services.put("AMBARIUI", 
Collections.singletonList("http://c6401.ambari.apache.org:8080";));
++        services.put("KNOXSSO", null);
++        services.put("KNOXTOKEN", null);
++        services.put("CustomRole", 
Collections.singletonList("http://c6402.ambari.apache.org:1234";));
++
++        final Map<String, Map<String, String>> serviceParams = new 
HashMap<>();
++        Map<String, String> knoxSSOParams = new HashMap<>();
++        knoxSSOParams.put("knoxsso.cookie.secure.only", "true");
++        knoxSSOParams.put("knoxsso.token.ttl", "100000");
++        serviceParams.put("KNOXSSO", knoxSSOParams);
++
++        Map<String, String> knoxTokenParams = new HashMap<>();
++        knoxTokenParams.put("knox.token.ttl", "36000000");
++        knoxTokenParams.put("knox.token.audiences", "tokenbased");
++        knoxTokenParams.put("knox.token.target.url", 
"https://localhost:8443/gateway/tokenbased";);
++        serviceParams.put("KNOXTOKEN", knoxTokenParams);
++
++        Map<String, String> customRoleParams = new HashMap<>();
++        customRoleParams.put("custom.param.1", "value1");
++        customRoleParams.put("custom.param.2", "value2");
++        serviceParams.put("CustomRole", customRoleParams);
++
++        String fileName = "test-topology.json";
++        File testJSON = null;
++        try {
++            testJSON = writeJSON(fileName,
++                                 discoveryType,
++                                 discoveryAddress,
++                                 discoveryUser,
++                                 providerConfig,
++                                 clusterName,
++                                 services,
++                                 serviceParams);
++            SimpleDescriptor sd = 
SimpleDescriptorFactory.parse(testJSON.getAbsolutePath());
++            validateSimpleDescriptor(sd, discoveryType, discoveryAddress, 
providerConfig, clusterName, services, serviceParams);
++        } catch (Exception e) {
++            e.printStackTrace();
++        } finally {
++            if (testJSON != null) {
++                try {
++                    testJSON.delete();
++                } catch (Exception e) {
++                    // Ignore
++                }
++            }
++        }
++    }
++
++    @Test
 +    public void testParseYAMLSimpleDescriptor() throws Exception {
 +
 +        final String   discoveryType    = "AMBARI";
 +        final String   discoveryAddress = 
"http://c6401.ambari.apache.org:8080";;
 +        final String   discoveryUser    = "joeblow";
 +        final String   providerConfig   = "ambari-cluster-policy.xml";
 +        final String   clusterName      = "myCluster";
 +
 +        final Map<String, List<String>> services = new HashMap<>();
 +        services.put("NODEMANAGER", null);
 +        services.put("JOBTRACKER", null);
 +        services.put("RESOURCEMANAGER", null);
 +        services.put("HIVE", Arrays.asList("http://c6401.ambari.apache.org";, 
"http://c6402.ambari.apache.org";, "http://c6403.ambari.apache.org";));
 +        services.put("AMBARIUI", 
Arrays.asList("http://c6401.ambari.apache.org:8080";));
 +
 +        String fileName = "test-topology.yml";
 +        File testYAML = null;
 +        try {
 +            testYAML = writeYAML(fileName, discoveryType, discoveryAddress, 
discoveryUser, providerConfig, clusterName, services);
 +            SimpleDescriptor sd = 
SimpleDescriptorFactory.parse(testYAML.getAbsolutePath());
 +            validateSimpleDescriptor(sd, discoveryType, discoveryAddress, 
providerConfig, clusterName, services);
 +        } catch (Exception e) {
 +            e.printStackTrace();
 +        } finally {
 +            if (testYAML != null) {
 +                try {
 +                    testYAML.delete();
 +                } catch (Exception e) {
 +                    // Ignore
 +                }
 +            }
 +        }
 +    }
 +
 +
-     private void validateSimpleDescriptor(SimpleDescriptor    sd,
-                                           String              discoveryType,
-                                           String              
discoveryAddress,
-                                           String              providerConfig,
-                                           String              clusterName,
++    @Test
++    public void testParseYAMLSimpleDescriptorWithServiceParams() throws 
Exception {
++
++        final String   discoveryType    = "AMBARI";
++        final String   discoveryAddress = 
"http://c6401.ambari.apache.org:8080";;
++        final String   discoveryUser    = "joeblow";
++        final String   providerConfig   = "ambari-cluster-policy.xml";
++        final String   clusterName      = "myCluster";
++
++        final Map<String, List<String>> services = new HashMap<>();
++        services.put("NODEMANAGER", null);
++        services.put("JOBTRACKER", null);
++        services.put("RESOURCEMANAGER", null);
++        services.put("HIVE", Arrays.asList("http://c6401.ambari.apache.org";, 
"http://c6402.ambari.apache.org";, "http://c6403.ambari.apache.org";));
++        services.put("AMBARIUI", 
Arrays.asList("http://c6401.ambari.apache.org:8080";));
++        services.put("KNOXSSO", null);
++        services.put("KNOXTOKEN", null);
++        services.put("CustomRole", 
Collections.singletonList("http://c6402.ambari.apache.org:1234";));
++
++        final Map<String, Map<String, String>> serviceParams = new 
HashMap<>();
++        Map<String, String> knoxSSOParams = new HashMap<>();
++        knoxSSOParams.put("knoxsso.cookie.secure.only", "true");
++        knoxSSOParams.put("knoxsso.token.ttl", "100000");
++        serviceParams.put("KNOXSSO", knoxSSOParams);
++
++        Map<String, String> knoxTokenParams = new HashMap<>();
++        knoxTokenParams.put("knox.token.ttl", "36000000");
++        knoxTokenParams.put("knox.token.audiences", "tokenbased");
++        knoxTokenParams.put("knox.token.target.url", 
"https://localhost:8443/gateway/tokenbased";);
++        serviceParams.put("KNOXTOKEN", knoxTokenParams);
++
++        Map<String, String> customRoleParams = new HashMap<>();
++        customRoleParams.put("custom.param.1", "value1");
++        customRoleParams.put("custom.param.2", "value2");
++        serviceParams.put("CustomRole", customRoleParams);
++
++        String fileName = "test-topology.yml";
++        File testYAML = null;
++        try {
++            testYAML = writeYAML(fileName, discoveryType, discoveryAddress, 
discoveryUser, providerConfig, clusterName, services, serviceParams);
++            SimpleDescriptor sd = 
SimpleDescriptorFactory.parse(testYAML.getAbsolutePath());
++            validateSimpleDescriptor(sd, discoveryType, discoveryAddress, 
providerConfig, clusterName, services, serviceParams);
++        } catch (Exception e) {
++            e.printStackTrace();
++        } finally {
++            if (testYAML != null) {
++                try {
++                    testYAML.delete();
++                } catch (Exception e) {
++                    // Ignore
++                }
++            }
++        }
++    }
++
++
++    private void validateSimpleDescriptor(SimpleDescriptor          sd,
++                                          String                    
discoveryType,
++                                          String                    
discoveryAddress,
++                                          String                    
providerConfig,
++                                          String                    
clusterName,
 +                                          Map<String, List<String>> 
expectedServices) {
++        validateSimpleDescriptor(sd, discoveryType, discoveryAddress, 
providerConfig, clusterName, expectedServices, null);
++    }
++
++
++    private void validateSimpleDescriptor(SimpleDescriptor                 sd,
++                                          String                           
discoveryType,
++                                          String                           
discoveryAddress,
++                                          String                           
providerConfig,
++                                          String                           
clusterName,
++                                          Map<String, List<String>>        
expectedServices,
++                                          Map<String, Map<String, String>> 
expectedServiceParameters) {
 +        assertNotNull(sd);
 +        assertEquals(discoveryType, sd.getDiscoveryType());
 +        assertEquals(discoveryAddress, sd.getDiscoveryAddress());
 +        assertEquals(providerConfig, sd.getProviderConfig());
 +        assertEquals(clusterName, sd.getClusterName());
 +
 +        List<SimpleDescriptor.Service> actualServices = sd.getServices();
 +
 +        assertEquals(expectedServices.size(), actualServices.size());
 +
 +        for (SimpleDescriptor.Service actualService : actualServices) {
 +            assertTrue(expectedServices.containsKey(actualService.getName()));
 +            assertEquals(expectedServices.get(actualService.getName()), 
actualService.getURLs());
++
++            // Validate service parameters
++            if (expectedServiceParameters != null) {
++                if 
(expectedServiceParameters.containsKey(actualService.getName())) {
++                    Map<String, String> expectedParams = 
expectedServiceParameters.get(actualService.getName());
++
++                    Map<String, String> actualServiceParams = 
actualService.getParams();
++                    assertNotNull(actualServiceParams);
++
++                    // Validate the size of the service parameter set
++                    assertEquals(expectedParams.size(), 
actualServiceParams.size());
++
++                    // Validate the parameter contents
++                    for (String paramName : actualServiceParams.keySet()) {
++                        assertTrue(expectedParams.containsKey(paramName));
++                        assertEquals(expectedParams.get(paramName), 
actualServiceParams.get(paramName));
++                    }
++                }
++            }
 +        }
 +    }
 +
 +
 +    private File writeJSON(String path, String content) throws Exception {
 +        File f = new File(path);
 +
 +        Writer fw = new FileWriter(f);
 +        fw.write(content);
 +        fw.flush();
 +        fw.close();
 +
 +        return f;
 +    }
 +
 +
 +    private File writeJSON(String path,
 +                           String discoveryType,
 +                           String discoveryAddress,
 +                           String discoveryUser,
 +                           String providerConfig,
 +                           String clusterName,
 +                           Map<String, List<String>> services) throws 
Exception {
++        return writeJSON(path, discoveryType, discoveryAddress, 
discoveryUser, providerConfig, clusterName, services, null);
++    }
++
++    private File writeJSON(String path,
++                           String discoveryType,
++                           String discoveryAddress,
++                           String discoveryUser,
++                           String providerConfig,
++                           String clusterName,
++                           Map<String, List<String>> services,
++                           Map<String, Map<String, String>> serviceParams) 
throws Exception {
 +        File f = new File(path);
 +
 +        Writer fw = new FileWriter(f);
 +        fw.write("{" + "\n");
 +        fw.write("\"discovery-type\":\"" + discoveryType + "\",\n");
 +        fw.write("\"discovery-address\":\"" + discoveryAddress + "\",\n");
 +        fw.write("\"discovery-user\":\"" + discoveryUser + "\",\n");
 +        fw.write("\"provider-config-ref\":\"" + providerConfig + "\",\n");
 +        fw.write("\"cluster\":\"" + clusterName + "\",\n");
 +        fw.write("\"services\":[\n");
 +
 +        int i = 0;
 +        for (String name : services.keySet()) {
 +            fw.write("{\"name\":\"" + name + "\"");
++
++            // Service params
++            if (serviceParams != null && !serviceParams.isEmpty()) {
++                Map<String, String> params = serviceParams.get(name);
++                if (params != null && !params.isEmpty()) {
++                    fw.write(",\n\"params\":{\n");
++                    Iterator<String> paramNames = params.keySet().iterator();
++                    while (paramNames.hasNext()) {
++                        String paramName = paramNames.next();
++                        String paramValue = params.get(paramName);
++                        fw.write("\"" + paramName + "\":\"" + paramValue + 
"\"");
++                        fw.write(paramNames.hasNext() ? ",\n" : "");
++                    }
++                    fw.write("\n}");
++                }
++            }
++
++            // Service URLs
 +            List<String> urls = services.get(name);
 +            if (urls != null) {
-                 fw.write(", \"urls\":[");
++                fw.write(",\n\"urls\":[");
 +                Iterator<String> urlIter = urls.iterator();
 +                while (urlIter.hasNext()) {
 +                    fw.write("\"" + urlIter.next() + "\"");
 +                    if (urlIter.hasNext()) {
 +                        fw.write(", ");
 +                    }
 +                }
-                 fw.write("]");
++                fw.write("]\n");
 +            }
++
 +            fw.write("}");
 +            if (i++ < services.size() - 1) {
 +                fw.write(",");
 +            }
 +            fw.write("\n");
 +        }
 +        fw.write("]\n");
 +        fw.write("}\n");
 +        fw.flush();
 +        fw.close();
 +
 +        return f;
 +    }
 +
-     private File writeYAML(String path,
-                            String discoveryType,
-                            String discoveryAddress,
-                            String discoveryUser,
-                            String providerConfig,
-                            String clusterName,
++
++    private File writeYAML(String                    path,
++                           String                    discoveryType,
++                           String                    discoveryAddress,
++                           String                    discoveryUser,
++                           String                    providerConfig,
++                           String                    clusterName,
 +                           Map<String, List<String>> services) throws 
Exception {
++        return writeYAML(path, discoveryType, discoveryAddress, 
discoveryUser, providerConfig, clusterName, services, null);
++    }
++
++
++    private File writeYAML(String                           path,
++                           String                           discoveryType,
++                           String                           discoveryAddress,
++                           String                           discoveryUser,
++                           String                           providerConfig,
++                           String                           clusterName,
++                           Map<String, List<String>>        services,
++                           Map<String, Map<String, String>> serviceParams) 
throws Exception {
 +        File f = new File(path);
 +
 +        Writer fw = new FileWriter(f);
 +        fw.write("---" + "\n");
 +        fw.write("discovery-type: " + discoveryType + "\n");
 +        fw.write("discovery-address: " + discoveryAddress + "\n");
 +        fw.write("discovery-user: " + discoveryUser + "\n");
 +        fw.write("provider-config-ref: " + providerConfig + "\n");
 +        fw.write("cluster: " + clusterName+ "\n");
 +        fw.write("services:\n");
 +        for (String name : services.keySet()) {
 +            fw.write("    - name: " + name + "\n");
++
++            // Service params
++            if (serviceParams != null && !serviceParams.isEmpty()) {
++                if (serviceParams.containsKey(name)) {
++                    Map<String, String> params = serviceParams.get(name);
++                    fw.write("      params:\n");
++                    for (String paramName : params.keySet()) {
++                        fw.write("            " + paramName + ": " + 
params.get(paramName) + "\n");
++                    }
++                }
++            }
++
++            // Service URLs
 +            List<String> urls = services.get(name);
 +            if (urls != null) {
 +                fw.write("      urls:\n");
 +                for (String url : urls) {
 +                    fw.write("          - " + url + "\n");
 +                }
 +            }
 +        }
 +        fw.flush();
 +        fw.close();
 +
 +        return f;
 +    }
 +
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
----------------------------------------------------------------------
diff --cc 
gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
index b5558fd,0000000..a0c977a
mode 100644,000000..100644
--- 
a/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
+++ 
b/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
@@@ -1,392 -1,0 +1,447 @@@
 +/**
 + * 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.knox.gateway.topology.simple;
 +
 +import org.apache.knox.gateway.topology.validation.TopologyValidator;
 +import org.apache.knox.gateway.util.XmlUtils;
 +import java.io.ByteArrayInputStream;
 +import java.io.File;
++import java.io.FileNotFoundException;
 +import java.io.FileOutputStream;
 +import java.io.IOException;
 +
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Properties;
 +
 +import javax.xml.xpath.XPath;
 +import javax.xml.xpath.XPathConstants;
 +import javax.xml.xpath.XPathFactory;
 +
 +import org.apache.commons.io.FileUtils;
 +import org.easymock.EasyMock;
 +import org.junit.Test;
 +import org.w3c.dom.Document;
 +import org.w3c.dom.Node;
 +import org.w3c.dom.NodeList;
 +import org.xml.sax.SAXException;
 +
 +import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertNotNull;
 +import static org.junit.Assert.assertTrue;
 +import static org.junit.Assert.fail;
 +
 +
 +public class SimpleDescriptorHandlerTest {
 +
 +    private static final String TEST_PROVIDER_CONFIG =
 +            "    <gateway>\n" +
 +                    "        <provider>\n" +
 +                    "            <role>authentication</role>\n" +
 +                    "            <name>ShiroProvider</name>\n" +
 +                    "            <enabled>true</enabled>\n" +
 +                    "            <param>\n" +
 +                    "                <!-- \n" +
 +                    "                session timeout in minutes,  this is 
really idle timeout,\n" +
 +                    "                defaults to 30mins, if the property 
value is not defined,, \n" +
 +                    "                current client authentication would 
expire if client idles contiuosly for more than this value\n" +
 +                    "                -->\n" +
 +                    "                <name>sessionTimeout</name>\n" +
 +                    "                <value>30</value>\n" +
 +                    "            </param>\n" +
 +                    "            <param>\n" +
 +                    "                <name>main.ldapRealm</name>\n" +
 +                    "                
<value>org.apache.knox.gateway.shirorealm.KnoxLdapRealm</value>\n" +
 +                    "            </param>\n" +
 +                    "            <param>\n" +
 +                    "                <name>main.ldapContextFactory</name>\n" +
 +                    "                
<value>org.apache.knox.gateway.shirorealm.KnoxLdapContextFactory</value>\n" +
 +                    "            </param>\n" +
 +                    "            <param>\n" +
 +                    "                
<name>main.ldapRealm.contextFactory</name>\n" +
 +                    "                <value>$ldapContextFactory</value>\n" +
 +                    "            </param>\n" +
 +                    "            <param>\n" +
 +                    "                
<name>main.ldapRealm.userDnTemplate</name>\n" +
 +                    "                
<value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>\n" +
 +                    "            </param>\n" +
 +                    "            <param>\n" +
 +                    "                
<name>main.ldapRealm.contextFactory.url</name>\n" +
 +                    "                <value>ldap://localhost:33389</value>\n" 
+
 +                    "            </param>\n" +
 +                    "            <param>\n" +
 +                    "                
<name>main.ldapRealm.contextFactory.authenticationMechanism</name>\n" +
 +                    "                <value>simple</value>\n" +
 +                    "            </param>\n" +
 +                    "            <param>\n" +
 +                    "                <name>urls./**</name>\n" +
 +                    "                <value>authcBasic</value>\n" +
 +                    "            </param>\n" +
 +                    "        </provider>\n" +
 +                    "\n" +
 +                    "        <provider>\n" +
 +                    "            <role>identity-assertion</role>\n" +
 +                    "            <name>Default</name>\n" +
 +                    "            <enabled>true</enabled>\n" +
 +                    "        </provider>\n" +
 +                    "\n" +
 +                    "        <!--\n" +
 +                    "        Defines rules for mapping host names internal to 
a Hadoop cluster to externally accessible host names.\n" +
 +                    "        For example, a hadoop service running in AWS may 
return a response that includes URLs containing the\n" +
 +                    "        some AWS internal host name.  If the client 
needs to make a subsequent request to the host identified\n" +
 +                    "        in those URLs they need to be mapped to external 
host names that the client Knox can use to connect.\n" +
 +                    "\n" +
 +                    "        If the external hostname and internal host names 
are same turn of this provider by setting the value of\n" +
 +                    "        enabled parameter as false.\n" +
 +                    "\n" +
 +                    "        The name parameter specifies the external host 
names in a comma separated list.\n" +
 +                    "        The value parameter specifies corresponding 
internal host names in a comma separated list.\n" +
 +                    "\n" +
 +                    "        Note that when you are using Sandbox, the 
external hostname needs to be localhost, as seen in out\n" +
 +                    "        of box sandbox.xml.  This is because Sandbox 
uses port mapping to allow clients to connect to the\n" +
 +                    "        Hadoop services using localhost.  In real 
clusters, external host names would almost never be localhost.\n" +
 +                    "        -->\n" +
 +                    "        <provider>\n" +
 +                    "            <role>hostmap</role>\n" +
 +                    "            <name>static</name>\n" +
 +                    "            <enabled>true</enabled>\n" +
 +                    "            
<param><name>localhost</name><value>sandbox,sandbox.hortonworks.com</value></param>\n"
 +
 +                    "        </provider>\n" +
 +                    "    </gateway>\n";
 +
 +
 +    /**
 +     * KNOX-1006
 +     *
-      * N.B. This test depends on the DummyServiceDiscovery extension being 
configured:
-      *             
org.apache.knox.gateway.topology.discovery.test.extension.DummyServiceDiscovery
++     * N.B. This test depends on the PropertiesFileServiceDiscovery extension 
being configured:
++     *             
org.apache.knox.gateway.topology.discovery.test.extension.PropertiesFileServiceDiscovery
 +     */
 +    @Test
 +    public void testSimpleDescriptorHandler() throws Exception {
 +
-         final String type = "DUMMY";
-         final String address = "http://c6401.ambari.apache.org:8080";;
++        final String type = "PROPERTIES_FILE";
 +        final String clusterName = "dummy";
++
++        // Create a properties file to be the source of service discovery 
details for this test
++        final File discoveryConfig = File.createTempFile(getClass().getName() 
+ "_discovery-config", ".properties");
++
++        final String address = discoveryConfig.getAbsolutePath();
++
++        final Properties DISCOVERY_PROPERTIES = new Properties();
++        DISCOVERY_PROPERTIES.setProperty(clusterName + ".name", clusterName);
++        DISCOVERY_PROPERTIES.setProperty(clusterName + ".NAMENODE", 
"hdfs://namenodehost:8020");
++        DISCOVERY_PROPERTIES.setProperty(clusterName + ".JOBTRACKER", 
"rpc://jobtrackerhostname:8050");
++        DISCOVERY_PROPERTIES.setProperty(clusterName + ".WEBHDFS", 
"http://webhdfshost:1234";);
++        DISCOVERY_PROPERTIES.setProperty(clusterName + ".WEBHCAT", 
"http://webhcathost:50111/templeton";);
++        DISCOVERY_PROPERTIES.setProperty(clusterName + ".OOZIE", 
"http://ooziehost:11000/oozie";);
++        DISCOVERY_PROPERTIES.setProperty(clusterName + ".WEBHBASE", 
"http://webhbasehost:1234";);
++        DISCOVERY_PROPERTIES.setProperty(clusterName + ".HIVE", 
"http://hivehostname:10001/clipath";);
++        DISCOVERY_PROPERTIES.setProperty(clusterName + ".RESOURCEMANAGER", 
"http://remanhost:8088/ws";);
++
++        try {
++            DISCOVERY_PROPERTIES.store(new FileOutputStream(discoveryConfig), 
null);
++        } catch (FileNotFoundException e) {
++            fail(e.getMessage());
++        }
++
 +        final Map<String, List<String>> serviceURLs = new HashMap<>();
 +        serviceURLs.put("NAMENODE", null);
 +        serviceURLs.put("JOBTRACKER", null);
 +        serviceURLs.put("WEBHDFS", null);
 +        serviceURLs.put("WEBHCAT", null);
 +        serviceURLs.put("OOZIE", null);
 +        serviceURLs.put("WEBHBASE", null);
 +        serviceURLs.put("HIVE", null);
 +        serviceURLs.put("RESOURCEMANAGER", null);
 +        serviceURLs.put("AMBARIUI", 
Collections.singletonList("http://c6401.ambari.apache.org:8080";));
++        serviceURLs.put("KNOXSSO", null);
 +
 +        // Write the externalized provider config to a temp file
-         File providerConfig = 
writeProviderConfig("ambari-cluster-policy.xml", TEST_PROVIDER_CONFIG);
++        File providerConfig = new File(System.getProperty("java.io.tmpdir"), 
"ambari-cluster-policy.xml");
++        FileUtils.write(providerConfig, TEST_PROVIDER_CONFIG);
 +
 +        File topologyFile = null;
 +        try {
-             File destDir = (new File(".")).getCanonicalFile();
++            File destDir = new 
File(System.getProperty("java.io.tmpdir")).getCanonicalFile();
++
++            Map<String, Map<String, String>> serviceParameters = new 
HashMap<>();
++            Map<String, String> knoxssoParams = new HashMap<>();
++            knoxssoParams.put("knoxsso.cookie.secure.only", "true");
++            knoxssoParams.put("knoxsso.token.ttl", "100000");
++            serviceParameters.put("KNOXSSO", knoxssoParams);
 +
 +            // Mock out the simple descriptor
 +            SimpleDescriptor testDescriptor = 
EasyMock.createNiceMock(SimpleDescriptor.class);
 +            
EasyMock.expect(testDescriptor.getName()).andReturn("mysimpledescriptor").anyTimes();
 +            
EasyMock.expect(testDescriptor.getDiscoveryAddress()).andReturn(address).anyTimes();
 +            
EasyMock.expect(testDescriptor.getDiscoveryType()).andReturn(type).anyTimes();
 +            
EasyMock.expect(testDescriptor.getDiscoveryUser()).andReturn(null).anyTimes();
 +            
EasyMock.expect(testDescriptor.getProviderConfig()).andReturn(providerConfig.getAbsolutePath()).anyTimes();
 +            
EasyMock.expect(testDescriptor.getClusterName()).andReturn(clusterName).anyTimes();
 +            List<SimpleDescriptor.Service> serviceMocks = new ArrayList<>();
 +            for (String serviceName : serviceURLs.keySet()) {
 +                SimpleDescriptor.Service svc = 
EasyMock.createNiceMock(SimpleDescriptor.Service.class);
 +                
EasyMock.expect(svc.getName()).andReturn(serviceName).anyTimes();
 +                
EasyMock.expect(svc.getURLs()).andReturn(serviceURLs.get(serviceName)).anyTimes();
++                
EasyMock.expect(svc.getParams()).andReturn(serviceParameters.get(serviceName)).anyTimes();
 +                EasyMock.replay(svc);
 +                serviceMocks.add(svc);
 +            }
 +            
EasyMock.expect(testDescriptor.getServices()).andReturn(serviceMocks).anyTimes();
 +            EasyMock.replay(testDescriptor);
 +
 +            // Invoke the simple descriptor handler
 +            Map<String, File> files =
 +                           SimpleDescriptorHandler.handle(testDescriptor,
 +                                                          
providerConfig.getParentFile(), // simple desc co-located with provider config
 +                                                          destDir);
 +            topologyFile = files.get("topology");
 +
 +            // Validate the resulting topology descriptor
 +            assertTrue(topologyFile.exists());
 +
 +            // Validate the topology descriptor's correctness
 +            TopologyValidator validator = new TopologyValidator( 
topologyFile.getAbsolutePath() );
 +            if( !validator.validateTopology() ){
 +                throw new SAXException( validator.getErrorString() );
 +            }
 +
 +            XPathFactory xPathfactory = XPathFactory.newInstance();
 +            XPath xpath = xPathfactory.newXPath();
 +
 +            // Parse the topology descriptor
 +            Document topologyXml = XmlUtils.readXml(topologyFile);
 +
 +            // Validate the provider configuration
 +            Document extProviderConf = XmlUtils.readXml(new 
ByteArrayInputStream(TEST_PROVIDER_CONFIG.getBytes()));
 +            Node gatewayNode = (Node) 
xpath.compile("/topology/gateway").evaluate(topologyXml, XPathConstants.NODE);
 +            assertTrue("Resulting provider config should be identical to the 
referenced content.",
 +                       
extProviderConf.getDocumentElement().isEqualNode(gatewayNode));
 +
 +            // Validate the service declarations
 +            Map<String, List<String>> topologyServiceURLs = new HashMap<>();
 +            NodeList serviceNodes =
 +                        (NodeList) 
xpath.compile("/topology/service").evaluate(topologyXml, 
XPathConstants.NODESET);
 +            for (int serviceNodeIndex=0; serviceNodeIndex < 
serviceNodes.getLength(); serviceNodeIndex++) {
 +                Node serviceNode = serviceNodes.item(serviceNodeIndex);
++
++                // Validate the role
 +                Node roleNode = (Node) 
xpath.compile("role/text()").evaluate(serviceNode, XPathConstants.NODE);
 +                assertNotNull(roleNode);
 +                String role = roleNode.getNodeValue();
++
++                // Validate the URLs
 +                NodeList urlNodes = (NodeList) 
xpath.compile("url/text()").evaluate(serviceNode, XPathConstants.NODESET);
 +                for(int urlNodeIndex = 0 ; urlNodeIndex < 
urlNodes.getLength(); urlNodeIndex++) {
 +                    Node urlNode = urlNodes.item(urlNodeIndex);
 +                    assertNotNull(urlNode);
 +                    String url = urlNode.getNodeValue();
-                     assertNotNull("Every declared service should have a 
URL.", url);
-                     if (!topologyServiceURLs.containsKey(role)) {
-                         topologyServiceURLs.put(role, new 
ArrayList<String>());
++
++                    // If the service should have a URL (some don't require 
it)
++                    if (serviceURLs.containsKey(role)) {
++                        assertNotNull("Declared service should have a URL.", 
url);
++                        if (!topologyServiceURLs.containsKey(role)) {
++                            topologyServiceURLs.put(role, new ArrayList<>());
++                        }
++                        topologyServiceURLs.get(role).add(url); // Add it for 
validation later
 +                    }
-                     topologyServiceURLs.get(role).add(url);
 +                }
++
++                // If params were declared in the descriptor, then validate 
them in the resulting topology file
++                Map<String, String> params = serviceParameters.get(role);
++                if (params != null) {
++                    NodeList paramNodes = (NodeList) 
xpath.compile("param").evaluate(serviceNode, XPathConstants.NODESET);
++                    for (int paramNodeIndex = 0; paramNodeIndex < 
paramNodes.getLength(); paramNodeIndex++) {
++                        Node paramNode = paramNodes.item(paramNodeIndex);
++                        String paramName = (String) 
xpath.compile("name/text()").evaluate(paramNode, XPathConstants.STRING);
++                        String paramValue = (String) 
xpath.compile("value/text()").evaluate(paramNode, XPathConstants.STRING);
++                        assertTrue(params.keySet().contains(paramName));
++                        assertEquals(params.get(paramName), paramValue);
++                    }
++                }
++
 +            }
-             assertEquals("Unexpected number of service declarations.", 
serviceURLs.size(), topologyServiceURLs.size());
++            assertEquals("Unexpected number of service declarations.", 
(serviceURLs.size() - 1), topologyServiceURLs.size());
 +
 +        } catch (Exception e) {
 +            e.printStackTrace();
 +            fail(e.getMessage());
 +        } finally {
 +            providerConfig.delete();
++            discoveryConfig.delete();
 +            if (topologyFile != null) {
 +                topologyFile.delete();
 +            }
 +        }
 +    }
 +
 +
 +    /**
 +     * KNOX-1006
 +     *
 +     * Verify the behavior of the SimpleDescriptorHandler when service 
discovery fails to produce a valid URL for
 +     * a service.
 +     *
 +     * N.B. This test depends on the PropertiesFileServiceDiscovery extension 
being configured:
 +     *             
org.apache.hadoop.gateway.topology.discovery.test.extension.PropertiesFileServiceDiscovery
 +     */
 +    @Test
 +    public void testInvalidServiceURLFromDiscovery() throws Exception {
 +        final String CLUSTER_NAME = "myproperties";
 +
 +        // Configure the PropertiesFile Service Discovery implementation for 
this test
 +        final String DEFAULT_VALID_SERVICE_URL = 
"http://localhost:9999/thiswillwork";;
 +        Properties serviceDiscoverySourceProps = new Properties();
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".NAMENODE",
 +                                                
DEFAULT_VALID_SERVICE_URL.replace("http", "hdfs"));
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".JOBTRACKER",
 +                                                
DEFAULT_VALID_SERVICE_URL.replace("http", "rpc"));
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".WEBHDFS",    
     DEFAULT_VALID_SERVICE_URL);
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".WEBHCAT",    
     DEFAULT_VALID_SERVICE_URL);
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".OOZIE",      
     DEFAULT_VALID_SERVICE_URL);
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".WEBHBASE",   
     DEFAULT_VALID_SERVICE_URL);
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".HIVE",       
     "{SCHEME}://localhost:10000/");
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + 
".RESOURCEMANAGER", DEFAULT_VALID_SERVICE_URL);
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".AMBARIUI",   
     DEFAULT_VALID_SERVICE_URL);
 +        File serviceDiscoverySource = 
File.createTempFile("service-discovery", ".properties");
 +        serviceDiscoverySourceProps.store(new 
FileOutputStream(serviceDiscoverySource),
 +                                          "Test Service Discovery Source");
 +
 +        // Prepare a mock SimpleDescriptor
 +        final String type = "PROPERTIES_FILE";
 +        final String address = serviceDiscoverySource.getAbsolutePath();
 +        final Map<String, List<String>> serviceURLs = new HashMap<>();
 +        serviceURLs.put("NAMENODE", null);
 +        serviceURLs.put("JOBTRACKER", null);
 +        serviceURLs.put("WEBHDFS", null);
 +        serviceURLs.put("WEBHCAT", null);
 +        serviceURLs.put("OOZIE", null);
 +        serviceURLs.put("WEBHBASE", null);
 +        serviceURLs.put("HIVE", null);
 +        serviceURLs.put("RESOURCEMANAGER", null);
 +        serviceURLs.put("AMBARIUI", 
Collections.singletonList("http://c6401.ambari.apache.org:8080";));
 +
 +        // Write the externalized provider config to a temp file
 +        File providerConfig = 
writeProviderConfig("ambari-cluster-policy.xml", TEST_PROVIDER_CONFIG);
 +
 +        File topologyFile = null;
 +        try {
 +            File destDir = (new File(".")).getCanonicalFile();
 +
 +            // Mock out the simple descriptor
 +            SimpleDescriptor testDescriptor = 
EasyMock.createNiceMock(SimpleDescriptor.class);
 +            
EasyMock.expect(testDescriptor.getName()).andReturn("mysimpledescriptor").anyTimes();
 +            
EasyMock.expect(testDescriptor.getDiscoveryAddress()).andReturn(address).anyTimes();
 +            
EasyMock.expect(testDescriptor.getDiscoveryType()).andReturn(type).anyTimes();
 +            
EasyMock.expect(testDescriptor.getDiscoveryUser()).andReturn(null).anyTimes();
 +            
EasyMock.expect(testDescriptor.getProviderConfig()).andReturn(providerConfig.getAbsolutePath()).anyTimes();
 +            
EasyMock.expect(testDescriptor.getClusterName()).andReturn(CLUSTER_NAME).anyTimes();
 +            List<SimpleDescriptor.Service> serviceMocks = new ArrayList<>();
 +            for (String serviceName : serviceURLs.keySet()) {
 +                SimpleDescriptor.Service svc = 
EasyMock.createNiceMock(SimpleDescriptor.Service.class);
 +                
EasyMock.expect(svc.getName()).andReturn(serviceName).anyTimes();
 +                
EasyMock.expect(svc.getURLs()).andReturn(serviceURLs.get(serviceName)).anyTimes();
 +                EasyMock.replay(svc);
 +                serviceMocks.add(svc);
 +            }
 +            
EasyMock.expect(testDescriptor.getServices()).andReturn(serviceMocks).anyTimes();
 +            EasyMock.replay(testDescriptor);
 +
 +            // Invoke the simple descriptor handler
 +            Map<String, File> files =
 +                    SimpleDescriptorHandler.handle(testDescriptor,
 +                                                   
providerConfig.getParentFile(), // simple desc co-located with provider config
 +                                                   destDir);
 +
 +            topologyFile = files.get("topology");
 +
 +            // Validate the resulting topology descriptor
 +            assertTrue(topologyFile.exists());
 +
 +            // Validate the topology descriptor's correctness
 +            TopologyValidator validator = new TopologyValidator( 
topologyFile.getAbsolutePath() );
 +            if( !validator.validateTopology() ){
 +                throw new SAXException( validator.getErrorString() );
 +            }
 +
 +            XPathFactory xPathfactory = XPathFactory.newInstance();
 +            XPath xpath = xPathfactory.newXPath();
 +
 +            // Parse the topology descriptor
 +            Document topologyXml = XmlUtils.readXml(topologyFile);
 +
 +            // Validate the provider configuration
 +            Document extProviderConf = XmlUtils.readXml(new 
ByteArrayInputStream(TEST_PROVIDER_CONFIG.getBytes()));
 +            Node gatewayNode = (Node) 
xpath.compile("/topology/gateway").evaluate(topologyXml, XPathConstants.NODE);
 +            assertTrue("Resulting provider config should be identical to the 
referenced content.",
 +                    
extProviderConf.getDocumentElement().isEqualNode(gatewayNode));
 +
 +            // Validate the service declarations
 +            List<String> topologyServices = new ArrayList<>();
 +            Map<String, List<String>> topologyServiceURLs = new HashMap<>();
 +            NodeList serviceNodes =
 +                    (NodeList) 
xpath.compile("/topology/service").evaluate(topologyXml, 
XPathConstants.NODESET);
 +            for (int serviceNodeIndex=0; serviceNodeIndex < 
serviceNodes.getLength(); serviceNodeIndex++) {
 +                Node serviceNode = serviceNodes.item(serviceNodeIndex);
 +                Node roleNode = (Node) 
xpath.compile("role/text()").evaluate(serviceNode, XPathConstants.NODE);
 +                assertNotNull(roleNode);
 +                String role = roleNode.getNodeValue();
 +                topologyServices.add(role);
 +                NodeList urlNodes = (NodeList) 
xpath.compile("url/text()").evaluate(serviceNode, XPathConstants.NODESET);
 +                for(int urlNodeIndex = 0 ; urlNodeIndex < 
urlNodes.getLength(); urlNodeIndex++) {
 +                    Node urlNode = urlNodes.item(urlNodeIndex);
 +                    assertNotNull(urlNode);
 +                    String url = urlNode.getNodeValue();
 +                    assertNotNull("Every declared service should have a 
URL.", url);
 +                    if (!topologyServiceURLs.containsKey(role)) {
-                         topologyServiceURLs.put(role, new 
ArrayList<String>());
++                        topologyServiceURLs.put(role, new ArrayList<>());
 +                    }
 +                    topologyServiceURLs.get(role).add(url);
 +                }
 +            }
 +
 +            // There should not be a service element for HIVE, since it had 
no valid URLs
 +            assertEquals("Unexpected number of service declarations.", 
serviceURLs.size() - 1, topologyServices.size());
 +            assertFalse("The HIVE service should have been omitted from the 
generated topology.", topologyServices.contains("HIVE"));
 +
 +            assertEquals("Unexpected number of service URLs.", 
serviceURLs.size() - 1, topologyServiceURLs.size());
 +
 +        } catch (Exception e) {
 +            e.printStackTrace();
 +            fail(e.getMessage());
 +        } finally {
 +            serviceDiscoverySource.delete();
 +            providerConfig.delete();
 +            if (topologyFile != null) {
 +                topologyFile.delete();
 +            }
 +        }
 +    }
 +
 +
 +    private File writeProviderConfig(String path, String content) throws 
IOException {
 +        File f = new File(path);
 +        FileUtils.write(f, content);
 +        return f;
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/58780d37/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
----------------------------------------------------------------------
diff --cc 
gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
index a97cee2,0000000..a103dac
mode 100644,000000..100644
--- 
a/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
+++ 
b/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
@@@ -1,322 -1,0 +1,334 @@@
 +/**
 + * 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.knox.gateway.service.knoxsso;
 +
 +import java.io.IOException;
 +import java.net.URI;
 +import java.net.URISyntaxException;
 +import java.security.Principal;
 +import java.util.ArrayList;
 +import java.util.Arrays;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Map.Entry;
 +
 +import javax.annotation.PostConstruct;
 +import javax.servlet.ServletContext;
 +import javax.servlet.http.Cookie;
 +import javax.servlet.http.HttpServletRequest;
 +import javax.servlet.http.HttpServletResponse;
 +import javax.servlet.http.HttpSession;
 +import javax.ws.rs.GET;
 +import javax.ws.rs.POST;
 +import javax.ws.rs.Path;
 +import javax.ws.rs.Produces;
 +import javax.ws.rs.core.Context;
 +import javax.ws.rs.core.Response;
 +import javax.ws.rs.WebApplicationException;
 +
 +import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 +import org.apache.knox.gateway.services.GatewayServices;
 +import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
 +import org.apache.knox.gateway.services.security.token.TokenServiceException;
 +import org.apache.knox.gateway.services.security.token.impl.JWT;
 +import org.apache.knox.gateway.util.RegExUtils;
 +import org.apache.knox.gateway.util.Urls;
 +
 +import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 +import static javax.ws.rs.core.MediaType.APPLICATION_XML;
 +
 +@Path( WebSSOResource.RESOURCE_PATH )
 +public class WebSSOResource {
 +  private static final String SSO_COOKIE_NAME = "knoxsso.cookie.name";
 +  private static final String SSO_COOKIE_SECURE_ONLY_INIT_PARAM = 
"knoxsso.cookie.secure.only";
 +  private static final String SSO_COOKIE_MAX_AGE_INIT_PARAM = 
"knoxsso.cookie.max.age";
 +  private static final String SSO_COOKIE_DOMAIN_SUFFIX_PARAM = 
"knoxsso.cookie.domain.suffix";
 +  private static final String SSO_COOKIE_TOKEN_TTL_PARAM = 
"knoxsso.token.ttl";
 +  private static final String SSO_COOKIE_TOKEN_AUDIENCES_PARAM = 
"knoxsso.token.audiences";
++  private static final String SSO_COOKIE_TOKEN_SIG_ALG = 
"knoxsso.token.sigalg";
 +  private static final String SSO_COOKIE_TOKEN_WHITELIST_PARAM = 
"knoxsso.redirect.whitelist.regex";
 +  private static final String SSO_ENABLE_SESSION_PARAM = 
"knoxsso.enable.session";
 +  private static final String ORIGINAL_URL_REQUEST_PARAM = "originalUrl";
 +  private static final String ORIGINAL_URL_COOKIE_NAME = "original-url";
 +  private static final String DEFAULT_SSO_COOKIE_NAME = "hadoop-jwt";
 +  // default for the whitelist - open up for development - relative paths and 
localhost only
 +  private static final String DEFAULT_WHITELIST = 
"^/.*$;^https?://(localhost|127.0.0.1|0:0:0:0:0:0:0:1|::1):\\d{0,9}/.*$";
++  private static final long TOKEN_TTL_DEFAULT = 30000L;
 +  static final String RESOURCE_PATH = "/api/v1/websso";
 +  private static KnoxSSOMessages log = MessagesFactory.get( 
KnoxSSOMessages.class );
 +  private String cookieName = null;
 +  private boolean secureOnly = true;
 +  private int maxAge = -1;
-   private long tokenTTL = 30000l;
++  private long tokenTTL = TOKEN_TTL_DEFAULT;
 +  private String whitelist = null;
 +  private String domainSuffix = null;
 +  private List<String> targetAudiences = new ArrayList<>();
 +  private boolean enableSession = false;
++  private String signatureAlgorithm = "RS256";
 +
 +  @Context
 +  HttpServletRequest request;
 +
 +  @Context
 +  HttpServletResponse response;
 +
 +  @Context
 +  ServletContext context;
 +
 +  @PostConstruct
 +  public void init() {
 +
 +    // configured cookieName
 +    cookieName = context.getInitParameter(SSO_COOKIE_NAME);
 +    if (cookieName == null) {
 +      cookieName = DEFAULT_SSO_COOKIE_NAME;
 +    }
 +
 +    String secure = 
context.getInitParameter(SSO_COOKIE_SECURE_ONLY_INIT_PARAM);
 +    if (secure != null) {
 +      secureOnly = ("false".equals(secure) ? false : true);
 +      if (!secureOnly) {
 +        log.cookieSecureOnly(secureOnly);
 +      }
 +    }
 +
 +    String age = context.getInitParameter(SSO_COOKIE_MAX_AGE_INIT_PARAM);
 +    if (age != null) {
 +      try {
 +        log.setMaxAge(age);
 +        maxAge = Integer.parseInt(age);
 +      }
 +      catch (NumberFormatException nfe) {
 +        log.invalidMaxAgeEncountered(age);
 +      }
 +    }
 +
 +    domainSuffix = context.getInitParameter(SSO_COOKIE_DOMAIN_SUFFIX_PARAM);
 +
 +    whitelist = context.getInitParameter(SSO_COOKIE_TOKEN_WHITELIST_PARAM);
 +    if (whitelist == null) {
 +      // default to local/relative targets
 +      whitelist = DEFAULT_WHITELIST;
 +    }
 +
 +    String audiences = 
context.getInitParameter(SSO_COOKIE_TOKEN_AUDIENCES_PARAM);
 +    if (audiences != null) {
 +      String[] auds = audiences.split(",");
 +      for (int i = 0; i < auds.length; i++) {
 +        targetAudiences.add(auds[i].trim());
 +      }
 +    }
 +
 +    String ttl = context.getInitParameter(SSO_COOKIE_TOKEN_TTL_PARAM);
 +    if (ttl != null) {
 +      try {
 +        tokenTTL = Long.parseLong(ttl);
++        if (tokenTTL < -1 || (tokenTTL + System.currentTimeMillis() < 0)) {
++          log.invalidTokenTTLEncountered(ttl);
++          tokenTTL = TOKEN_TTL_DEFAULT;
++        }
 +      }
 +      catch (NumberFormatException nfe) {
 +        log.invalidTokenTTLEncountered(ttl);
 +      }
 +    }
 +
 +    String enableSession = context.getInitParameter(SSO_ENABLE_SESSION_PARAM);
 +    this.enableSession = ("true".equals(enableSession));
++
++    String sigAlg = context.getInitParameter(SSO_COOKIE_TOKEN_SIG_ALG);
++    if (sigAlg != null) {
++      signatureAlgorithm = sigAlg;
++    }
 +  }
 +
 +  @GET
 +  @Produces({APPLICATION_JSON, APPLICATION_XML})
 +  public Response doGet() {
 +    return getAuthenticationToken(HttpServletResponse.SC_TEMPORARY_REDIRECT);
 +  }
 +
 +  @POST
 +  @Produces({APPLICATION_JSON, APPLICATION_XML})
 +  public Response doPost() {
 +    return getAuthenticationToken(HttpServletResponse.SC_SEE_OTHER);
 +  }
 +
 +  private Response getAuthenticationToken(int statusCode) {
 +    GatewayServices services = (GatewayServices) request.getServletContext()
 +            .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +    boolean removeOriginalUrlCookie = true;
 +    String original = getCookieValue((HttpServletRequest) request, 
ORIGINAL_URL_COOKIE_NAME);
 +    if (original == null) {
 +      // in the case where there are no SAML redirects done before here
 +      // we need to get it from the request parameters
 +      removeOriginalUrlCookie = false;
 +      original = getOriginalUrlFromQueryParams();
 +      if (original.isEmpty()) {
 +        log.originalURLNotFound();
 +        throw new WebApplicationException("Original URL not found in the 
request.", Response.Status.BAD_REQUEST);
 +      }
 +      boolean validRedirect = RegExUtils.checkWhitelist(whitelist, original);
 +      if (!validRedirect) {
 +        log.whiteListMatchFail(original, whitelist);
 +        throw new WebApplicationException("Original URL not valid according 
to the configured whitelist.",
 +                Response.Status.BAD_REQUEST);
 +      }
 +    }
 +
 +    JWTokenAuthority ts = services.getService(GatewayServices.TOKEN_SERVICE);
 +    Principal p = ((HttpServletRequest)request).getUserPrincipal();
 +
 +    try {
 +      JWT token = null;
 +      if (targetAudiences.isEmpty()) {
-         token = ts.issueToken(p, "RS256", getExpiry());
++        token = ts.issueToken(p, signatureAlgorithm, getExpiry());
 +      } else {
-         token = ts.issueToken(p, targetAudiences, "RS256", getExpiry());
++        token = ts.issueToken(p, targetAudiences, signatureAlgorithm, 
getExpiry());
 +      }
 +
 +      // Coverity CID 1327959
 +      if( token != null ) {
 +        addJWTHadoopCookie( original, token );
 +      }
 +
 +      if (removeOriginalUrlCookie) {
 +        removeOriginalUrlCookie(response);
 +      }
 +
 +      log.aboutToRedirectToOriginal(original);
 +      response.setStatus(statusCode);
 +      response.setHeader("Location", original);
 +      try {
 +        response.getOutputStream().close();
 +      } catch (IOException e) {
 +        log.unableToCloseOutputStream(e.getMessage(), 
Arrays.toString(e.getStackTrace()));
 +      }
 +    }
 +    catch (TokenServiceException e) {
 +      log.unableToIssueToken(e);
 +    }
 +    URI location = null;
 +    try {
 +      location = new URI(original);
 +    }
 +    catch(URISyntaxException urise) {
 +      // todo log return error response
 +    }
 +
 +    if (!enableSession) {
 +      // invalidate the session to avoid autologin
 +      // Coverity CID 1352857
 +      HttpSession session = request.getSession(false);
 +      if( session != null ) {
 +        session.invalidate();
 +      }
 +    }
 +
 +    return Response.seeOther(location).entity("{ \"redirectTo\" : " + 
original + " }").build();
 +  }
 +
 +  private String getOriginalUrlFromQueryParams() {
 +    String original = request.getParameter(ORIGINAL_URL_REQUEST_PARAM);
 +    StringBuffer buf = new StringBuffer(original);
 +
 +    // Add any other query params.
 +    // Probably not ideal but will not break existing integrations by 
requiring
 +    // some encoding.
 +    Map<String, String[]> params = request.getParameterMap();
 +    for (Entry<String, String[]> entry : params.entrySet()) {
 +      if (!ORIGINAL_URL_REQUEST_PARAM.equals(entry.getKey())
 +          && !original.contains(entry.getKey() + "=")) {
 +        buf.append("&").append(entry.getKey());
 +        String[] values = entry.getValue();
 +        if (values.length > 0 && values[0] != null) {
 +          buf.append("=");
 +        }
 +        for (int i = 0; i < values.length; i++) {
 +          if (values[0] != null) {
 +            buf.append(values[i]);
 +            if (i < values.length-1) {
 +              buf.append("&").append(entry.getKey()).append("=");
 +            }
 +          }
 +        }
 +      }
 +    }
 +
 +    return buf.toString();
 +  }
 +
 +  private long getExpiry() {
 +    long expiry = 0l;
 +    if (tokenTTL == -1) {
 +      expiry = -1;
 +    }
 +    else {
 +      expiry = System.currentTimeMillis() + tokenTTL;
 +    }
 +    return expiry;
 +  }
 +
 +  private void addJWTHadoopCookie(String original, JWT token) {
 +    log.addingJWTCookie(token.toString());
 +    Cookie c = new Cookie(cookieName,  token.toString());
 +    c.setPath("/");
 +    try {
 +      String domain = Urls.getDomainName(original, domainSuffix);
 +      if (domain != null) {
 +        c.setDomain(domain);
 +      }
 +      c.setHttpOnly(true);
 +      if (secureOnly) {
 +        c.setSecure(true);
 +      }
 +      if (maxAge != -1) {
 +        c.setMaxAge(maxAge);
 +      }
 +      response.addCookie(c);
 +      log.addedJWTCookie();
 +    }
 +    catch(Exception e) {
 +      log.unableAddCookieToResponse(e.getMessage(), 
Arrays.toString(e.getStackTrace()));
 +      throw new WebApplicationException("Unable to add JWT cookie to 
response.");
 +    }
 +  }
 +
 +  private void removeOriginalUrlCookie(HttpServletResponse response) {
 +    Cookie c = new Cookie(ORIGINAL_URL_COOKIE_NAME, null);
 +    c.setMaxAge(0);
 +    c.setPath(RESOURCE_PATH);
 +    response.addCookie(c);
 +  }
 +
 +  private String getCookieValue(HttpServletRequest request, String name) {
 +    Cookie[] cookies = request.getCookies();
 +    String value = null;
 +    if (cookies != null) {
 +      for(Cookie cookie : cookies){
 +        if(name.equals(cookie.getName())){
 +          value = cookie.getValue();
 +        }
 +      }
 +    }
 +    if (value == null) {
 +      log.cookieNotFound(name);
 +    }
 +    return value;
 +  }
 +}

Reply via email to