/**
* 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.hadoop.gateway.deploy.impl;
import org.apache.hadoop.gateway.config.impl.GatewayConfigImpl;
import org.apache.hadoop.gateway.deploy.DeploymentContext;
import org.apache.hadoop.gateway.deploy.ServiceDeploymentContributorBase;
import org.apache.hadoop.gateway.descriptor.FilterDescriptor;
import org.apache.hadoop.gateway.descriptor.FilterParamDescriptor;
import org.apache.hadoop.gateway.descriptor.ResourceDescriptor;
import org.apache.hadoop.gateway.dispatch.GatewayDispatchFilter;
import org.apache.hadoop.gateway.filter.XForwardedHeaderFilter;
import org.apache.hadoop.gateway.filter.rewrite.api.CookieScopeServletFilter;
import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteRulesDescriptor;
import org.apache.hadoop.gateway.service.definition.CustomDispatch;
import org.apache.hadoop.gateway.service.definition.Policy;
import org.apache.hadoop.gateway.service.definition.Rewrite;
import org.apache.hadoop.gateway.service.definition.Route;
import org.apache.hadoop.gateway.service.definition.ServiceDefinition;
import org.apache.hadoop.gateway.topology.Provider;
import org.apache.hadoop.gateway.topology.Service;
import org.apache.hadoop.gateway.topology.Version;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ServiceDefinitionDeploymentContributor extends ServiceDeploymentContributorBase {
private static final String DISPATCH_ROLE = "dispatch";
private static final String DISPATCH_IMPL_PARAM = "dispatch-impl";
private static final String HTTP_CLIENT_FACTORY_PARAM = "httpClientFactory";
private static final String SERVICE_ROLE_PARAM = "serviceRole";
private static final String XFORWARDED_FILTER_NAME = "XForwardedHeaderFilter";
private static final String XFORWARDED_FILTER_ROLE = "xforwardedheaders";
private static final String DEFAULT_HA_DISPATCH_CLASS = "org.apache.hadoop.gateway.ha.dispatch.DefaultHaDispatch";
private static final String COOKIE_SCOPING_FILTER_NAME = "CookieScopeServletFilter";
private static final String COOKIE_SCOPING_FILTER_ROLE = "cookiescopef";
private ServiceDefinition serviceDefinition;
private UrlRewriteRulesDescriptor serviceRules;
public ServiceDefinitionDeploymentContributor(ServiceDefinition serviceDefinition, UrlRewriteRulesDescriptor serviceRules) {
this.serviceDefinition = serviceDefinition;
this.serviceRules = serviceRules;
}
@Override
public String getRole() {
return serviceDefinition.getRole();
}
@Override
public String getName() {
return serviceDefinition.getName();
}
@Override
public Version getVersion() {
return new Version(serviceDefinition.getVersion());
}
@Override
public void contributeService(DeploymentContext context, Service service) throws Exception {
contributeRewriteRules(context, service);
contributeResources(context, service);
}
private void contributeRewriteRules(DeploymentContext context, Service service) {
if ( serviceRules != null ) {
UrlRewriteRulesDescriptor clusterRules = context.getDescriptor("rewrite");
clusterRules.addRules(serviceRules);
}
}
private void contributeResources(DeploymentContext context, Service service) {
Map<String, String> filterParams = new HashMap<String, String>();
List<Route> bindings = serviceDefinition.getRoutes();
for ( Route binding : bindings ) {
List<Rewrite> filters = binding.getRewrites();
if ( filters != null && !filters.isEmpty() ) {
filterParams.clear();
for ( Rewrite filter : filters ) {
filterParams.put(filter.getTo(), filter.getApply());
}
}
try {
contributeResource(context, service, binding, filterParams);
} catch ( URISyntaxException e ) {
e.printStackTrace();
}
}
}
private void contributeResource(DeploymentContext context, Service service, Route binding, Map<String, String> filterParams) throws URISyntaxException {
List<FilterParamDescriptor> params = new ArrayList<FilterParamDescriptor>();
ResourceDescriptor resource = context.getGatewayDescriptor().addResource();
resource.role(service.getRole());
resource.pattern(binding.getPath());
//add x-forwarded filter if enabled in config
if (context.getGatewayConfig().isXForwardedEnabled()) {
resource.addFilter().name(XFORWARDED_FILTER_NAME).role(XFORWARDED_FILTER_ROLE).impl(XForwardedHeaderFilter.class);
}
if (context.getGatewayConfig().isCookieScopingToPathEnabled()) {
FilterDescriptor filter = resource.addFilter().name(COOKIE_SCOPING_FILTER_NAME).role(COOKIE_SCOPING_FILTER_ROLE).impl(CookieScopeServletFilter.class);
filter.param().name(GatewayConfigImpl.HTTP_PATH).value(context.getGatewayConfig().getGatewayPath());
}
List<Policy> policyBindings = binding.getPolicies();
if ( policyBindings == null ) {
policyBindings = serviceDefinition.getPolicies();
}
if ( policyBindings == null ) {
//add default set
addDefaultPolicies(context, service, filterParams, params, resource);
} else {
addPolicies(context, service, filterParams, params, resource, policyBindings);
}
addDispatchFilter(context, service, resource, binding);
}
private void addPolicies(DeploymentContext context, Service service, Map<String, String> filterParams, List<FilterParamDescriptor> params, ResourceDescriptor resource, List<Policy> policyBindings) throws URISyntaxException {
for ( Policy policyBinding : policyBindings ) {
String role = policyBinding.getRole();
if ( role == null ) {
throw new IllegalArgumentException("Policy defined has no role for service " + service.getName());
}
role = role.trim().toLowerCase();
if ( role.equals("rewrite") ) {
addRewriteFilter(context, service, filterParams, params, resource);
} else if ( topologyContainsProviderType(context, role) ) {
context.contributeFilter(service, resource, role, policyBinding.getName(), null);
}
}
}
private void addDefaultPolicies(DeploymentContext context, Service service, Map<String, String> filterParams, List<FilterParamDescriptor> params, ResourceDescriptor resource) throws URISyntaxException {
addWebAppSecFilters(context, service, resource);
addAuthenticationFilter(context, service, resource);
addRewriteFilter(context, service, filterParams, params, resource);
addIdentityAssertionFilter(context, service, resource);
addAuthorizationFilter(context, service, resource);
}
private void addRewriteFilter(DeploymentContext context, Service service, Map<String, String> filterParams, List<FilterParamDescriptor> params, ResourceDescriptor resource) throws URISyntaxException {
if ( !filterParams.isEmpty() ) {
for ( Map.Entry<String, String> filterParam : filterParams.entrySet() ) {
params.add(resource.createFilterParam().name(filterParam.getKey()).value(filterParam.getValue()));
}
}
addRewriteFilter(context, service, resource, params);
}
private void addDispatchFilter(DeploymentContext context, Service service, ResourceDescriptor resource, Route binding) {
CustomDispatch customDispatch = binding.getDispatch();
if ( customDispatch == null ) {
customDispatch = serviceDefinition.getDispatch();
}
boolean isHaEnabled = isHaEnabled(context);
if ( customDispatch != null ) {
String haContributorName = customDispatch.getHaContributorName();
String haClassName = customDispatch.getHaClassName();
String httpClientFactory = customDispatch.getHttpClientFactory();
if ( isHaEnabled) {
if (haContributorName != null) {
addDispatchFilter(context, service, resource, DISPATCH_ROLE, haContributorName);
} else if (haClassName != null) {
addDispatchFilterForClass(context, service, resource, haClassName, httpClientFactory);
} else {
addDefaultHaDispatchFilter(context, service, resource);
}
} else {
String contributorName = customDispatch.getContributorName();
if ( contributorName != null ) {
addDispatchFilter(context, service, resource, DISPATCH_ROLE, contributorName);
} else {
String className = customDispatch.getClassName();
if ( className != null ) {
addDispatchFilterForClass(context, service, resource, className, httpClientFactory);
} else {
//final fallback to the default dispatch
addDispatchFilter(context, service, resource, DISPATCH_ROLE, "http-client");
}
}
}
} else if (isHaEnabled) {
addDefaultHaDispatchFilter(context, service, resource);
} else {
addDispatchFilter(context, service, resource, DISPATCH_ROLE, "http-client");
}
}
private void addDefaultHaDispatchFilter(DeploymentContext context, Service service, ResourceDescriptor resource) {
FilterDescriptor filter = addDispatchFilterForClass(context, service, resource, DEFAULT_HA_DISPATCH_CLASS, null);
filter.param().name(SERVICE_ROLE_PARAM).value(service.getRole());
}
private FilterDescriptor addDispatchFilterForClass(DeploymentContext context, Service service, ResourceDescriptor resource, String dispatchClass, String httpClientFactory) {
FilterDescriptor filter = resource.addFilter().name(getName()).role(DISPATCH_ROLE).impl(GatewayDispatchFilter.class);
filter.param().name(DISPATCH_IMPL_PARAM).value(dispatchClass);
if (httpClientFactory != null) {
filter.param().name(HTTP_CLIENT_FACTORY_PARAM).value(httpClientFactory);
}
for ( Map.Entry<String, String> serviceParam : service.getParams().entrySet() ) {
filter.param().name(serviceParam.getKey()).value(serviceParam.getValue());
}
if ( context.getGatewayConfig().isHadoopKerberosSecured() ) {
filter.param().name("kerberos").value("true");
} else {
//TODO: [sumit] Get rid of special case. Add config/param capabilities to service definitions?
//special case for hive
filter.param().name("basicAuthPreemptive").value("true");
}
return filter;
}
private boolean isHaEnabled(DeploymentContext context) {
Provider provider = getProviderByRole(context, "ha");
if ( provider != null && provider.isEnabled() ) {
Map<String, String> params = provider.getParams();
if ( params != null ) {
if ( params.containsKey(getRole()) ) {
return true;
}
}
}
return false;
}
}