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.JSONObject; 031 032 import java.io.IOException; 033 import java.net.HttpURLConnection; 034 import java.net.InetSocketAddress; 035 import java.net.URI; 036 import java.net.URL; 037 import java.util.HashMap; 038 import java.util.Map; 039 040 /** 041 * A <code>KerberosAuthenticator</code> subclass that fallback to 042 * {@link HttpFSPseudoAuthenticator}. 043 */ 044 @InterfaceAudience.Private 045 public class HttpFSKerberosAuthenticator extends KerberosAuthenticator { 046 047 /** 048 * Returns the fallback authenticator if the server does not use 049 * Kerberos SPNEGO HTTP authentication. 050 * 051 * @return a {@link HttpFSPseudoAuthenticator} instance. 052 */ 053 @Override 054 protected Authenticator getFallBackAuthenticator() { 055 return new HttpFSPseudoAuthenticator(); 056 } 057 058 private static final String HTTP_GET = "GET"; 059 private static final String HTTP_PUT = "PUT"; 060 061 public static final String DELEGATION_PARAM = "delegation"; 062 public static final String TOKEN_PARAM = "token"; 063 public static final String RENEWER_PARAM = "renewer"; 064 public static final String TOKEN_KIND = "HTTPFS_DELEGATION_TOKEN"; 065 public static final String DELEGATION_TOKEN_JSON = "Token"; 066 public static final String DELEGATION_TOKEN_URL_STRING_JSON = "urlString"; 067 public static final String RENEW_DELEGATION_TOKEN_JSON = "long"; 068 069 /** 070 * DelegationToken operations. 071 */ 072 @InterfaceAudience.Private 073 public static enum DelegationTokenOperation { 074 GETDELEGATIONTOKEN(HTTP_GET, true), 075 RENEWDELEGATIONTOKEN(HTTP_PUT, true), 076 CANCELDELEGATIONTOKEN(HTTP_PUT, false); 077 078 private String httpMethod; 079 private boolean requiresKerberosCredentials; 080 081 private DelegationTokenOperation(String httpMethod, 082 boolean requiresKerberosCredentials) { 083 this.httpMethod = httpMethod; 084 this.requiresKerberosCredentials = requiresKerberosCredentials; 085 } 086 087 public String getHttpMethod() { 088 return httpMethod; 089 } 090 091 public boolean requiresKerberosCredentials() { 092 return requiresKerberosCredentials; 093 } 094 095 } 096 097 public static void injectDelegationToken(Map<String, String> params, 098 Token<?> dtToken) 099 throws IOException { 100 if (dtToken != null) { 101 params.put(DELEGATION_PARAM, dtToken.encodeToUrlString()); 102 } 103 } 104 105 private boolean hasDelegationToken(URL url) { 106 return url.getQuery().contains(DELEGATION_PARAM + "="); 107 } 108 109 @Override 110 public void authenticate(URL url, AuthenticatedURL.Token token) 111 throws IOException, AuthenticationException { 112 if (!hasDelegationToken(url)) { 113 super.authenticate(url, token); 114 } 115 } 116 117 public static final String OP_PARAM = "op"; 118 119 public static Token<?> getDelegationToken(URI fsURI, 120 InetSocketAddress httpFSAddr, AuthenticatedURL.Token token, 121 String renewer) throws IOException { 122 DelegationTokenOperation op = 123 DelegationTokenOperation.GETDELEGATIONTOKEN; 124 Map<String, String> params = new HashMap<String, String>(); 125 params.put(OP_PARAM, op.toString()); 126 params.put(RENEWER_PARAM,renewer); 127 URL url = HttpFSUtils.createHttpURL(new Path(fsURI), params); 128 AuthenticatedURL aUrl = 129 new AuthenticatedURL(new HttpFSKerberosAuthenticator()); 130 try { 131 HttpURLConnection conn = aUrl.openConnection(url, token); 132 conn.setRequestMethod(op.getHttpMethod()); 133 HttpFSUtils.validateResponse(conn, HttpURLConnection.HTTP_OK); 134 JSONObject json = (JSONObject) ((JSONObject) 135 HttpFSUtils.jsonParse(conn)).get(DELEGATION_TOKEN_JSON); 136 String tokenStr = (String) 137 json.get(DELEGATION_TOKEN_URL_STRING_JSON); 138 Token<AbstractDelegationTokenIdentifier> dToken = 139 new Token<AbstractDelegationTokenIdentifier>(); 140 dToken.decodeFromUrlString(tokenStr); 141 SecurityUtil.setTokenService(dToken, httpFSAddr); 142 return dToken; 143 } catch (AuthenticationException ex) { 144 throw new IOException(ex.toString(), ex); 145 } 146 } 147 148 public static long renewDelegationToken(URI fsURI, 149 AuthenticatedURL.Token token, Token<?> dToken) throws IOException { 150 Map<String, String> params = new HashMap<String, String>(); 151 params.put(OP_PARAM, 152 DelegationTokenOperation.RENEWDELEGATIONTOKEN.toString()); 153 params.put(TOKEN_PARAM, dToken.encodeToUrlString()); 154 URL url = HttpFSUtils.createHttpURL(new Path(fsURI), params); 155 AuthenticatedURL aUrl = 156 new AuthenticatedURL(new HttpFSKerberosAuthenticator()); 157 try { 158 HttpURLConnection conn = aUrl.openConnection(url, token); 159 conn.setRequestMethod( 160 DelegationTokenOperation.RENEWDELEGATIONTOKEN.getHttpMethod()); 161 HttpFSUtils.validateResponse(conn, HttpURLConnection.HTTP_OK); 162 JSONObject json = (JSONObject) ((JSONObject) 163 HttpFSUtils.jsonParse(conn)).get(DELEGATION_TOKEN_JSON); 164 return (Long)(json.get(RENEW_DELEGATION_TOKEN_JSON)); 165 } catch (AuthenticationException ex) { 166 throw new IOException(ex.toString(), ex); 167 } 168 } 169 170 public static void cancelDelegationToken(URI fsURI, 171 AuthenticatedURL.Token token, Token<?> dToken) throws IOException { 172 Map<String, String> params = new HashMap<String, String>(); 173 params.put(OP_PARAM, 174 DelegationTokenOperation.CANCELDELEGATIONTOKEN.toString()); 175 params.put(TOKEN_PARAM, dToken.encodeToUrlString()); 176 URL url = HttpFSUtils.createHttpURL(new Path(fsURI), params); 177 AuthenticatedURL aUrl = 178 new AuthenticatedURL(new HttpFSKerberosAuthenticator()); 179 try { 180 HttpURLConnection conn = aUrl.openConnection(url, token); 181 conn.setRequestMethod( 182 DelegationTokenOperation.CANCELDELEGATIONTOKEN.getHttpMethod()); 183 HttpFSUtils.validateResponse(conn, HttpURLConnection.HTTP_OK); 184 } catch (AuthenticationException ex) { 185 throw new IOException(ex.toString(), ex); 186 } 187 } 188 189 }