co.cask.cdap.gateway.handlers.ImpersonationHandler.java Source code

Java tutorial

Introduction

Here is the source code for co.cask.cdap.gateway.handlers.ImpersonationHandler.java

Source

/*
 * Copyright  2016-2017 Cask Data, Inc.
 *
 * Licensed 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 co.cask.cdap.gateway.handlers;

import co.cask.cdap.common.BadRequestException;
import co.cask.cdap.common.kerberos.ImpersonationInfo;
import co.cask.cdap.security.TokenSecureStoreUpdater;
import co.cask.cdap.security.impersonation.ImpersonationUtils;
import co.cask.cdap.security.impersonation.UGIProvider;
import co.cask.http.AbstractHttpHandler;
import co.cask.http.HttpResponder;
import com.google.common.base.Charsets;
import com.google.gson.Gson;
import com.google.inject.Inject;
import org.apache.hadoop.security.Credentials;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.twill.api.SecureStore;
import org.apache.twill.filesystem.Location;
import org.apache.twill.filesystem.LocationFactory;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.util.concurrent.Callable;
import javax.ws.rs.POST;
import javax.ws.rs.Path;

/**
 * Resolves the UGI for a given namespace, acquires the delegation tokens for that UGI,
 * using {@link TokenSecureStoreUpdater}, and serializes these Credentials to a location.
 *
 * Response with the location to which the credentials were serialized to, as well as the UGI's short username
 */
// we don't share the same version as other handlers, so we can upgrade/iterate faster
@Path("/v1/impersonation")
public class ImpersonationHandler extends AbstractHttpHandler {

    private static final Logger LOG = LoggerFactory.getLogger(ImpersonationHandler.class);
    private static final Gson GSON = new Gson();

    private final UGIProvider ugiProvider;
    private final TokenSecureStoreUpdater tokenSecureStoreUpdater;
    private final LocationFactory locationFactory;

    @Inject
    ImpersonationHandler(UGIProvider ugiProvider, TokenSecureStoreUpdater tokenSecureStoreUpdater,
            LocationFactory locationFactory) {
        this.ugiProvider = ugiProvider;
        this.tokenSecureStoreUpdater = tokenSecureStoreUpdater;
        this.locationFactory = locationFactory;
    }

    @POST
    @Path("/credentials")
    public void getCredentials(HttpRequest request, HttpResponder responder) throws Exception {
        String requestContent = request.getContent().toString(Charsets.UTF_8);
        if (requestContent == null) {
            throw new BadRequestException("Request body is empty.");
        }
        ImpersonationInfo impersonationInfo = GSON.fromJson(requestContent, ImpersonationInfo.class);
        LOG.info("Credentials for {}", impersonationInfo);
        UserGroupInformation ugi = ugiProvider.getConfiguredUGI(impersonationInfo);
        Credentials credentials = ImpersonationUtils.doAs(ugi, new Callable<Credentials>() {
            @Override
            public Credentials call() throws Exception {
                SecureStore update = tokenSecureStoreUpdater.update();
                return update.getStore();
            }
        });

        // example: hdfs:///cdap/credentials
        Location credentialsDir = locationFactory.create("credentials");
        if (credentialsDir.isDirectory() || credentialsDir.mkdirs() || credentialsDir.isDirectory()) {

            // the getTempFile() doesn't create the file within the directory that you call it on. It simply appends the path
            // without a separator, which is why we manually append the "tmp"
            // example: hdfs:///cdap/credentials/tmp.5960fe60-6fd8-4f3e-8e92-3fb6d4726006.credentials
            Location credentialsFile = credentialsDir.append("tmp").getTempFile(".credentials");
            // 600 is owner-only READ_WRITE
            try (DataOutputStream os = new DataOutputStream(
                    new BufferedOutputStream(credentialsFile.getOutputStream("600")))) {
                credentials.writeTokenStorageToStream(os);
            }
            LOG.debug("Wrote credentials for user {} to {}", ugi.getUserName(), credentialsFile);
            responder.sendString(HttpResponseStatus.OK, credentialsFile.toURI().toString());
        } else {
            throw new IllegalStateException("Unable to create credentails directory.");
        }
    }
}