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
019
020package org.apache.oozie.util;
021
022import java.io.BufferedReader;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.InputStreamReader;
026import java.io.UnsupportedEncodingException;
027import java.net.HttpURLConnection;
028import java.net.URL;
029import java.net.URLEncoder;
030import java.security.PrivilegedExceptionAction;
031import java.util.Map;
032
033import org.apache.hadoop.security.UserGroupInformation;
034import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
035import org.apache.hadoop.security.authentication.client.AuthenticationException;
036import org.apache.hadoop.security.authentication.client.Authenticator;
037import org.apache.hadoop.security.authentication.client.KerberosAuthenticator;
038import org.apache.hadoop.security.authentication.client.PseudoAuthenticator;
039import org.apache.oozie.service.ConfigurationService;
040import org.apache.oozie.service.Services;
041
042public class AuthUrlClient {
043
044    public static final String SERVER_SERVER_AUTH_TYPE = "oozie.server.authentication.type";
045
046    private static XLog LOG = XLog.getLog(AuthUrlClient.class);
047
048    static private Class<? extends Authenticator> AuthenticatorClass = null;
049
050    static private String errorMsg = null;
051
052    static {
053        try {
054            AuthenticatorClass = determineAuthenticatorClassType();
055        }
056        catch (Exception e) {
057            errorMsg = e.getMessage();
058        }
059    }
060
061    private static HttpURLConnection getConnection(URL url) throws IOException {
062        AuthenticatedURL.Token token = new AuthenticatedURL.Token();
063        HttpURLConnection conn;
064        try {
065            conn = new AuthenticatedURL(AuthenticatorClass.newInstance()).openConnection(url, token);
066        }
067        catch (AuthenticationException ex) {
068            throw new IOException("Could not authenticate, " + ex.getMessage(), ex);
069        }
070        catch (InstantiationException ex) {
071            throw new IOException("Could not authenticate, " + ex.getMessage(), ex);
072        }
073        catch (IllegalAccessException ex) {
074            throw new IOException("Could not authenticate, " + ex.getMessage(), ex);
075        }
076        if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
077            throw new IOException("Unexpected response code [" + conn.getResponseCode() + "], message ["
078                    + conn.getResponseMessage() + "]");
079        }
080        return conn;
081    }
082
083    @SuppressWarnings("unchecked")
084    private static Class<? extends Authenticator> determineAuthenticatorClassType() throws Exception {
085        // Adapted from
086        // org.apache.hadoop.security.authentication.server.AuthenticationFilter#init
087        Class<? extends Authenticator> authClass;
088        String authName = ConfigurationService.get(SERVER_SERVER_AUTH_TYPE);
089
090        LOG.info("Oozie server-server authentication is " + authName);
091
092        String authClassName;
093        if (authName == null) {
094            throw new IOException("Authentication type must be specified: simple|kerberos|<class>");
095        }
096        authName = authName.trim();
097        if (authName.equals("simple")) {
098            authClassName = PseudoAuthenticator.class.getName();
099        }
100        else if (authName.equals("kerberos")) {
101            authClassName = KerberosAuthenticator.class.getName();
102        }
103        else {
104            authClassName = authName;
105        }
106
107        authClass = (Class<? extends Authenticator>) Thread.currentThread().getContextClassLoader()
108                .loadClass(authClassName);
109        return authClass;
110    }
111
112    /**
113     * Calls other Oozie server over HTTP.
114     *
115     * @param server The URL of the other Oozie server
116     * @return BufferedReader of inputstream.
117     * @throws IOException Signals that an I/O exception has occurred.
118     */
119    public static BufferedReader callServer(String server) throws IOException {
120
121        if (AuthenticatorClass == null) {
122            throw new IOException(errorMsg);
123        }
124
125        final URL url = new URL(server);
126        BufferedReader reader = null;
127        try {
128            reader = UserGroupInformation.getLoginUser().doAs(new PrivilegedExceptionAction<BufferedReader>() {
129                @Override
130                public BufferedReader run() throws IOException {
131                    HttpURLConnection conn = getConnection(url);
132                    BufferedReader reader = null;
133                    if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
134                        InputStream is = conn.getInputStream();
135                        reader = new BufferedReader(new InputStreamReader(is));
136                    }
137                    return reader;
138                }
139            });
140        }
141        catch (InterruptedException ie) {
142            throw new IOException(ie);
143        }
144        return reader;
145    }
146
147    public static String getQueryParamString(Map<String, String[]> params) throws UnsupportedEncodingException {
148        StringBuilder stringBuilder = new StringBuilder();
149        if (params == null || params.isEmpty()) {
150            return "";
151        }
152        for (String key : params.keySet()) {
153            if (!key.isEmpty() && params.get(key).length > 0) {
154                stringBuilder.append("&");
155                String value = params.get(key)[0]; // We don't support multi value.
156                stringBuilder.append(key);
157                stringBuilder.append("=");
158                stringBuilder.append(URLEncoder.encode(value,"UTF-8"));
159            }
160        }
161        return stringBuilder.toString();
162    }
163
164}