001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.oozie.servlet;
019
020import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
021import org.apache.hadoop.conf.Configuration;
022import org.apache.oozie.service.Services;
023
024import javax.servlet.FilterChain;
025import javax.servlet.FilterConfig;
026import javax.servlet.ServletException;
027import javax.servlet.ServletRequest;
028import javax.servlet.ServletResponse;
029import javax.servlet.http.HttpServlet;
030import javax.servlet.http.HttpServletRequest;
031import java.io.IOException;
032import java.util.Map;
033import java.util.Properties;
034
035/**
036 * Authentication filter that extends Hadoop-auth AuthenticationFilter to override
037 * the configuration loading.
038 */
039public class AuthFilter extends AuthenticationFilter {
040    private static final String OOZIE_PREFIX = "oozie.authentication.";
041
042    private HttpServlet optionsServlet;
043
044    /**
045     * Initialize the filter.
046     *
047     * @param filterConfig filter configuration.
048     * @throws ServletException thrown if the filter could not be initialized.
049     */
050    @Override
051    public void init(FilterConfig filterConfig) throws ServletException {
052        super.init(filterConfig);
053        optionsServlet = new HttpServlet() {};
054        optionsServlet.init();
055    }
056
057    /**
058     * Destroy the filter.
059     */
060    @Override
061    public void destroy() {
062        optionsServlet.destroy();
063        super.destroy();
064    }
065
066    /**
067     * Returns the configuration from Oozie configuration to be used by the authentication filter.
068     * <p/>
069     * All properties from Oozie configuration which name starts with {@link #OOZIE_PREFIX} will
070     * be returned. The keys of the returned properties are trimmed from the {@link #OOZIE_PREFIX}
071     * prefix, for example the Oozie configuration property name 'oozie.authentication.type' will
072     * be just 'type'.
073     *
074     * @param configPrefix configuration prefix, this parameter is ignored by this implementation.
075     * @param filterConfig filter configuration, this parameter is ignored by this implementation.
076     * @return all Oozie configuration properties prefixed with {@link #OOZIE_PREFIX}, without the
077     * prefix.
078     */
079    @Override
080    protected Properties getConfiguration(String configPrefix, FilterConfig filterConfig) {
081        Properties props = new Properties();
082        Configuration conf = Services.get().getConf();
083
084        //setting the cookie path to root '/' so it is used for all resources.
085        props.setProperty(AuthenticationFilter.COOKIE_PATH, "/");
086
087        for (Map.Entry<String, String> entry : conf) {
088            String name = entry.getKey();
089            if (name.startsWith(OOZIE_PREFIX)) {
090                String value = conf.get(name);
091                name = name.substring(OOZIE_PREFIX.length());
092                props.setProperty(name, value);
093            }
094        }
095
096        return props;
097    }
098
099    /**
100     * Enforces authentication using Hadoop-auth AuthenticationFilter.
101     * <p/>
102     * This method is overriden to respond to HTTP OPTIONS requests for authenticated calls, regardless
103     * of the target servlet supporting OPTIONS or not and to inject the authenticated user name as
104     * request attribute for Oozie to retrieve the user id.
105     *
106     * @param request http request.
107     * @param response http response.
108     * @param filterChain filter chain.
109     * @throws IOException thrown if an IO error occurs.
110     * @throws ServletException thrown if a servlet error occurs.
111     */
112    @Override
113    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain)
114            throws IOException, ServletException {
115
116        FilterChain filterChainWrapper = new FilterChain() {
117            @Override
118            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse)
119                    throws IOException, ServletException {
120                HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
121                if (httpRequest.getMethod().equals("OPTIONS")) {
122                    optionsServlet.service(request, response);
123                }
124                else {
125                  httpRequest.setAttribute(JsonRestServlet.USER_NAME, httpRequest.getRemoteUser());
126                  filterChain.doFilter(servletRequest, servletResponse);
127                }
128            }
129        };
130
131        super.doFilter(request, response, filterChainWrapper);
132    }
133
134}