org.apache.solr.security.BasicAuthPlugin.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.solr.security.BasicAuthPlugin.java

Source

/*
 * 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.solr.security;

import javax.servlet.FilterChain;
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.io.UnsupportedEncodingException;
import java.lang.invoke.MethodHandles;
import java.security.Principal;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import com.google.common.collect.ImmutableSet;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
import org.apache.http.auth.BasicUserPrincipal;
import org.apache.http.message.BasicHeader;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.ValidatingJsonMap;
import org.apache.solr.util.CommandOperation;
import org.apache.solr.api.SpecProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BasicAuthPlugin extends AuthenticationPlugin implements ConfigEditablePlugin, SpecProvider {
    private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
    private AuthenticationProvider authenticationProvider;
    private final static ThreadLocal<Header> authHeader = new ThreadLocal<>();
    private boolean blockUnknown = false;

    public boolean authenticate(String username, String pwd) {
        return authenticationProvider.authenticate(username, pwd);
    }

    @Override
    public void init(Map<String, Object> pluginConfig) {
        Object o = pluginConfig.get(BLOCK_UNKNOWN);
        if (o != null) {
            try {
                blockUnknown = Boolean.parseBoolean(o.toString());
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }
        authenticationProvider = getAuthenticationProvider(pluginConfig);
    }

    @Override
    public Map<String, Object> edit(Map<String, Object> latestConf, List<CommandOperation> commands) {
        for (CommandOperation command : commands) {
            if (command.name.equals("set-property")) {
                for (Map.Entry<String, Object> e : command.getDataMap().entrySet()) {
                    if (PROPS.contains(e.getKey())) {
                        latestConf.put(e.getKey(), e.getValue());
                        return latestConf;
                    } else {
                        command.addError("Unknown property " + e.getKey());
                    }
                }
            }
        }
        if (!CommandOperation.captureErrors(commands).isEmpty())
            return null;
        if (authenticationProvider instanceof ConfigEditablePlugin) {
            ConfigEditablePlugin editablePlugin = (ConfigEditablePlugin) authenticationProvider;
            return editablePlugin.edit(latestConf, commands);
        }
        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "This cannot be edited");
    }

    protected AuthenticationProvider getAuthenticationProvider(Map<String, Object> pluginConfig) {
        Sha256AuthenticationProvider provider = new Sha256AuthenticationProvider();
        provider.init(pluginConfig);
        return provider;
    }

    private void authenticationFailure(HttpServletResponse response, String message) throws IOException {
        for (Map.Entry<String, String> entry : authenticationProvider.getPromptHeaders().entrySet()) {
            response.setHeader(entry.getKey(), entry.getValue());
        }
        response.sendError(401, message);
    }

    @Override
    public boolean doAuthenticate(ServletRequest servletRequest, ServletResponse servletResponse,
            FilterChain filterChain) throws Exception {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        String authHeader = request.getHeader("Authorization");
        if (authHeader != null) {
            BasicAuthPlugin.authHeader.set(new BasicHeader("Authorization", authHeader));
            StringTokenizer st = new StringTokenizer(authHeader);
            if (st.hasMoreTokens()) {
                String basic = st.nextToken();
                if (basic.equalsIgnoreCase("Basic")) {
                    try {
                        String credentials = new String(Base64.decodeBase64(st.nextToken()), "UTF-8");
                        int p = credentials.indexOf(":");
                        if (p != -1) {
                            final String username = credentials.substring(0, p).trim();
                            String pwd = credentials.substring(p + 1).trim();
                            if (!authenticate(username, pwd)) {
                                log.debug("Bad auth credentials supplied in Authorization header");
                                authenticationFailure(response, "Bad credentials");
                            } else {
                                HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
                                    @Override
                                    public Principal getUserPrincipal() {
                                        return new BasicUserPrincipal(username);
                                    }
                                };
                                filterChain.doFilter(wrapper, response);
                                return true;
                            }

                        } else {
                            authenticationFailure(response, "Invalid authentication token");
                        }
                    } catch (UnsupportedEncodingException e) {
                        throw new Error("Couldn't retrieve authentication", e);
                    }
                }
            }
        } else {
            if (blockUnknown) {
                authenticationFailure(response, "require authentication");
            } else {
                request.setAttribute(AuthenticationPlugin.class.getName(),
                        authenticationProvider.getPromptHeaders());
                filterChain.doFilter(request, response);
                return true;
            }
        }
        return false;
    }

    @Override
    public void close() throws IOException {

    }

    @Override
    public void closeRequest() {
        authHeader.remove();
    }

    public interface AuthenticationProvider extends SpecProvider {
        void init(Map<String, Object> pluginConfig);

        boolean authenticate(String user, String pwd);

        Map<String, String> getPromptHeaders();
    }

    @Override
    public ValidatingJsonMap getSpec() {
        return authenticationProvider.getSpec();
    }

    public boolean getBlockUnknown() {
        return blockUnknown;
    }

    public static final String BLOCK_UNKNOWN = "blockUnknown";
    private static final Set<String> PROPS = ImmutableSet.of(BLOCK_UNKNOWN);

}