org.apache.hive.hcatalog.templeton.ProxyUserSupport.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hive.hcatalog.templeton.ProxyUserSupport.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.hive.hcatalog.templeton;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.security.Groups;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * When WebHCat is run with doAs query parameter this class ensures that user making the
 * call is allowed to impersonate doAs user and is making a call from authorized host.
 */
final class ProxyUserSupport {
    private static final Logger LOG = LoggerFactory.getLogger(ProxyUserSupport.class);
    private static final String CONF_PROXYUSER_PREFIX = "webhcat.proxyuser.";
    private static final String CONF_GROUPS_SUFFIX = ".groups";
    private static final String CONF_HOSTS_SUFFIX = ".hosts";
    private static final Set<String> WILD_CARD = Collections.unmodifiableSet(new HashSet<String>(0));
    private static final Map<String, Set<String>> proxyUserGroups = new HashMap<String, Set<String>>();
    private static final Map<String, Set<String>> proxyUserHosts = new HashMap<String, Set<String>>();

    static void processProxyuserConfig(AppConfig conf) {
        for (Map.Entry<String, String> confEnt : conf) {
            if (confEnt.getKey().startsWith(CONF_PROXYUSER_PREFIX)
                    && confEnt.getKey().endsWith(CONF_GROUPS_SUFFIX)) {
                //process user groups for which doAs is authorized
                String proxyUser = confEnt.getKey().substring(CONF_PROXYUSER_PREFIX.length(),
                        confEnt.getKey().lastIndexOf(CONF_GROUPS_SUFFIX));
                Set<String> groups;
                if ("*".equals(confEnt.getValue())) {
                    groups = WILD_CARD;
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("User [" + proxyUser + "] is authorized to do doAs any user.");
                    }
                } else if (confEnt.getValue() != null && confEnt.getValue().trim().length() > 0) {
                    groups = new HashSet<String>(Arrays.asList(confEnt.getValue().trim().split(",")));
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("User [" + proxyUser
                                + "] is authorized to do doAs for users in the following groups: ["
                                + confEnt.getValue().trim() + "]");
                    }
                } else {
                    groups = Collections.emptySet();
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("User [" + proxyUser
                                + "] is authorized to do doAs for users in the following groups: []");
                    }
                }
                proxyUserGroups.put(proxyUser, groups);
            } else if (confEnt.getKey().startsWith(CONF_PROXYUSER_PREFIX)
                    && confEnt.getKey().endsWith(CONF_HOSTS_SUFFIX)) {
                //process hosts from which doAs requests are authorized
                String proxyUser = confEnt.getKey().substring(CONF_PROXYUSER_PREFIX.length(),
                        confEnt.getKey().lastIndexOf(CONF_HOSTS_SUFFIX));
                Set<String> hosts;
                if ("*".equals(confEnt.getValue())) {
                    hosts = WILD_CARD;
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("User [" + proxyUser + "] is authorized to do doAs from any host.");
                    }
                } else if (confEnt.getValue() != null && confEnt.getValue().trim().length() > 0) {
                    String[] hostValues = confEnt.getValue().trim().split(",");
                    hosts = new HashSet<String>();
                    for (String hostname : hostValues) {
                        String nhn = normalizeHostname(hostname);
                        if (nhn != null) {
                            hosts.add(nhn);
                        }
                    }
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("User [" + proxyUser + "] is authorized to do doAs from the following hosts: ["
                                + confEnt.getValue().trim() + "]");
                    }
                } else {
                    hosts = Collections.emptySet();
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("User [" + proxyUser + "] is authorized to do doAs from the following hosts: []");
                    }
                }
                proxyUserHosts.put(proxyUser, hosts);
            }
        }
    }

    /**
     * Verifies a that proxyUser is making the request from authorized host and that doAs user
     * belongs to one of the groups for which proxyUser is allowed to impersonate users.
     *
     * @param proxyUser user name of the proxy (logged in) user.
     * @param proxyHost host the proxy user is making the request from.
     * @param doAsUser user the proxy user is impersonating.
     * @throws NotAuthorizedException thrown if the user is not allowed to perform the proxyuser request.
     */
    static void validate(String proxyUser, String proxyHost, String doAsUser) throws NotAuthorizedException {
        assertNotEmpty(proxyUser, "proxyUser",
                "If you're attempting to use user-impersonation via a proxy user, please make sure that "
                        + CONF_PROXYUSER_PREFIX + "#USER#" + CONF_HOSTS_SUFFIX + " and " + CONF_PROXYUSER_PREFIX
                        + "#USER#" + CONF_GROUPS_SUFFIX + " are configured correctly");
        assertNotEmpty(proxyHost, "proxyHost",
                "If you're attempting to use user-impersonation via a proxy user, please make sure that "
                        + CONF_PROXYUSER_PREFIX + proxyUser + CONF_HOSTS_SUFFIX + " and " + CONF_PROXYUSER_PREFIX
                        + proxyUser + CONF_GROUPS_SUFFIX + " are configured correctly");
        assertNotEmpty(doAsUser, Server.DO_AS_PARAM);
        LOG.debug(MessageFormat.format("Authorization check proxyuser [{0}] host [{1}] doAs [{2}]", proxyUser,
                proxyHost, doAsUser));
        if (proxyUserHosts.containsKey(proxyUser)) {
            proxyHost = normalizeHostname(proxyHost);
            validateRequestorHost(proxyUser, proxyHost);
            validateGroup(proxyUser, doAsUser);
        } else {
            throw new NotAuthorizedException(
                    MessageFormat.format("User [{0}] not defined as proxyuser", proxyUser));
        }
    }

    private static void validateRequestorHost(String proxyUser, String hostname) throws NotAuthorizedException {
        Set<String> validHosts = proxyUserHosts.get(proxyUser);
        if (validHosts == WILD_CARD) {
            return;
        }
        if (validHosts == null || !validHosts.contains(hostname)) {
            throw new NotAuthorizedException(
                    MessageFormat.format("Unauthorized host [{0}] for proxyuser [{1}]", hostname, proxyUser));
        }
    }

    private static void validateGroup(String proxyUser, String doAsUser) throws NotAuthorizedException {
        Set<String> validGroups = proxyUserGroups.get(proxyUser);
        if (validGroups == WILD_CARD) {
            return;
        } else if (validGroups == null || validGroups.isEmpty()) {
            throw new NotAuthorizedException(
                    MessageFormat.format("Unauthorized proxyuser [{0}] for doAsUser [{1}], not in proxyuser groups",
                            proxyUser, doAsUser));
        }
        Groups groupsInfo = new Groups(Main.getAppConfigInstance());
        try {
            List<String> userGroups = groupsInfo.getGroups(doAsUser);
            for (String g : validGroups) {
                if (userGroups.contains(g)) {
                    return;
                }
            }
        } catch (IOException ex) {//thrown, for example, if there is no such user on the system
            LOG.warn(MessageFormat.format("Unable to get list of groups for doAsUser [{0}].", doAsUser), ex);
        }
        throw new NotAuthorizedException(MessageFormat.format(
                "Unauthorized proxyuser [{0}] for doAsUser [{1}], not in proxyuser groups", proxyUser, doAsUser));
    }

    private static String normalizeHostname(String name) {
        try {
            InetAddress address = InetAddress.getByName("localhost".equalsIgnoreCase(name) ? null : name);
            return address.getCanonicalHostName();
        } catch (UnknownHostException ex) {
            LOG.warn(MessageFormat.format("Unable to normalize hostname [{0}]", name));
            return null;
        }
    }

    /**
     * Check that a string is not null and not empty. If null or empty 
     * throws an IllegalArgumentException.
     *
     * @param str value.
     * @param name parameter name for the exception message.
     * @return the given value.
     */
    private static String assertNotEmpty(String str, String name) {
        return assertNotEmpty(str, name, null);
    }

    /**
     * Check that a string is not null and not empty. If null or empty 
     * throws an IllegalArgumentException.
     *
     * @param str value.
     * @param name parameter name for the exception message.
     * @param info additional information to be printed with the exception message
     * @return the given value.
     */
    private static String assertNotEmpty(String str, String name, String info) {
        if (str == null) {
            throw new IllegalArgumentException(name + " cannot be null" + (info == null ? "" : ", " + info));
        }
        if (str.length() == 0) {
            throw new IllegalArgumentException(name + " cannot be empty" + (info == null ? "" : ", " + info));
        }
        return str;
    }
}