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; 040 041public class AuthUrlClient { 042 043 public static final String SERVER_SERVER_AUTH_TYPE = "oozie.server.authentication.type"; 044 public static final String SERVER_SERVER_CONNECTION_TIMEOUT_SECONDS = "oozie.server.connection.timeout.seconds"; 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 conn.setConnectTimeout(ConfigurationService.getInt(SERVER_SERVER_CONNECTION_TIMEOUT_SECONDS, 180)); 133 BufferedReader reader = null; 134 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 135 InputStream is = conn.getInputStream(); 136 reader = new BufferedReader(new InputStreamReader(is)); 137 } 138 return reader; 139 } 140 }); 141 } 142 catch (InterruptedException ie) { 143 throw new IOException(ie); 144 } 145 return reader; 146 } 147 148 public static String getQueryParamString(Map<String, String[]> params) throws UnsupportedEncodingException { 149 StringBuilder stringBuilder = new StringBuilder(); 150 if (params == null || params.isEmpty()) { 151 return ""; 152 } 153 for (String key : params.keySet()) { 154 if (!key.isEmpty() && params.get(key).length > 0) { 155 stringBuilder.append("&"); 156 String value = params.get(key)[0]; // We don't support multi value. 157 stringBuilder.append(key); 158 stringBuilder.append("="); 159 stringBuilder.append(URLEncoder.encode(value,"UTF-8")); 160 } 161 } 162 return stringBuilder.toString(); 163 } 164 165}