knox-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From m...@apache.org
Subject [20/25] knox git commit: Merge branch 'master' into KNOX-998-Package_Restructuring
Date Thu, 02 Nov 2017 18:48:24 GMT
Merge branch 'master' into KNOX-998-Package_Restructuring

# Conflicts:
#	gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/BaseDirectoryService.java
#	gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/BaseDirectoryServiceFactory.java
#	gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/SimpleDirectoryServiceFactory.java
#	gateway-server/src/main/java/org/apache/knox/gateway/GatewayFilter.java
#	gateway-server/src/test/java/org/apache/knox/gateway/GatewayFilterTest.java
#	gateway-server/src/test/java/org/apache/knox/gateway/services/topology/DefaultTopologyServiceTest.java
#	gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/TopologiesResource.java
#	gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminTopologyFuncTest.java


Project: http://git-wip-us.apache.org/repos/asf/knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/c754cc06
Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/c754cc06
Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/c754cc06

Branch: refs/heads/KNOX-998-Package_Restructuring
Commit: c754cc06ac33c7cfff28c47ec562d888241c2641
Parents: 9577842 11ec78a
Author: Sandeep More <more@apache.org>
Authored: Wed Nov 1 17:10:14 2017 -0400
Committer: Sandeep More <more@apache.org>
Committed: Wed Nov 1 17:10:14 2017 -0400

----------------------------------------------------------------------
 gateway-demo-ldap/pom.xml                       |   36 +-
 .../security/ldap/BaseDirectoryService.java     | 2323 ------------------
 .../ldap/BaseDirectoryServiceFactory.java       |  290 ---
 .../security/ldap/SimpleDirectoryService.java   |    6 +-
 .../ldap/SimpleDirectoryServiceFactory.java     |   34 -
 .../ldap/SimpleLdapDirectoryServer.java         |   38 +-
 .../ambari/AmbariServiceDiscovery.java          |    3 +-
 .../filter/RegexIdentityAssertionFilter.java    |    4 +-
 .../regex/filter/RegexTemplate.java             |   12 +-
 .../regex/filter/RegexTemplateTest.java         |   23 +-
 .../webappsec/filter/StrictTranportFilter.java  |  137 ++
 .../webappsec/deploy/WebAppSecContributor.java  |   11 +
 .../webappsec/StrictTranportFilterTest.java     |  164 ++
 .../home/conf/topologies/manager.xml            |    1 +
 gateway-release/home/templates/sandbox-apps.xml |    1 +
 .../org/apache/knox/gateway/GatewayFilter.java  |   65 +-
 .../apache/knox/gateway/GatewayMessages.java    |   34 +-
 .../gateway/config/impl/GatewayConfigImpl.java  |    3 +-
 .../topology/impl/DefaultTopologyService.java   |  221 +-
 .../builder/BeanPropertyTopologyBuilder.java    |   11 +
 .../xml/KnoxFormatXmlTopologyRules.java         |    2 +
 .../src/main/resources/conf/topology-v1.xsd     |    1 +
 .../apache/knox/gateway/GatewayFilterTest.java  |   49 +
 .../topology/DefaultTopologyServiceTest.java    |  404 ++-
 .../topology/file/provider-config-one.xml       |   74 +
 .../topology/file/simple-descriptor-five.json   |   14 +
 .../topology/file/simple-descriptor-six.json    |   18 +
 .../service/admin/HrefListingMarshaller.java    |   75 +
 .../service/admin/TopologiesResource.java       |  393 ++-
 .../service/admin/beans/BeanConverter.java      |    2 +
 .../gateway/service/admin/beans/Topology.java   |   11 +
 .../services/ambariui/2.2.1/rewrite.xml         |  104 +
 .../services/ambariui/2.2.1/service.xml         |   92 +
 .../knox/gateway/i18n/GatewaySpiMessages.java   |   10 +-
 .../services/topology/TopologyService.java      |   33 +-
 .../apache/knox/gateway/topology/Topology.java  |    9 +
 .../gateway/topology/topology_binding-xml.xml   |    5 +-
 gateway-test-release/pom.xml                    |   11 -
 gateway-test/pom.xml                            |   14 -
 .../gateway/GatewayAdminTopologyFuncTest.java   |  586 +++++
 pom.xml                                         |    8 +-
 41 files changed, 2495 insertions(+), 2837 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-demo-ldap/pom.xml
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/BaseDirectoryService.java
----------------------------------------------------------------------
diff --cc gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/BaseDirectoryService.java
index 53add76,0000000..e69de29
mode 100644,000000..100644
--- a/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/BaseDirectoryService.java
+++ b/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/BaseDirectoryService.java

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/BaseDirectoryServiceFactory.java
----------------------------------------------------------------------
diff --cc gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/BaseDirectoryServiceFactory.java
index aed78bf,0000000..e69de29
mode 100644,000000..100644
--- a/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/BaseDirectoryServiceFactory.java
+++ b/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/BaseDirectoryServiceFactory.java

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/SimpleDirectoryService.java
----------------------------------------------------------------------
diff --cc gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/SimpleDirectoryService.java
index 69cdb3c,0000000..4e843a5
mode 100644,000000..100644
--- a/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/SimpleDirectoryService.java
+++ b/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/SimpleDirectoryService.java
@@@ -1,29 -1,0 +1,33 @@@
 +/**
 + * 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.security.ldap;
 +
- public class SimpleDirectoryService extends BaseDirectoryService {
++import org.apache.directory.server.core.DefaultDirectoryService;
++
++public class SimpleDirectoryService extends DefaultDirectoryService {
 +
 +  public SimpleDirectoryService() throws Exception {
++    super();
 +  }
 +
++  @Override
 +  protected void showSecurityWarnings() throws Exception {
 +    // NoOp - This prevents confusing warnings from being output.
 +  }
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/SimpleDirectoryServiceFactory.java
----------------------------------------------------------------------
diff --cc gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/SimpleDirectoryServiceFactory.java
index a25355b,0000000..e69de29
mode 100644,000000..100644
--- a/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/SimpleDirectoryServiceFactory.java
+++ b/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/SimpleDirectoryServiceFactory.java

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/SimpleLdapDirectoryServer.java
----------------------------------------------------------------------
diff --cc gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/SimpleLdapDirectoryServer.java
index 9f59e9b,0000000..4809f19
mode 100644,000000..100644
--- a/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/SimpleLdapDirectoryServer.java
+++ b/gateway-demo-ldap/src/main/java/org/apache/knox/gateway/security/ldap/SimpleLdapDirectoryServer.java
@@@ -1,124 -1,0 +1,160 @@@
 +/**
 + * 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.security.ldap;
 +
 +import org.apache.commons.io.FileUtils;
 +import org.apache.directory.api.ldap.model.entry.DefaultModification;
 +import org.apache.directory.api.ldap.model.entry.ModificationOperation;
 +import org.apache.directory.api.ldap.model.exception.LdapException;
 +import org.apache.directory.api.ldap.model.name.Dn;
 +import org.apache.directory.server.core.api.CoreSession;
 +import org.apache.directory.server.core.api.DirectoryService;
 +import org.apache.directory.server.core.api.partition.Partition;
++import org.apache.directory.server.core.factory.DefaultDirectoryServiceFactory;
 +import org.apache.directory.server.core.factory.DirectoryServiceFactory;
++import org.apache.directory.server.core.factory.JdbmPartitionFactory;
++import org.apache.directory.server.core.factory.PartitionFactory;
 +import org.apache.directory.server.ldap.LdapServer;
 +import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
 +import org.apache.directory.server.protocol.shared.transport.TcpTransport;
 +import org.apache.directory.server.protocol.shared.transport.Transport;
 +import org.apache.log4j.PropertyConfigurator;
++import org.slf4j.Logger;
++import org.slf4j.LoggerFactory;
 +
 +import java.io.File;
 +import java.io.FileNotFoundException;
 +import java.net.ServerSocket;
 +import java.util.UUID;
 +
 +public class SimpleLdapDirectoryServer {
 +
++  private static final Logger LOG = LoggerFactory.getLogger(SimpleLdapDirectoryServer.class);
++
 +  private DirectoryServiceFactory factory;
 +
 +  private DirectoryService service;
 +
 +  private LdapServer server;
 +
 +  public SimpleLdapDirectoryServer( String rootDn, File usersLdif, Transport... transports ) throws Exception {
 +    if( !usersLdif.exists() ) {
 +      throw new FileNotFoundException( usersLdif.getAbsolutePath() );
 +    }
 +
-     factory = new SimpleDirectoryServiceFactory();
++    DirectoryService directoryService = null;
++    try {
++      // creating the instance here so that
++      // we we can set some properties like accesscontrol, anon access
++      // before starting up the service
++      directoryService = new SimpleDirectoryService();
++
++      // no need to register a shutdown hook during tests because this
++      // starts a lot of threads and slows down test execution
++      directoryService.setShutdownHookEnabled( false );
++    } catch ( Exception e ) {
++      throw new RuntimeException( e );
++    }
++
++    PartitionFactory partitionFactory = null;
++    try {
++      String typeName = System.getProperty( "apacheds.partition.factory" );
++
++      if ( typeName != null ) {
++        Class<? extends PartitionFactory> type = ( Class<? extends PartitionFactory> ) Class.forName( typeName );
++        partitionFactory = type.newInstance();
++      } else {
++        partitionFactory = new JdbmPartitionFactory();
++      }
++    } catch ( Exception e ) {
++      LOG.error( "Error instantiating custom partiton factory", e );
++      throw new RuntimeException( e );
++    }
++
++    factory = new DefaultDirectoryServiceFactory( directoryService, partitionFactory );
 +    factory.init( UUID.randomUUID().toString() );
 +    service = factory.getDirectoryService();
 +
 +    enabledPosixSchema( service );
 +
 +    Partition partition = factory.getPartitionFactory().createPartition(
 +        service.getSchemaManager(), service.getDnFactory(), "users", rootDn, 500,
 +        service.getInstanceLayout().getInstanceDirectory() );
 +    service.addPartition( partition );
 +
 +    CoreSession session = service.getAdminSession();
 +    LdifFileLoader lfl = new LdifFileLoader( session, usersLdif, null );
 +    lfl.execute();
 +
 +    server = new LdapServer();
 +    server.setTransports( transports );
 +    server.setDirectoryService( service );
 +  }
 +
 +  private static void enabledPosixSchema( DirectoryService service ) throws LdapException {
 +    service.getSchemaManager().getLoadedSchema( "nis" ).enable();
 +    service.getAdminSession().modify(
 +        new Dn( "cn=nis,ou=schema" ),
 +        new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, "m-disabled", "FALSE" ) );
 +  }
 +
 +  public void start() throws Exception {
 +    service.startup();
 +    server.start();
 +  }
 +
 +  public void stop( boolean clean ) throws Exception {
 +    server.stop();
 +    service.shutdown();
 +    if( clean ) {
 +      FileUtils.deleteDirectory( service.getInstanceLayout().getInstanceDirectory() );
 +    }
 +  }
 +
 +  public static void main( String[] args ) throws Exception {
 +    PropertyConfigurator.configure( System.getProperty( "log4j.configuration" ) );
 +
 +    SimpleLdapDirectoryServer ldap;
 +
 +    File file;
 +    if ( args.length < 1 ) {
 +      file = new File( "conf/users.ldif" );
 +    } else {
 +      File dir = new File( args[0] );
 +      if( !dir.exists() || !dir.isDirectory() ) {
 +        throw new FileNotFoundException( dir.getAbsolutePath() );
 +      }
 +      file = new File( dir, "users.ldif" );
 +    }
 +
 +    if( !file.exists() || !file.canRead() ) {
 +      throw new FileNotFoundException( file.getAbsolutePath() );
 +    }
 +
 +    int port = 33389;
 +
 +    // Make sure the port is free.
 +    ServerSocket socket = new ServerSocket( port );
 +    socket.close();
 +
 +    TcpTransport transport = new TcpTransport( port );
 +    ldap = new SimpleLdapDirectoryServer( "dc=hadoop,dc=apache,dc=org", file, transport );
 +    ldap.start();
 +  }
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscovery.java
----------------------------------------------------------------------
diff --cc gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscovery.java
index 70af903,0000000..dbc783d
mode 100644,000000..100644
--- a/gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscovery.java
+++ b/gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscovery.java
@@@ -1,305 -1,0 +1,306 @@@
 +/**
 + * 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.discovery.ambari;
 +
 +import java.io.IOException;
 +import java.util.ArrayList;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Properties;
 +
 +import net.minidev.json.JSONArray;
 +import net.minidev.json.JSONObject;
 +import net.minidev.json.JSONValue;
 +import org.apache.knox.gateway.config.ConfigurationException;
 +import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 +import org.apache.knox.gateway.services.security.AliasService;
 +import org.apache.knox.gateway.services.security.AliasServiceException;
 +import org.apache.knox.gateway.topology.discovery.GatewayService;
 +import org.apache.knox.gateway.topology.discovery.ServiceDiscovery;
 +import org.apache.knox.gateway.topology.discovery.ServiceDiscoveryConfig;
 +import org.apache.http.HttpEntity;
 +import org.apache.http.HttpStatus;
 +import org.apache.http.client.methods.CloseableHttpResponse;
 +import org.apache.http.client.methods.HttpGet;
 +import org.apache.http.impl.client.CloseableHttpClient;
 +import org.apache.http.message.BasicHeader;
 +import org.apache.http.util.EntityUtils;
 +
 +
 +class AmbariServiceDiscovery implements ServiceDiscovery {
 +
 +    static final String TYPE = "AMBARI";
 +
 +    static final String AMBARI_CLUSTERS_URI = "/api/v1/clusters";
 +
 +    static final String AMBARI_HOSTROLES_URI =
 +                                       AMBARI_CLUSTERS_URI + "/%s/services?fields=components/host_components/HostRoles";
 +
 +    static final String AMBARI_SERVICECONFIGS_URI =
 +            AMBARI_CLUSTERS_URI + "/%s/configurations/service_config_versions?is_current=true";
 +
 +    private static final String COMPONENT_CONFIG_MAPPING_FILE =
 +                                                        "ambari-service-discovery-component-config-mapping.properties";
 +
 +    private static final AmbariServiceDiscoveryMessages log = MessagesFactory.get(AmbariServiceDiscoveryMessages.class);
 +
 +    // Map of component names to service configuration types
 +    private static Map<String, String> componentServiceConfigs = new HashMap<>();
 +    static {
 +        try {
 +            Properties configMapping = new Properties();
 +            configMapping.load(AmbariServiceDiscovery.class.getClassLoader().getResourceAsStream(COMPONENT_CONFIG_MAPPING_FILE));
 +            for (String componentName : configMapping.stringPropertyNames()) {
 +                componentServiceConfigs.put(componentName, configMapping.getProperty(componentName));
 +            }
 +        } catch (Exception e) {
 +            log.failedToLoadServiceDiscoveryConfiguration(COMPONENT_CONFIG_MAPPING_FILE, e);
 +        }
 +    }
 +
 +    private static final String DEFAULT_USER_ALIAS = "ambari.discovery.user";
 +    private static final String DEFAULT_PWD_ALIAS  = "ambari.discovery.password";
 +
 +    @GatewayService
 +    private AliasService aliasService;
 +
 +    private CloseableHttpClient httpClient = null;
 +
 +
 +    AmbariServiceDiscovery() {
 +        httpClient = org.apache.http.impl.client.HttpClients.createDefault();
 +    }
 +
 +
 +    @Override
 +    public String getType() {
 +        return TYPE;
 +    }
 +
 +
 +    @Override
 +    public Map<String, Cluster> discover(ServiceDiscoveryConfig config) {
 +        Map<String, Cluster> clusters = new HashMap<String, Cluster>();
 +
 +        String discoveryAddress = config.getAddress();
 +
 +        // Invoke Ambari REST API to discover the available clusters
 +        String clustersDiscoveryURL = String.format("%s" + AMBARI_CLUSTERS_URI, discoveryAddress);
 +
 +        JSONObject json = invokeREST(clustersDiscoveryURL, config.getUser(), config.getPasswordAlias());
 +
 +        // Parse the cluster names from the response, and perform the cluster discovery
 +        JSONArray clusterItems = (JSONArray) json.get("items");
 +        for (Object clusterItem : clusterItems) {
 +            String clusterName = (String) ((JSONObject)((JSONObject) clusterItem).get("Clusters")).get("cluster_name");
 +            try {
 +                Cluster c = discover(config, clusterName);
 +                clusters.put(clusterName, c);
 +            } catch (Exception e) {
 +                log.clusterDiscoveryError(clusterName, e);
 +            }
 +        }
 +
 +        return clusters;
 +    }
 +
 +
 +    @Override
 +    public Cluster discover(ServiceDiscoveryConfig config, String clusterName) {
 +        AmbariCluster cluster = new AmbariCluster(clusterName);
 +
 +        Map<String, String> serviceComponents = new HashMap<>();
 +
 +        String discoveryAddress = config.getAddress();
 +        String discoveryUser = config.getUser();
 +        String discoveryPwdAlias = config.getPasswordAlias();
 +
 +        Map<String, List<String>> componentHostNames = new HashMap<>();
 +        String hostRolesURL = String.format("%s" + AMBARI_HOSTROLES_URI, discoveryAddress, clusterName);
 +        JSONObject hostRolesJSON = invokeREST(hostRolesURL, discoveryUser, discoveryPwdAlias);
 +        if (hostRolesJSON != null) {
 +            // Process the host roles JSON
 +            JSONArray items = (JSONArray) hostRolesJSON.get("items");
 +            for (Object obj : items) {
 +                JSONArray components = (JSONArray) ((JSONObject) obj).get("components");
 +                for (Object component : components) {
 +                    JSONArray hostComponents = (JSONArray) ((JSONObject) component).get("host_components");
 +                    for (Object hostComponent : hostComponents) {
 +                        JSONObject hostRoles = (JSONObject) ((JSONObject) hostComponent).get("HostRoles");
 +                        String serviceName = (String) hostRoles.get("service_name");
 +                        String componentName = (String) hostRoles.get("component_name");
 +
 +                        serviceComponents.put(componentName, serviceName);
 +
 +                        // Assuming public host name is more applicable than host_name
 +                        String hostName = (String) hostRoles.get("public_host_name");
 +                        if (hostName == null) {
 +                            // Some (even slightly) older versions of Ambari/HDP do not return public_host_name,
 +                            // so fall back to host_name in those cases.
 +                            hostName = (String) hostRoles.get("host_name");
 +                        }
 +
 +                        if (hostName != null) {
 +                            log.discoveredServiceHost(serviceName, hostName);
 +                            if (!componentHostNames.containsKey(componentName)) {
 +                                componentHostNames.put(componentName, new ArrayList<String>());
 +                            }
 +                            componentHostNames.get(componentName).add(hostName);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        Map<String, Map<String, AmbariCluster.ServiceConfiguration>> serviceConfigurations =
 +                                                 new HashMap<String, Map<String, AmbariCluster.ServiceConfiguration>>();
 +        String serviceConfigsURL = String.format("%s" + AMBARI_SERVICECONFIGS_URI, discoveryAddress, clusterName);
 +        JSONObject serviceConfigsJSON = invokeREST(serviceConfigsURL, discoveryUser, discoveryPwdAlias);
 +        if (serviceConfigsJSON != null) {
 +            // Process the service configurations
 +            JSONArray serviceConfigs = (JSONArray) serviceConfigsJSON.get("items");
 +            for (Object serviceConfig : serviceConfigs) {
 +                String serviceName = (String) ((JSONObject) serviceConfig).get("service_name");
 +                JSONArray configurations = (JSONArray) ((JSONObject) serviceConfig).get("configurations");
 +                for (Object configuration : configurations) {
 +                    String configType = (String) ((JSONObject) configuration).get("type");
 +                    String configVersion = String.valueOf(((JSONObject) configuration).get("version"));
 +
 +                    Map<String, String> configProps = new HashMap<String, String>();
 +                    JSONObject configProperties = (JSONObject) ((JSONObject) configuration).get("properties");
 +                    for (String propertyName : configProperties.keySet()) {
 +                        configProps.put(propertyName, String.valueOf(((JSONObject) configProperties).get(propertyName)));
 +                    }
 +                    if (!serviceConfigurations.containsKey(serviceName)) {
 +                        serviceConfigurations.put(serviceName, new HashMap<String, AmbariCluster.ServiceConfiguration>());
 +                    }
 +                    serviceConfigurations.get(serviceName).put(configType, new AmbariCluster.ServiceConfiguration(configType, configVersion, configProps));
 +                    cluster.addServiceConfiguration(serviceName, configType, new AmbariCluster.ServiceConfiguration(configType, configVersion, configProps));
 +                }
 +            }
 +        }
 +
 +        // Construct the AmbariCluster model
 +        for (String componentName : serviceComponents.keySet()) {
 +            String serviceName = serviceComponents.get(componentName);
 +            List<String> hostNames = componentHostNames.get(componentName);
 +
 +            Map<String, AmbariCluster.ServiceConfiguration> configs = serviceConfigurations.get(serviceName);
 +            String configType = componentServiceConfigs.get(componentName);
 +            if (configType != null) {
 +                AmbariCluster.ServiceConfiguration svcConfig = configs.get(configType);
 +                AmbariComponent c = new AmbariComponent(componentName,
 +                                                        svcConfig.getVersion(),
 +                                                        clusterName,
 +                                                        serviceName,
 +                                                        hostNames,
 +                                                        svcConfig.getProperties());
 +                cluster.addComponent(c);
 +            }
 +        }
 +
 +        return cluster;
 +    }
 +
 +
 +    protected JSONObject invokeREST(String url, String username, String passwordAlias) {
 +        JSONObject result = null;
 +
 +        CloseableHttpResponse response = null;
 +        try {
 +            HttpGet request = new HttpGet(url);
 +
 +            // If no configured username, then use default username alias
 +            String password = null;
 +            if (username == null) {
 +                if (aliasService != null) {
 +                    try {
 +                        char[] defaultUser = aliasService.getPasswordFromAliasForGateway(DEFAULT_USER_ALIAS);
 +                        if (defaultUser != null) {
 +                            username = new String(defaultUser);
 +                        }
 +                    } catch (AliasServiceException e) {
 +                        log.aliasServiceUserError(DEFAULT_USER_ALIAS, e.getLocalizedMessage());
 +                    }
 +                }
 +
 +                // If username is still null
 +                if (username == null) {
 +                    log.aliasServiceUserNotFound();
 +                    throw new ConfigurationException("No username is configured for Ambari service discovery.");
 +                }
 +            }
 +
 +            if (aliasService != null) {
-                 // If not password alias is configured, then try the default alias
++                // If no password alias is configured, then try the default alias
 +                if (passwordAlias == null) {
 +                    passwordAlias = DEFAULT_PWD_ALIAS;
 +                }
++
 +                try {
 +                    char[] pwd = aliasService.getPasswordFromAliasForGateway(passwordAlias);
 +                    if (pwd != null) {
 +                        password = new String(pwd);
 +                    }
 +
 +                } catch (AliasServiceException e) {
 +                    log.aliasServicePasswordError(passwordAlias, e.getLocalizedMessage());
 +                }
 +            }
 +
 +            // If the password could not be determined
 +            if (password == null) {
 +                log.aliasServicePasswordNotFound();
 +                throw new ConfigurationException("No password is configured for Ambari service discovery.");
 +            }
 +
 +            // Add an auth header if credentials are available
 +            String encodedCreds =
 +                    org.apache.commons.codec.binary.Base64.encodeBase64String((username + ":" + password).getBytes());
 +            request.addHeader(new BasicHeader("Authorization", "Basic " + encodedCreds));
 +
 +            response = httpClient.execute(request);
 +
 +            if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {
 +                HttpEntity entity = response.getEntity();
 +                if (entity != null) {
 +                    result = (JSONObject) JSONValue.parse((EntityUtils.toString(entity)));
 +                    log.debugJSON(result.toJSONString());
 +                } else {
 +                    log.noJSON(url);
 +                }
 +            } else {
 +                log.unexpectedRestResponseStatusCode(url, response.getStatusLine().getStatusCode());
 +            }
 +
 +        } catch (IOException e) {
 +            log.restInvocationError(url, e);
 +        } finally {
 +            if(response != null) {
 +                try {
 +                    response.close();
 +                } catch (IOException e) {
 +                    // Ignore
 +                }
 +            }
 +        }
 +        return result;
 +    }
 +
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-provider-identity-assertion-regex/src/main/java/org/apache/knox/gateway/identityasserter/regex/filter/RegexIdentityAssertionFilter.java
----------------------------------------------------------------------
diff --cc gateway-provider-identity-assertion-regex/src/main/java/org/apache/knox/gateway/identityasserter/regex/filter/RegexIdentityAssertionFilter.java
index 4cc86ae,0000000..3c9cf11
mode 100644,000000..100644
--- a/gateway-provider-identity-assertion-regex/src/main/java/org/apache/knox/gateway/identityasserter/regex/filter/RegexIdentityAssertionFilter.java
+++ b/gateway-provider-identity-assertion-regex/src/main/java/org/apache/knox/gateway/identityasserter/regex/filter/RegexIdentityAssertionFilter.java
@@@ -1,88 -1,0 +1,90 @@@
 +/**
 + * 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.identityasserter.regex.filter;
 +
 +import javax.security.auth.Subject;
 +import javax.servlet.FilterConfig;
 +import javax.servlet.ServletException;
 +
 +import org.apache.knox.gateway.identityasserter.common.filter.CommonIdentityAssertionFilter;
 +import org.apache.knox.gateway.security.principal.PrincipalMappingException;
 +
 +import java.util.Map;
 +import java.util.StringTokenizer;
 +import java.util.TreeMap;
++import java.lang.Boolean;
 +
 +public class RegexIdentityAssertionFilter extends
 +    CommonIdentityAssertionFilter {
 +
 +  private String input = null;
 +  private String output = null;
 +  private Map<String,String> dict;
 +  RegexTemplate template;
 +  
 +  @Override
 +  public void init(FilterConfig filterConfig) throws ServletException {
 +    super.init(filterConfig);
 +    try {
 +      input = filterConfig.getInitParameter( "input" );
 +      if( input == null ) {
 +        input = "";
 +      }
 +      output = filterConfig.getInitParameter( "output" );
 +      if( output == null ) {
 +        output = "";
 +      }
 +      dict = loadDictionary( filterConfig.getInitParameter( "lookup" ) );
-       template = new RegexTemplate( input, output, dict );
++      boolean useOriginalOnLookupFailure = Boolean.parseBoolean(filterConfig.getInitParameter("use.original.on.lookup.failure"));
++      template = new RegexTemplate( input, output, dict, useOriginalOnLookupFailure);
 +    } catch ( PrincipalMappingException e ) {
 +      throw new ServletException( e );
 +    }
 +  }
 +
 +  public String[] mapGroupPrincipals(String mappedPrincipalName, Subject subject) {
 +    // Returning null will allow existing Subject group principals to remain the same
 +    return null;
 +  }
 +
 +  public String mapUserPrincipal(String principalName) {
 +    return template.apply( principalName );
 +  }
 +
 +  private Map<String, String> loadDictionary( String config ) throws PrincipalMappingException {
 +    Map<String,String> dict = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
 +    if( config != null && !config.isEmpty() ) {
 +      try {
 +        StringTokenizer t = new StringTokenizer( config, ";" );
 +        while( t.hasMoreTokens() ) {
 +          String nvp = t.nextToken();
 +          String[] a = nvp.split( "=" );
 +          dict.put( a[0].trim(), a[1].trim() );
 +        }
 +        return dict;
 +      } catch( Exception e ) {
 +        dict.clear();
 +        throw new PrincipalMappingException(
 +            "Unable to load lookup dictionary from provided configuration: " + config +
 +                ".  No principal mapping will be provided.", e );
 +      }
 +    }
 +    return dict;
 +  }
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-provider-identity-assertion-regex/src/main/java/org/apache/knox/gateway/identityasserter/regex/filter/RegexTemplate.java
----------------------------------------------------------------------
diff --cc gateway-provider-identity-assertion-regex/src/main/java/org/apache/knox/gateway/identityasserter/regex/filter/RegexTemplate.java
index e8f108e,0000000..659d3df
mode 100644,000000..100644
--- a/gateway-provider-identity-assertion-regex/src/main/java/org/apache/knox/gateway/identityasserter/regex/filter/RegexTemplate.java
+++ b/gateway-provider-identity-assertion-regex/src/main/java/org/apache/knox/gateway/identityasserter/regex/filter/RegexTemplate.java
@@@ -1,75 -1,0 +1,79 @@@
 +/**
 + * 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
 + * <p/>
 + * http://www.apache.org/licenses/LICENSE-2.0
 + * <p/>
 + * 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.identityasserter.regex.filter;
 +
 +import java.util.Map;
 +import java.util.regex.Matcher;
 +import java.util.regex.Pattern;
 +
 +public class RegexTemplate {
 +
 +  private static Pattern directPattern = Pattern.compile( "\\{(\\[?\\d+?\\]?)\\}" );
 +  private static Pattern indirectPattern = Pattern.compile( "\\[(\\d+?)\\]" );
 +
 +  Pattern inputPattern;
 +  String outputTemplate;
 +  Map<String,String> lookupTable;
++  boolean useOriginalOnLookupFailure;
 +
 +  public RegexTemplate( String regex, String template ) {
-     this( regex, template, null );
++    this( regex, template, null, false );
 +  }
 +
-   public RegexTemplate( String regex, String template, Map<String,String> map ) {
++  public RegexTemplate( String regex, String template, Map<String,String> map, boolean useOriginalOnLookupFailure ) {
 +    this.inputPattern = Pattern.compile( regex );
 +    this.outputTemplate = template;
 +    this.lookupTable = map;
++    this.useOriginalOnLookupFailure = useOriginalOnLookupFailure;
 +  }
 +
 +  public String apply( String input ) {
 +    String output = outputTemplate;
 +    Matcher inputMatcher = inputPattern.matcher( input );
 +    if( inputMatcher.find() ) {
 +      output = expandTemplate( inputMatcher, output );
 +    }
 +    return output;
 +  }
 +
 +  private String expandTemplate( Matcher inputMatcher, String output ) {
 +    Matcher directMatcher = directPattern.matcher( output );
 +    while( directMatcher.find() ) {
++      String lookupKey = null;
 +      String lookupValue = null;
 +      String lookupStr = directMatcher.group( 1 );
 +      Matcher indirectMatcher = indirectPattern.matcher( lookupStr );
 +      if( indirectMatcher.find() ) {
 +        lookupStr = indirectMatcher.group( 1 );
 +        int lookupIndex = Integer.parseInt( lookupStr );
 +        if( lookupTable != null ) {
-           String lookupKey = inputMatcher.group( lookupIndex );
++          lookupKey = inputMatcher.group( lookupIndex );
 +          lookupValue = lookupTable.get( lookupKey );
 +        }
 +      } else {
 +        int lookupIndex = Integer.parseInt( lookupStr );
 +        lookupValue = inputMatcher.group( lookupIndex );
 +      }
-       output = directMatcher.replaceFirst( lookupValue == null ? "" : lookupValue );
++      String replaceWith = this.useOriginalOnLookupFailure ? lookupKey : "" ;
++      output = directMatcher.replaceFirst( lookupValue == null ? replaceWith : lookupValue );
 +      directMatcher = directPattern.matcher( output );
 +    }
 +    return output;
 +  }
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-provider-identity-assertion-regex/src/test/java/org/apache/knox/gateway/identityasserter/regex/filter/RegexTemplateTest.java
----------------------------------------------------------------------
diff --cc gateway-provider-identity-assertion-regex/src/test/java/org/apache/knox/gateway/identityasserter/regex/filter/RegexTemplateTest.java
index 3c3b06f,0000000..49630be
mode 100644,000000..100644
--- a/gateway-provider-identity-assertion-regex/src/test/java/org/apache/knox/gateway/identityasserter/regex/filter/RegexTemplateTest.java
+++ b/gateway-provider-identity-assertion-regex/src/test/java/org/apache/knox/gateway/identityasserter/regex/filter/RegexTemplateTest.java
@@@ -1,72 -1,0 +1,93 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + * <p/>
 + * http://www.apache.org/licenses/LICENSE-2.0
 + * <p/>
 + * 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.identityasserter.regex.filter;
 +
 +import org.junit.Test;
 +
 +import java.util.Map;
 +import java.util.TreeMap;
 +
 +import static org.hamcrest.MatcherAssert.assertThat;
 +import static org.hamcrest.core.Is.is;
 +
 +public class RegexTemplateTest {
 +
 +  @Test
 +  public void testExtractUsernameFromEmailAddress() {
 +
 +    RegexTemplate template;
 +    String actual;
 +
 +    template = new RegexTemplate( "(.*)@.*", "prefix_{1}_suffix" );
 +    actual = template.apply( "member@apache.org" );
 +    assertThat( actual, is( "prefix_member_suffix" ) );
 +
 +    template = new RegexTemplate( "(.*)@.*", "prefix_{0}_suffix" );
 +    actual = template.apply( "member@apache.org" );
 +    assertThat( actual, is( "prefix_member@apache.org_suffix" ) );
 +
 +    template = new RegexTemplate( "(.*)@.*", "prefix_{1}_{a}_suffix" );
 +    actual = template.apply( "member@apache.org" );
 +    assertThat( actual, is( "prefix_member_{a}_suffix" ) );
 +
 +  }
 +
 +  @Test
 +  public void testExtractUsernameFromEmailAddressAndMapDomain() {
 +
 +    RegexTemplate template;
 +    Map<String,String> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
 +    map.put( "us", "USA" );
 +    map.put( "ca", "CANADA" );
 +
 +    String actual;
 +
-     template = new RegexTemplate( "(.*)@(.*?)\\..*", "prefix_{1}:{[2]}_suffix", map );
++    template = new RegexTemplate( "(.*)@(.*?)\\..*", "prefix_{1}:{[2]}_suffix", map, false );
 +    actual = template.apply( "member@us.apache.org" );
 +    assertThat( actual, is( "prefix_member:USA_suffix" ) );
 +
 +    actual = template.apply( "member@ca.apache.org" );
 +    assertThat( actual, is( "prefix_member:CANADA_suffix" ) );
 +
 +    actual = template.apply( "member@nj.apache.org" );
 +    assertThat( actual, is( "prefix_member:_suffix" ) );
 +
 +  }
 +
++  @Test
++  public void testLookupFailure() {
++
++    RegexTemplate template;
++    Map<String,String> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
++    map.put( "us", "USA" );
++    map.put( "ca", "CANADA" );
++
++    String actual;
++
++    template = new RegexTemplate( "(.*)@(.*?)\\..*", "prefix_{1}:{[2]}_suffix", map, true );
++    actual = template.apply( "member@us.apache.org" );
++    assertThat( actual, is( "prefix_member:USA_suffix" ) );
++
++    actual = template.apply( "member@ca.apache.org" );
++    assertThat( actual, is( "prefix_member:CANADA_suffix" ) );
++
++    actual = template.apply( "member@nj.apache.org" );
++    assertThat( actual, is( "prefix_member:nj_suffix" ) );
++
++  }
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-provider-security-webappsec/src/main/java/org/apache/knox/gateway/webappsec/deploy/WebAppSecContributor.java
----------------------------------------------------------------------
diff --cc gateway-provider-security-webappsec/src/main/java/org/apache/knox/gateway/webappsec/deploy/WebAppSecContributor.java
index a182b37,0000000..17fb8c2
mode 100644,000000..100644
--- a/gateway-provider-security-webappsec/src/main/java/org/apache/knox/gateway/webappsec/deploy/WebAppSecContributor.java
+++ b/gateway-provider-security-webappsec/src/main/java/org/apache/knox/gateway/webappsec/deploy/WebAppSecContributor.java
@@@ -1,107 -1,0 +1,118 @@@
 +/**
 + * 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.webappsec.deploy;
 +
 +import java.util.ArrayList;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Map.Entry;
 +
 +import org.apache.knox.gateway.deploy.DeploymentContext;
 +import org.apache.knox.gateway.deploy.ProviderDeploymentContributorBase;
 +import org.apache.knox.gateway.descriptor.FilterParamDescriptor;
 +import org.apache.knox.gateway.descriptor.ResourceDescriptor;
 +import org.apache.knox.gateway.topology.Provider;
 +import org.apache.knox.gateway.topology.Service;
 +
 +public class WebAppSecContributor extends
 +    ProviderDeploymentContributorBase {
 +  private static final String ROLE = "webappsec";
 +  private static final String NAME = "WebAppSec";
 +  private static final String CSRF_SUFFIX = "_CSRF";
 +  private static final String CSRF_FILTER_CLASSNAME = "org.apache.knox.gateway.webappsec.filter.CSRFPreventionFilter";
 +  private static final String CSRF_ENABLED = "csrf.enabled";
 +  private static final String CORS_SUFFIX = "_CORS";
 +  private static final String CORS_FILTER_CLASSNAME = "com.thetransactioncompany.cors.CORSFilter";
 +  private static final String CORS_ENABLED = "cors.enabled";
 +  private static final String XFRAME_OPTIONS_SUFFIX = "_XFRAMEOPTIONS";
 +  private static final String XFRAME_OPTIONS_FILTER_CLASSNAME = "org.apache.knox.gateway.webappsec.filter.XFrameOptionsFilter";
 +  private static final String XFRAME_OPTIONS_ENABLED = "xframe.options.enabled";
++  private static final String STRICT_TRANSPORT_SUFFIX = "_STRICTTRANSPORT";
++  private static final String STRICT_TRANSPORT_FILTER_CLASSNAME = "org.apache.hadoop.gateway.webappsec.filter.StrictTranportFilter";
++  private static final String STRICT_TRANSPORT_ENABLED = "strict.transport.enabled";
 +
 +
 +  @Override
 +  public String getRole() {
 +    return ROLE;
 +  }
 +
 +  @Override
 +  public String getName() {
 +    return NAME;
 +  }
 +
 +  @Override
 +  public void initializeContribution(DeploymentContext context) {
 +    super.initializeContribution(context);
 +  }
 +
 +  @Override
 +  public void contributeFilter(DeploymentContext context, Provider provider, Service service, 
 +      ResourceDescriptor resource, List<FilterParamDescriptor> params) {
 +    
 +    Provider webappsec = context.getTopology().getProvider(ROLE, NAME);
 +    if (webappsec != null && webappsec.isEnabled()) {
 +      Map<String,String> map = provider.getParams();
 +      if (params == null) {
 +        params = new ArrayList<FilterParamDescriptor>();
 +      }
 +
 +      Map<String, String> providerParams = provider.getParams();
 +      // CORS support
 +      String corsEnabled = map.get(CORS_ENABLED);
 +      if ( corsEnabled != null && "true".equals(corsEnabled)) {
 +        provisionConfig(resource, providerParams, params, "cors.");
 +        resource.addFilter().name( getName() + CORS_SUFFIX ).role( getRole() ).impl( CORS_FILTER_CLASSNAME ).params( params );
 +      }
 +
 +      // CRSF
 +      params = new ArrayList<FilterParamDescriptor>();
 +      String csrfEnabled = map.get(CSRF_ENABLED);
 +      if ( csrfEnabled != null && "true".equals(csrfEnabled)) {
 +        provisionConfig(resource, providerParams, params, "csrf.");
 +        resource.addFilter().name( getName() + CSRF_SUFFIX ).role( getRole() ).impl( CSRF_FILTER_CLASSNAME ).params( params );
 +      }
 +
 +      // X-Frame-Options - clickjacking protection
 +      params = new ArrayList<FilterParamDescriptor>();
 +      String xframeOptionsEnabled = map.get(XFRAME_OPTIONS_ENABLED);
 +      if ( xframeOptionsEnabled != null && "true".equals(xframeOptionsEnabled)) {
 +        provisionConfig(resource, providerParams, params, "xframe.");
 +        resource.addFilter().name( getName() + XFRAME_OPTIONS_SUFFIX ).role( getRole() ).impl( XFRAME_OPTIONS_FILTER_CLASSNAME ).params( params );
 +      }
++
++      // HTTP Strict-Transport-Security
++      params = new ArrayList<FilterParamDescriptor>();
++      String strictTranportEnabled = map.get(STRICT_TRANSPORT_ENABLED);
++      if ( strictTranportEnabled != null && "true".equals(strictTranportEnabled)) {
++        provisionConfig(resource, providerParams, params, "strict.");
++        resource.addFilter().name( getName() + STRICT_TRANSPORT_SUFFIX).role( getRole() ).impl(STRICT_TRANSPORT_FILTER_CLASSNAME).params( params );
++      }
 +    }
 +  }
 +
 +  private void provisionConfig(ResourceDescriptor resource, Map<String,String> providerParams,
 +      List<FilterParamDescriptor> params, String prefix) {
 +    for(Entry<String, String> entry : providerParams.entrySet()) {
 +      if (entry.getKey().startsWith(prefix)) {
 +        params.add( resource.createFilterParam().name( entry.getKey().toLowerCase() ).value( entry.getValue() ) );
 +      }
 +    }
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-release/home/conf/topologies/manager.xml
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-server/src/main/java/org/apache/knox/gateway/GatewayFilter.java
----------------------------------------------------------------------
diff --cc gateway-server/src/main/java/org/apache/knox/gateway/GatewayFilter.java
index 5d7c5db,0000000..8dd29bf
mode 100644,000000..100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayFilter.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayFilter.java
@@@ -1,390 -1,0 +1,453 @@@
 +/**
 + * 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;
 +
 +import org.apache.knox.gateway.audit.api.Action;
 +import org.apache.knox.gateway.audit.api.ActionOutcome;
 +import org.apache.knox.gateway.audit.api.AuditContext;
 +import org.apache.knox.gateway.audit.api.AuditService;
 +import org.apache.knox.gateway.audit.api.AuditServiceFactory;
 +import org.apache.knox.gateway.audit.api.Auditor;
 +import org.apache.knox.gateway.audit.api.CorrelationContext;
 +import org.apache.knox.gateway.audit.api.CorrelationServiceFactory;
 +import org.apache.knox.gateway.audit.api.ResourceType;
 +import org.apache.knox.gateway.audit.log4j.audit.AuditConstants;
 +import org.apache.knox.gateway.config.GatewayConfig;
 +import org.apache.knox.gateway.filter.AbstractGatewayFilter;
 +import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 +import org.apache.knox.gateway.i18n.resources.ResourcesFactory;
++import org.apache.knox.gateway.topology.Topology;
 +import org.apache.knox.gateway.util.urltemplate.Matcher;
 +import org.apache.knox.gateway.util.urltemplate.Parser;
 +import org.apache.knox.gateway.util.urltemplate.Template;
 +
 +import javax.servlet.Filter;
 +import javax.servlet.FilterChain;
 +import javax.servlet.FilterConfig;
 +import javax.servlet.ServletContext;
 +import javax.servlet.ServletException;
 +import javax.servlet.ServletRequest;
 +import javax.servlet.ServletResponse;
 +import javax.servlet.http.HttpServletRequest;
++import javax.servlet.http.HttpServletRequestWrapper;
 +import javax.servlet.http.HttpServletResponse;
 +
 +import java.io.IOException;
 +import java.net.URISyntaxException;
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.Enumeration;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import java.util.UUID;
 +
 +/**
 + *
 + */
 +public class GatewayFilter implements Filter {
 +
 +  private static final FilterChain EMPTY_CHAIN = new FilterChain() {
 +    public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse ) throws IOException, ServletException {
 +    }
 +  };
 +  
 +  private static final GatewayMessages LOG = MessagesFactory.get( GatewayMessages.class );
 +  private static final GatewayResources RES = ResourcesFactory.get( GatewayResources.class );
 +  private static AuditService auditService = AuditServiceFactory.getAuditService();
 +  private static Auditor auditor = auditService.getAuditor(
 +      AuditConstants.DEFAULT_AUDITOR_NAME, AuditConstants.KNOX_SERVICE_NAME,
 +      AuditConstants.KNOX_COMPONENT_NAME );
 +
 +  private Set<Holder> holders;
 +  private Matcher<Chain> chains;
 +  private FilterConfig config;
 +
 +  public GatewayFilter() {
 +    holders = new HashSet<>();
 +    chains = new Matcher<Chain>();
 +  }
 +
 +  @Override
 +  public void init( FilterConfig filterConfig ) throws ServletException {
 +    this.config = filterConfig;
 +  }
 +
 +  @Override
 +  public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain ) throws IOException, ServletException {
 +    doFilter( servletRequest, servletResponse );
 +    if( filterChain != null ) {
 +      filterChain.doFilter( servletRequest, servletResponse );
 +    }
 +  }
 +
 +  @SuppressWarnings("unchecked")
 +  public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse ) throws IOException, ServletException {
 +    HttpServletRequest httpRequest = (HttpServletRequest)servletRequest;
 +    HttpServletResponse httpResponse = (HttpServletResponse)servletResponse;
 +
 +    //TODO: The resulting pathInfo + query needs to be added to the servlet context somehow so that filters don't need to rebuild it.  This is done in HttpClientDispatch right now for example.
 +    String servlet = httpRequest.getServletPath();
 +    String path = httpRequest.getPathInfo();
 +    String query = httpRequest.getQueryString();
 +    String requestPath = ( servlet == null ? "" : servlet ) + ( path == null ? "" : path );
 +    String requestPathWithQuery = requestPath + ( query == null ? "" : "?" + query );
 +
 +    Template pathWithQueryTemplate;
 +    try {
 +      pathWithQueryTemplate = Parser.parseLiteral( requestPathWithQuery );
 +    } catch( URISyntaxException e ) {
 +      throw new ServletException( e );
 +    }
 +    String contextWithPathAndQuery = httpRequest.getContextPath() + requestPathWithQuery;
 +    LOG.receivedRequest( httpRequest.getMethod(), requestPath );
 +
 +    servletRequest.setAttribute(
 +        AbstractGatewayFilter.SOURCE_REQUEST_URL_ATTRIBUTE_NAME, pathWithQueryTemplate );
 +    servletRequest.setAttribute(
 +        AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME, contextWithPathAndQuery );
 +
 +    Matcher<Chain>.Match match = chains.match( pathWithQueryTemplate );
-     
++
++    // if there was no match then look for a default service for the topology
++    if (match == null) {
++      Topology topology = (Topology) servletRequest.getServletContext().getAttribute("org.apache.hadoop.gateway.topology");
++      if (topology != null) {
++        String defaultServicePath = topology.getDefaultServicePath();
++        if (defaultServicePath != null) {
++          try {
++            String newPathWithQuery = defaultServicePath + "/" + pathWithQueryTemplate;
++            match = chains.match(Parser.parseLiteral(newPathWithQuery));
++            String origUrl = ((HttpServletRequest) servletRequest).getRequestURL().toString();
++            String url = origUrl;
++            if (path.equals("/")) {
++              url += defaultServicePath;
++            }
++            else {
++              int index = origUrl.indexOf(path);
++              url = origUrl.substring(0, index) + "/" + defaultServicePath + path;
++            }
++            String contextPath = defaultServicePath;
++            servletRequest = new ForwardedRequest((HttpServletRequest) servletRequest,
++                contextPath,
++                url);
++          } catch (URISyntaxException e) {
++            throw new ServletException( e );
++          }
++        }
++      }
++    }
++
 +    assignCorrelationRequestId();
 +    // Populate Audit/correlation parameters
 +    AuditContext auditContext = auditService.getContext();
 +    auditContext.setTargetServiceName( match == null ? null : match.getValue().getResourceRole() );
 +    auditContext.setRemoteIp( getRemoteAddress(servletRequest) );
 +    auditContext.setRemoteHostname( servletRequest.getRemoteHost() );
 +    auditor.audit(
 +        Action.ACCESS, contextWithPathAndQuery, ResourceType.URI,
 +        ActionOutcome.UNAVAILABLE, RES.requestMethod(((HttpServletRequest)servletRequest).getMethod()));
 +    
 +    if( match != null ) {
 +      Chain chain = match.getValue();
 +      servletRequest.setAttribute( AbstractGatewayFilter.TARGET_SERVICE_ROLE, chain.getResourceRole() );
 +      try {
 +        chain.doFilter( servletRequest, servletResponse );
 +      } catch( IOException e ) {
 +        LOG.failedToExecuteFilter( e );
 +        auditor.audit( Action.ACCESS, contextWithPathAndQuery, ResourceType.URI, ActionOutcome.FAILURE );
 +        throw e;
 +      } catch( ServletException e ) {
 +        LOG.failedToExecuteFilter( e );
 +        auditor.audit( Action.ACCESS, contextWithPathAndQuery, ResourceType.URI, ActionOutcome.FAILURE );
 +        throw e;
 +      } catch( RuntimeException e ) {
 +        LOG.failedToExecuteFilter( e );
 +        auditor.audit( Action.ACCESS, contextWithPathAndQuery, ResourceType.URI, ActionOutcome.FAILURE );
 +        throw e;
 +      } catch( ThreadDeath e ) {
 +        LOG.failedToExecuteFilter( e );
 +        auditor.audit( Action.ACCESS, contextWithPathAndQuery, ResourceType.URI, ActionOutcome.FAILURE );
 +        throw e;
 +      } catch( Throwable e ) {
 +        LOG.failedToExecuteFilter( e );
 +        auditor.audit( Action.ACCESS, contextWithPathAndQuery, ResourceType.URI, ActionOutcome.FAILURE );
 +        throw new ServletException( e );
 +      }
 +    } else {
 +      LOG.failedToMatchPath( requestPath );
 +      httpResponse.setStatus( HttpServletResponse.SC_NOT_FOUND );
 +    }
 +    //KAM[ Don't do this or the Jetty default servlet will overwrite any response setup by the filter.
 +    // filterChain.doFilter( servletRequest, servletResponse );
 +    //]
 +  }
 +
 +  private String getRemoteAddress(ServletRequest servletRequest) {
 +    GatewayConfig gatewayConfig =
 +        (GatewayConfig) servletRequest.getServletContext().
 +        getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
 +
 +    String addrHeaderName = gatewayConfig.getHeaderNameForRemoteAddress();
 +    String addr = ((HttpServletRequest)servletRequest).getHeader(addrHeaderName);
 +    if (addr == null || addr.trim().isEmpty()) {
 +      addr = servletRequest.getRemoteAddr();
 +    }
 +    return addr;
 +  }
 +
 +  @Override
 +  public void destroy() {
 +    for( Holder holder : holders ) {
 +      holder.destroy();
 +    }
 +  }
 +
 +  private void addHolder( Holder holder ) {
 +    holders.add( holder );
 +    Chain chain = chains.get( holder.template );
 +    if( chain == null ) {
 +      chain = new Chain();
 +      chain.setResourceRole( holder.getResourceRole() );
 +      chains.add( holder.template, chain );
 +    }
 +    chain.chain.add( holder );
 +  }
 +
 +  public void addFilter( String path, String name, Filter filter, Map<String,String> params, String resourceRole ) throws URISyntaxException {
 +    Holder holder = new Holder( path, name, filter, params, resourceRole );
 +    addHolder( holder );
 +  }
 +
 +//  public void addFilter( String path, String name, Class<RegexDirFilter> clazz, Map<String,String> params ) throws URISyntaxException {
 +//    Holder holder = new Holder( path, name, clazz, params );
 +//    addHolder( holder );
 +//  }
 +
 +  public void addFilter( String path, String name, String clazz, Map<String,String> params, String resourceRole ) throws URISyntaxException {
 +    Holder holder = new Holder( path, name, clazz, params, resourceRole );
 +    addHolder( holder );
 +  }
 +
 +  // Now creating the correlation context only if required since it may be created upstream in the CorrelationHandler.
 +  private void assignCorrelationRequestId() {
 +    CorrelationContext correlationContext = CorrelationServiceFactory.getCorrelationService().getContext();
 +    if( correlationContext == null ) {
 +      correlationContext = CorrelationServiceFactory.getCorrelationService().createContext();
 +    }
 +    String requestId = correlationContext.getRequestId();
 +    if( requestId == null ) {
 +      correlationContext.setRequestId( UUID.randomUUID().toString() );
 +    }
 +  }
 +
 +  private class Chain implements FilterChain {
 +
 +    private List<Holder> chain;
 +    private String resourceRole; 
 +
 +    private Chain() {
 +      this.chain = new ArrayList<Holder>();
 +    }
 +
 +    private Chain( List<Holder> chain ) {
 +      this.chain = chain;
 +    }
 +
 +    public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse ) throws IOException, ServletException {
 +      if( chain != null && !chain.isEmpty() ) {
 +        final Filter filter = chain.get( 0 );
 +        final FilterChain chain = subChain();
 +        filter.doFilter( servletRequest, servletResponse, chain );
 +      }
 +    }
 +
 +    private FilterChain subChain() {
 +      if( chain != null && chain.size() > 1 ) {
 +        return new Chain( chain.subList( 1, chain.size() ) );
 +      } else {
 +        return EMPTY_CHAIN;
 +      }
 +    }
 +
 +    private String getResourceRole() {
 +      return resourceRole;
 +    }
 +
 +    private void setResourceRole( String resourceRole ) {
 +      this.resourceRole = resourceRole;
 +    }
 +
 +  }
 +
 +  private class Holder implements Filter, FilterConfig {
 +//    private String path;
 +    private Template template;
 +    private String name;
 +    private Map<String,String> params;
 +    private Filter instance;
 +    private Class<? extends Filter> clazz;
 +    private String type;
 +    private String resourceRole;
 +
 +    private Holder( String path, String name, Filter filter, Map<String,String> params, String resourceRole ) throws URISyntaxException {
 +//      this.path = path;
 +      this.template = Parser.parseTemplate( path );
 +      this.name = name;
 +      this.params = params;
 +      this.instance = filter;
 +      this.clazz = filter.getClass();
 +      this.type = clazz.getCanonicalName();
 +      this.resourceRole = resourceRole;
 +    }
 +
 +//    private Holder( String path, String name, Class<RegexDirFilter> clazz, Map<String,String> params ) throws URISyntaxException {
 +//      this.path = path;
 +//      this.template = Parser.parse( path );
 +//      this.name = name;
 +//      this.params = params;
 +//      this.instance = null;
 +//      this.clazz = clazz;
 +//      this.type = clazz.getCanonicalName();
 +//    }
 +
 +    private Holder( String path, String name, String clazz, Map<String,String> params, String resourceRole ) throws URISyntaxException {
 +//      this.path = path;
 +      this.template = Parser.parseTemplate( path );
 +      this.name = name;
 +      this.params = params;
 +      this.instance = null;
 +      this.clazz = null;
 +      this.type = clazz;
 +      this.resourceRole = resourceRole;
 +    }
 +
 +    @Override
 +    public String getFilterName() {
 +      return name;
 +    }
 +
 +    @Override
 +    public ServletContext getServletContext() {
 +      return GatewayFilter.this.config.getServletContext();
 +    }
 +
 +    @Override
 +    public String getInitParameter( String name ) {
 +      String value = null;
 +      if( params != null ) {
 +        value = params.get( name );
 +      }
 +      return value;
 +    }
 +
 +    @Override
 +    public Enumeration<String> getInitParameterNames() {
 +      Enumeration<String> names = null;
 +      if( params != null ) {
 +        names = Collections.enumeration( params.keySet() );
 +      }
 +      return names;
 +    }
 +
 +    @Override
 +    public void init( FilterConfig filterConfig ) throws ServletException {
 +      getInstance().init( filterConfig );
 +    }
 +
 +    @Override
 +    public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain ) throws IOException, ServletException {
 +      final Filter filter = getInstance();
 +      filter.doFilter( servletRequest, servletResponse, filterChain );
 +    }
 +
 +    @Override
 +    public void destroy() {
 +      if( instance != null ) {
 +        instance.destroy();
 +        instance = null;
 +      }
 +    }
 +
 +    @SuppressWarnings("unchecked")
 +    private Class<? extends Filter> getClazz() throws ClassNotFoundException {
 +      if( clazz == null ) {
 +        ClassLoader loader = Thread.currentThread().getContextClassLoader();
 +        if( loader == null ) {
 +          loader = this.getClass().getClassLoader();
 +        }
 +        clazz = (Class)loader.loadClass( type );
 +      }
 +      return clazz;
 +    }
 +
 +    private Filter getInstance() throws ServletException {
 +      if( instance == null ) {
 +        try {
 +          if( clazz == null ) {
 +            clazz = getClazz();
 +          }
 +          instance = clazz.newInstance();
 +          instance.init( this );
 +        } catch( Exception e ) {
 +          throw new ServletException( e );
 +        }
 +      }
 +      return instance;
 +    }
 +    
 +    private String getResourceRole() {
 +      return resourceRole;
 +    }
 +
 +  }
 +
++  /**
++   * A request wrapper class that wraps a request and adds the context path if
++   * needed.
++   */
++  static class ForwardedRequest extends HttpServletRequestWrapper {
++
++    private String newURL;
++    private String contextpath;
++
++    public ForwardedRequest(final HttpServletRequest request,
++        final String contextpath, final String newURL) {
++      super(request);
++      this.newURL = newURL;
++      this.contextpath = contextpath;
++    }
++
++    @Override
++    public StringBuffer getRequestURL() {
++      return new StringBuffer(newURL);
++    }
++
++    @Override
++    public String getRequestURI() {
++      return newURL;
++    }
++
++    @Override
++    public String getContextPath() {
++      return super.getContextPath() + "/" + this.contextpath;
++    }
++
++  }
 +}


Mime
View raw message