001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.oozie.util;
020
021import java.io.BufferedReader;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.InputStreamReader;
025import java.net.HttpURLConnection;
026import java.net.URL;
027import java.security.PrivilegedExceptionAction;
028import java.util.Map;
029
030import org.apache.hadoop.security.UserGroupInformation;
031import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
032import org.apache.hadoop.security.authentication.client.AuthenticationException;
033import org.apache.hadoop.security.authentication.client.Authenticator;
034import org.apache.hadoop.security.authentication.client.KerberosAuthenticator;
035import org.apache.hadoop.security.authentication.client.PseudoAuthenticator;
036import org.apache.oozie.service.Services;
037
038public class AuthUrlClient {
039
040    static private Class<? extends Authenticator> AuthenticatorClass = null;
041
042    static private String errorMsg = null;
043
044    static {
045        try {
046            AuthenticatorClass = determineAuthenticatorClassType();
047        }
048        catch (Exception e) {
049            errorMsg = e.getMessage();
050        }
051    }
052
053    private static HttpURLConnection getConnection(URL url) throws IOException {
054        AuthenticatedURL.Token token = new AuthenticatedURL.Token();
055        HttpURLConnection conn;
056        try {
057            conn = new AuthenticatedURL(AuthenticatorClass.newInstance()).openConnection(url, token);
058        }
059        catch (AuthenticationException ex) {
060            throw new IOException("Could not authenticate, " + ex.getMessage(), ex);
061        }
062        catch (InstantiationException ex) {
063            throw new IOException("Could not authenticate, " + ex.getMessage(), ex);
064        }
065        catch (IllegalAccessException ex) {
066            throw new IOException("Could not authenticate, " + ex.getMessage(), ex);
067        }
068        if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
069            throw new IOException("Unexpected response code [" + conn.getResponseCode() + "], message ["
070                    + conn.getResponseMessage() + "]");
071        }
072        return conn;
073    }
074
075    @SuppressWarnings("unchecked")
076    private static Class<? extends Authenticator> determineAuthenticatorClassType() throws Exception {
077        // Adapted from
078        // org.apache.hadoop.security.authentication.server.AuthenticationFilter#init
079        Class<? extends Authenticator> authClass;
080        String authName = Services.get().getConf().get("oozie.authentication.type");
081        String authClassName;
082        if (authName == null) {
083            throw new IOException("Authentication type must be specified: simple|kerberos|<class>");
084        }
085        authName = authName.trim();
086        if (authName.equals("simple")) {
087            authClassName = PseudoAuthenticator.class.getName();
088        }
089        else if (authName.equals("kerberos")) {
090            authClassName = KerberosAuthenticator.class.getName();
091        }
092        else {
093            authClassName = authName;
094        }
095
096        authClass = (Class<? extends Authenticator>) Thread.currentThread().getContextClassLoader()
097                .loadClass(authClassName);
098        return authClass;
099    }
100
101    /**
102     * Calls other Oozie server over HTTP.
103     *
104     * @param server The URL of the other Oozie server
105     * @return BufferedReader of inputstream.
106     * @throws IOException Signals that an I/O exception has occurred.
107     */
108    public static BufferedReader callServer(String server) throws IOException {
109
110        if (AuthenticatorClass == null) {
111            throw new IOException(errorMsg);
112        }
113
114        final URL url = new URL(server);
115        BufferedReader reader = null;
116        try {
117            reader = UserGroupInformation.getLoginUser().doAs(new PrivilegedExceptionAction<BufferedReader>() {
118                @Override
119                public BufferedReader run() throws IOException {
120                    HttpURLConnection conn = getConnection(url);
121                    BufferedReader reader = null;
122                    if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
123                        InputStream is = conn.getInputStream();
124                        reader = new BufferedReader(new InputStreamReader(is));
125                    }
126                    return reader;
127                }
128            });
129        }
130        catch (InterruptedException ie) {
131            throw new IOException(ie);
132        }
133        return reader;
134    }
135
136    public static String getQueryParamString(Map<String, String[]> params) {
137        StringBuilder stringBuilder = new StringBuilder();
138        if (params == null || params.isEmpty()) {
139            return "";
140        }
141        for (String key : params.keySet()) {
142            if (!key.isEmpty() && params.get(key).length > 0) {
143                stringBuilder.append("&");
144                String value = params.get(key)[0]; // We don't support multi value.
145                stringBuilder.append(key);
146                stringBuilder.append("=");
147                stringBuilder.append(value);
148            }
149        }
150        return stringBuilder.toString();
151    }
152
153}