All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.hadoop.mr.security.TokenUtil Maven / Gradle / Ivy

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch 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.elasticsearch.hadoop.mr.security;

import java.io.IOException;
import java.security.PrivilegedExceptionAction;
import java.util.UUID;

import javax.security.auth.kerberos.KerberosPrincipal;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.security.token.Token;
import org.elasticsearch.hadoop.EsHadoopException;
import org.elasticsearch.hadoop.rest.RestClient;
import org.elasticsearch.hadoop.security.EsToken;
import org.elasticsearch.hadoop.security.User;
import org.elasticsearch.hadoop.util.Assert;
import org.elasticsearch.hadoop.util.ClusterName;

/**
 * Provides the main logic for obtaining Hadoop specific Tokens containing Elasticsearch authentication
 * info, caching them in User objects (Subjects or Hadoop UGI), and adding them to jobs and job configurations.
 */
public class TokenUtil {

    private static Log LOG = LogFactory.getLog(TokenUtil.class);

    public static final String KEY_NAME_PREFIX = "ESHADOOP_";

    /**
     * Generates a new unique name for an API Key
     * @return unique api key name
     */
    private static String newKeyName() {
        return KEY_NAME_PREFIX + UUID.randomUUID().toString();
    }

    /**
     * Obtain the given user's authentication token from Elasticsearch by performing the getAuthToken operation
     * as the given user, thus ensuring the subject's private credentials available on the thread's access control
     * context for the life of the operation.
     * @param client The Elasticsearch client
     * @param user the user object that contains credentials for obtaining an auth token
     * @return the authentication token in ES-Hadoop specific format.
     */
    private static EsToken obtainEsToken(final RestClient client, User user) {
        // TODO: Should we extend this to basic authentication at some point?
        KerberosPrincipal principal = user.getKerberosPrincipal();
        if (user.isProxyUser()) {
            principal = user.getRealUserProvider().getUser().getKerberosPrincipal();
        }
        Assert.isTrue(principal != null, "Kerberos credentials are missing on current user");
        return user.doAs(new PrivilegedExceptionAction() {
            @Override
            public EsToken run() {
                return client.createNewApiToken(newKeyName());
            }
        });
    }

    /**
     * Obtain and return an authentication token for the current user.
     * @param client The Elasticsearch client
     * @return the authentication token instance in Hadoop Token format.
     */
    public static Token obtainToken(RestClient client, User user) {
        EsToken esToken = obtainEsToken(client, user);
        return EsTokenIdentifier.createTokenFrom(esToken);
    }

    /**
     * Obtain an authentication token for the given user and add it to the
     * user's credentials.
     * @param client The Elasticsearch client
     * @param user The user for obtaining and storing the token
     * @throws IOException If making a remote call to the authentication service fails
     */
    public static void obtainAndCache(RestClient client, User user) throws IOException {
        EsToken token = obtainEsToken(client, user);

        if (token == null) {
            throw new IOException("No token returned for user " + user.getKerberosPrincipal().getName());
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug("Obtained token " + EsTokenIdentifier.KIND_NAME + " for user " +
                    user.getKerberosPrincipal().getName());
        }
        user.addEsToken(token);
    }

    /**
     * Obtain an authentication token on behalf of the given user and add it to
     * the credentials for the given map reduce job. This version always obtains
     * a fresh authentication token instead of checking for existing ones on the
     * current user.
     *
     * @param client The Elasticsearch client
     * @param user The user for whom to obtain the token
     * @param job The job instance in which the token should be stored
     */
    public static void obtainTokenForJob(final RestClient client, User user, Job job) {
        Token token = obtainToken(client, user);
        if (token == null) {
            throw new EsHadoopException("No token returned for user " + user.getKerberosPrincipal().getName());
        }
        Text clusterName = token.getService();
        if (LOG.isDebugEnabled()) {
            LOG.debug("Obtained token " + EsTokenIdentifier.KIND_NAME.toString() + " for user " +
                    user.getKerberosPrincipal().getName() + " on cluster " + clusterName.toString());
        }
        job.getCredentials().addToken(clusterName, token);
    }

    /**
     * Obtain an authentication token on behalf of the given user and add it to
     * the credentials for the given map reduce job. This version always obtains
     * a fresh authentication token instead of checking for existing ones on the
     * current user.
     *
     * @param client The Elasticsearch client
     * @param user The user for whom to obtain the token
     * @param jobConf The job configuration in which the token should be stored
     */
    public static void obtainTokenForJob(final RestClient client, User user, JobConf jobConf) {
        Token token = obtainToken(client, user);
        if (token == null) {
            throw new EsHadoopException("No token returned for user " + user.getKerberosPrincipal().getName());
        }
        Text clusterName = token.getService();
        if (LOG.isDebugEnabled()) {
            LOG.debug("Obtained token " + EsTokenIdentifier.KIND_NAME.toString() + " for user " +
                    user.getKerberosPrincipal().getName() + " on cluster " + clusterName.toString());
        }
        jobConf.getCredentials().addToken(clusterName, token);
    }

    /**
     * Retrieves an authentication token from the given user, obtaining a new token if necessary,
     * and adds it to the credentials for the given map reduce job.
     *
     * @param client The Elasticsearch client
     * @param clusterName the name of the cluster you are connecting to
     * @param user The user for whom to obtain the token
     * @param job The job instance in which the token should be stored
     * @throws IOException If making a remote call fails
     * @throws InterruptedException If executing as the given user is interrupted
     */
    public static void addTokenForJob(final RestClient client, ClusterName clusterName, User user, Job job) {
        Token token = getAuthToken(clusterName, user);
        if (token == null) {
            token = obtainToken(client, user);
        }
        job.getCredentials().addToken(token.getService(), token);
    }

    /**
     * Retrieves an authentication token from the given user, obtaining a new token if necessary,
     * and adds it to the credentials for the given map reduce job configuration.
     *
     * @param client The Elasticsearch client
     * @param clusterName the name of the cluster you are connecting to
     * @param user The user for whom to obtain the token
     * @param job The job instance in which the token should be stored
     * @throws IOException If making a remote call fails
     * @throws InterruptedException If executing as the given user is interrupted
     */
    public static void addTokenForJobConf(final RestClient client, ClusterName clusterName, User user, JobConf job) {
        Token token = getAuthToken(clusterName, user);
        if (token == null) {
            token = obtainToken(client, user);
        }
        job.getCredentials().addToken(token.getService(), token);
    }

    /**
     * Get the authentication token of the user for the provided cluster name in its Hadoop Token form.
     * @return null if the user does not have the token, otherwise the auth token for the cluster.
     */
    private static Token getAuthToken(ClusterName clusterName, User user) {
        EsToken esToken = getEsAuthToken(clusterName, user);
        if (esToken == null) {
            return null;
        } else {
            return EsTokenIdentifier.createTokenFrom(esToken);
        }
    }

    /**
     * Get the authentication token of the user for the provided cluster name in its ES-Hadoop specific form.
     * @return null if the user does not have the token, otherwise the auth token for the cluster.
     */
    private static EsToken getEsAuthToken(ClusterName clusterName, User user) {
        return user.getEsToken(clusterName.getName());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy