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 */ 018package org.apache.hadoop.log; 019 020import java.io.IOException; 021import java.net.HttpURLConnection; 022import java.net.URL; 023import java.net.URLConnection; 024 025import org.apache.hadoop.conf.Configuration; 026import org.apache.hadoop.fs.CommonConfigurationKeysPublic; 027import org.apache.hadoop.security.authentication.client.AuthenticatedURL; 028import org.apache.hadoop.security.authentication.client.AuthenticatedURL.Token; 029import org.apache.hadoop.security.authentication.client.AuthenticationException; 030import org.apache.hadoop.security.authentication.client.Authenticator; 031import org.apache.log4j.Logger; 032 033/** 034 * 1. Returns a usable https connection if an authenticator is configured; 035 * otherwise returns the given connection as is. 036 * 037 * 2. Caches a authentication token and re-uses it for connections long as it is 038 * valid. 039 * 040 * 3. Performs authentication via configured authenticator only if the token 041 * becomes invalid. 042 * <p> 043 * TODO 044 * 1. This code is not cluster aware (it always watches if security is enabled 045 * or not on the current cluster) 046 * 047 * 2. This code is not user aware (the authenticator will pick up creds of the 048 * process uid that is running this code) 049 */ 050public class SecureHTTPURLConnectionProvider { 051 private static final Logger LOG = Logger.getLogger(SecureHTTPURLConnectionProvider.class); 052 053 private static Token token = new Token(); 054 055 private static Authenticator authenticator = null; 056 057 static { 058 Configuration conf = new Configuration(); 059 Class<? extends Authenticator> clazz = conf.getClass( 060 CommonConfigurationKeysPublic.LOG_LEVEL_AUTHENTICATOR_CLASS, 061 null, Authenticator.class); 062 063 if (clazz != null) { 064 try { 065 authenticator = clazz.getDeclaredConstructor() 066 .newInstance(); 067 } catch (Exception e) { 068 throw new RuntimeException(e); 069 } 070 } 071 } 072 073 public static URLConnection openConnection(URL url) throws IOException { 074 if (authenticator == null) { 075 return url.openConnection(); 076 } 077 078 synchronized (SecureHTTPURLConnectionProvider.class) { 079 080 HttpURLConnection conn = null; 081 if (token.isSet()) { // try the existing token 082 conn = (HttpURLConnection) url.openConnection(); 083 AuthenticatedURL.injectToken(conn, token); 084 conn.connect(); 085 if (conn.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) { 086 token = new Token(); // refresh the token on auth failure 087 if (LOG.isDebugEnabled()) { 088 LOG.debug("Received HTTP " + conn.getResponseCode() + ". Created a new token."); 089 } 090 } else { 091 if (LOG.isDebugEnabled()) { 092 LOG.debug("Received HTTP " + conn.getResponseCode() + ". Token still good.."); 093 } 094 } 095 } 096 097 if (!token.isSet()) { // authenticate only if the token is not set 098 LOG.debug("Token not set. Peforming authentication.."); 099 AuthenticatedURL authenticatedURL = new AuthenticatedURL(authenticator); 100 try { 101 conn = authenticatedURL.openConnection(url, token); 102 } catch (AuthenticationException e) { 103 LOG.error("Authentication failed while connecting to URL: " + url.toString()); 104 } 105 } 106 107 return conn; 108 } 109 } 110}