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 package org.apache.hadoop.fs.http.client; 019 020 021 import org.apache.hadoop.classification.InterfaceAudience; 022 import org.apache.hadoop.fs.Path; 023 import org.apache.hadoop.security.SecurityUtil; 024 import org.apache.hadoop.security.authentication.client.AuthenticatedURL; 025 import org.apache.hadoop.security.authentication.client.AuthenticationException; 026 import org.apache.hadoop.security.authentication.client.Authenticator; 027 import org.apache.hadoop.security.authentication.client.KerberosAuthenticator; 028 import org.apache.hadoop.security.token.Token; 029 import org.apache.hadoop.security.token.delegation.AbstractDelegationTokenIdentifier; 030 import org.json.simple.JSONArray; 031 import org.json.simple.JSONObject; 032 033 import java.io.IOException; 034 import java.net.HttpURLConnection; 035 import java.net.InetSocketAddress; 036 import java.net.URI; 037 import java.net.URL; 038 import java.util.ArrayList; 039 import java.util.HashMap; 040 import java.util.List; 041 import java.util.Map; 042 043 /** 044 * A <code>KerberosAuthenticator</code> subclass that fallback to 045 * {@link HttpFSPseudoAuthenticator}. 046 */ 047 @InterfaceAudience.Private 048 public class HttpFSKerberosAuthenticator extends KerberosAuthenticator { 049 050 /** 051 * Returns the fallback authenticator if the server does not use 052 * Kerberos SPNEGO HTTP authentication. 053 * 054 * @return a {@link HttpFSPseudoAuthenticator} instance. 055 */ 056 @Override 057 protected Authenticator getFallBackAuthenticator() { 058 return new HttpFSPseudoAuthenticator(); 059 } 060 061 private static final String HTTP_GET = "GET"; 062 private static final String HTTP_PUT = "PUT"; 063 064 public static final String DELEGATION_PARAM = "delegation"; 065 public static final String TOKEN_PARAM = "token"; 066 public static final String RENEWER_PARAM = "renewer"; 067 public static final String TOKEN_KIND = "HTTPFS_DELEGATION_TOKEN"; 068 public static final String DELEGATION_TOKEN_JSON = "Token"; 069 public static final String DELEGATION_TOKENS_JSON = "Tokens"; 070 public static final String DELEGATION_TOKEN_URL_STRING_JSON = "urlString"; 071 public static final String RENEW_DELEGATION_TOKEN_JSON = "long"; 072 073 /** 074 * DelegationToken operations. 075 */ 076 @InterfaceAudience.Private 077 public static enum DelegationTokenOperation { 078 GETDELEGATIONTOKEN(HTTP_GET, true), 079 GETDELEGATIONTOKENS(HTTP_GET, true), 080 RENEWDELEGATIONTOKEN(HTTP_PUT, true), 081 CANCELDELEGATIONTOKEN(HTTP_PUT, false); 082 083 private String httpMethod; 084 private boolean requiresKerberosCredentials; 085 086 private DelegationTokenOperation(String httpMethod, 087 boolean requiresKerberosCredentials) { 088 this.httpMethod = httpMethod; 089 this.requiresKerberosCredentials = requiresKerberosCredentials; 090 } 091 092 public String getHttpMethod() { 093 return httpMethod; 094 } 095 096 public boolean requiresKerberosCredentials() { 097 return requiresKerberosCredentials; 098 } 099 100 } 101 102 public static void injectDelegationToken(Map<String, String> params, 103 Token<?> dtToken) 104 throws IOException { 105 if (dtToken != null) { 106 params.put(DELEGATION_PARAM, dtToken.encodeToUrlString()); 107 } 108 } 109 110 private boolean hasDelegationToken(URL url) { 111 return url.getQuery().contains(DELEGATION_PARAM + "="); 112 } 113 114 @Override 115 public void authenticate(URL url, AuthenticatedURL.Token token) 116 throws IOException, AuthenticationException { 117 if (!hasDelegationToken(url)) { 118 super.authenticate(url, token); 119 } 120 } 121 122 public static final String OP_PARAM = "op"; 123 124 private static List<Token<?>> getDelegationTokens(URI fsURI, 125 InetSocketAddress httpFSAddr, DelegationTokenOperation op, 126 AuthenticatedURL.Token token, String renewer) 127 throws IOException { 128 Map<String, String> params = new HashMap<String, String>(); 129 params.put(OP_PARAM, op.toString()); 130 params.put(RENEWER_PARAM,renewer); 131 URL url = HttpFSUtils.createHttpURL(new Path(fsURI), params); 132 AuthenticatedURL aUrl = 133 new AuthenticatedURL(new HttpFSKerberosAuthenticator()); 134 try { 135 HttpURLConnection conn = aUrl.openConnection(url, token); 136 conn.setRequestMethod(op.getHttpMethod()); 137 HttpFSUtils.validateResponse(conn, HttpURLConnection.HTTP_OK); 138 List<String> list = new ArrayList<String>(); 139 if (op == DelegationTokenOperation.GETDELEGATIONTOKEN) { 140 JSONObject json = (JSONObject) ((JSONObject) 141 HttpFSUtils.jsonParse(conn)).get(DELEGATION_TOKEN_JSON); 142 String tokenStr = (String) 143 json.get(DELEGATION_TOKEN_URL_STRING_JSON); 144 list.add(tokenStr); 145 } 146 else if (op == DelegationTokenOperation.GETDELEGATIONTOKENS) { 147 JSONObject json = (JSONObject) ((JSONObject) 148 HttpFSUtils.jsonParse(conn)).get(DELEGATION_TOKENS_JSON); 149 JSONArray array = (JSONArray) json.get(DELEGATION_TOKEN_JSON); 150 for (Object element : array) { 151 String tokenStr = (String) 152 ((Map) element).get(DELEGATION_TOKEN_URL_STRING_JSON); 153 list.add(tokenStr); 154 } 155 156 } else { 157 throw new IllegalArgumentException("Invalid operation: " + 158 op.toString()); 159 } 160 List<Token<?>> dTokens = new ArrayList<Token<?>>(); 161 for (String tokenStr : list) { 162 Token<AbstractDelegationTokenIdentifier> dToken = 163 new Token<AbstractDelegationTokenIdentifier>(); 164 dToken.decodeFromUrlString(tokenStr); 165 dTokens.add(dToken); 166 SecurityUtil.setTokenService(dToken, httpFSAddr); 167 } 168 return dTokens; 169 } catch (AuthenticationException ex) { 170 throw new IOException(ex.toString(), ex); 171 } 172 } 173 174 public static List<Token<?>> getDelegationTokens(URI fsURI, 175 InetSocketAddress httpFSAddr, AuthenticatedURL.Token token, 176 String renewer) throws IOException { 177 return getDelegationTokens(fsURI, httpFSAddr, 178 DelegationTokenOperation.GETDELEGATIONTOKENS, token, renewer); 179 } 180 181 public static Token<?> getDelegationToken(URI fsURI, 182 InetSocketAddress httpFSAddr, AuthenticatedURL.Token token, 183 String renewer) throws IOException { 184 return getDelegationTokens(fsURI, httpFSAddr, 185 DelegationTokenOperation.GETDELEGATIONTOKENS, token, renewer).get(0); 186 } 187 188 public static long renewDelegationToken(URI fsURI, 189 AuthenticatedURL.Token token, Token<?> dToken) throws IOException { 190 Map<String, String> params = new HashMap<String, String>(); 191 params.put(OP_PARAM, 192 DelegationTokenOperation.RENEWDELEGATIONTOKEN.toString()); 193 params.put(TOKEN_PARAM, dToken.encodeToUrlString()); 194 URL url = HttpFSUtils.createHttpURL(new Path(fsURI), params); 195 AuthenticatedURL aUrl = 196 new AuthenticatedURL(new HttpFSKerberosAuthenticator()); 197 try { 198 HttpURLConnection conn = aUrl.openConnection(url, token); 199 conn.setRequestMethod( 200 DelegationTokenOperation.RENEWDELEGATIONTOKEN.getHttpMethod()); 201 HttpFSUtils.validateResponse(conn, HttpURLConnection.HTTP_OK); 202 JSONObject json = (JSONObject) ((JSONObject) 203 HttpFSUtils.jsonParse(conn)).get(DELEGATION_TOKEN_JSON); 204 return (Long)(json.get(RENEW_DELEGATION_TOKEN_JSON)); 205 } catch (AuthenticationException ex) { 206 throw new IOException(ex.toString(), ex); 207 } 208 } 209 210 public static void cancelDelegationToken(URI fsURI, 211 AuthenticatedURL.Token token, Token<?> dToken) throws IOException { 212 Map<String, String> params = new HashMap<String, String>(); 213 params.put(OP_PARAM, 214 DelegationTokenOperation.CANCELDELEGATIONTOKEN.toString()); 215 params.put(TOKEN_PARAM, dToken.encodeToUrlString()); 216 URL url = HttpFSUtils.createHttpURL(new Path(fsURI), params); 217 AuthenticatedURL aUrl = 218 new AuthenticatedURL(new HttpFSKerberosAuthenticator()); 219 try { 220 HttpURLConnection conn = aUrl.openConnection(url, token); 221 conn.setRequestMethod( 222 DelegationTokenOperation.CANCELDELEGATIONTOKEN.getHttpMethod()); 223 HttpFSUtils.validateResponse(conn, HttpURLConnection.HTTP_OK); 224 } catch (AuthenticationException ex) { 225 throw new IOException(ex.toString(), ex); 226 } 227 } 228 229 }