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