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.server; 019 020 import org.apache.hadoop.classification.InterfaceAudience; 021 import org.apache.hadoop.fs.http.client.HttpFSFileSystem; 022 import org.apache.hadoop.fs.http.client.HttpFSKerberosAuthenticator; 023 import org.apache.hadoop.fs.http.client.HttpFSKerberosAuthenticator.DelegationTokenOperation; 024 import org.apache.hadoop.lib.service.DelegationTokenIdentifier; 025 import org.apache.hadoop.lib.service.DelegationTokenManager; 026 import org.apache.hadoop.lib.service.DelegationTokenManagerException; 027 import org.apache.hadoop.security.UserGroupInformation; 028 import org.apache.hadoop.security.authentication.client.AuthenticationException; 029 import org.apache.hadoop.security.authentication.server.AuthenticationToken; 030 import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler; 031 import org.apache.hadoop.security.token.Token; 032 import org.json.simple.JSONObject; 033 034 import javax.servlet.http.HttpServletRequest; 035 import javax.servlet.http.HttpServletResponse; 036 import javax.ws.rs.core.MediaType; 037 import java.io.IOException; 038 import java.io.Writer; 039 import java.text.MessageFormat; 040 import java.util.ArrayList; 041 import java.util.Arrays; 042 import java.util.HashMap; 043 import java.util.HashSet; 044 import java.util.LinkedHashMap; 045 import java.util.List; 046 import java.util.Map; 047 import java.util.Set; 048 049 /** 050 * Server side <code>AuthenticationHandler</code> that authenticates requests 051 * using the incoming delegation token as a 'delegation' query string parameter. 052 * <p/> 053 * If not delegation token is present in the request it delegates to the 054 * {@link KerberosAuthenticationHandler} 055 */ 056 @InterfaceAudience.Private 057 public class HttpFSKerberosAuthenticationHandler 058 extends KerberosAuthenticationHandler { 059 060 static final Set<String> DELEGATION_TOKEN_OPS = 061 new HashSet<String>(); 062 063 static { 064 DELEGATION_TOKEN_OPS.add( 065 DelegationTokenOperation.GETDELEGATIONTOKEN.toString()); 066 DELEGATION_TOKEN_OPS.add( 067 DelegationTokenOperation.RENEWDELEGATIONTOKEN.toString()); 068 DELEGATION_TOKEN_OPS.add( 069 DelegationTokenOperation.CANCELDELEGATIONTOKEN.toString()); 070 } 071 072 public static final String TYPE = "kerberos-dt"; 073 074 /** 075 * Returns authentication type of the handler. 076 * 077 * @return <code>delegationtoken-kerberos</code> 078 */ 079 @Override 080 public String getType() { 081 return TYPE; 082 } 083 084 private static final String ENTER = System.getProperty("line.separator"); 085 086 @Override 087 @SuppressWarnings("unchecked") 088 public boolean managementOperation(AuthenticationToken token, 089 HttpServletRequest request, HttpServletResponse response) 090 throws IOException, AuthenticationException { 091 boolean requestContinues = true; 092 String op = request.getParameter(HttpFSFileSystem.OP_PARAM); 093 op = (op != null) ? op.toUpperCase() : null; 094 if (DELEGATION_TOKEN_OPS.contains(op) && 095 !request.getMethod().equals("OPTIONS")) { 096 DelegationTokenOperation dtOp = 097 DelegationTokenOperation.valueOf(op); 098 if (dtOp.getHttpMethod().equals(request.getMethod())) { 099 if (dtOp.requiresKerberosCredentials() && token == null) { 100 response.sendError(HttpServletResponse.SC_UNAUTHORIZED, 101 MessageFormat.format( 102 "Operation [{0}] requires SPNEGO authentication established", 103 dtOp)); 104 requestContinues = false; 105 } else { 106 DelegationTokenManager tokenManager = 107 HttpFSServerWebApp.get().get(DelegationTokenManager.class); 108 try { 109 Map map = null; 110 switch (dtOp) { 111 case GETDELEGATIONTOKEN: 112 String renewerParam = 113 request.getParameter(HttpFSKerberosAuthenticator.RENEWER_PARAM); 114 if (renewerParam == null) { 115 renewerParam = token.getUserName(); 116 } 117 Token<?> dToken = tokenManager.createToken( 118 UserGroupInformation.getCurrentUser(), renewerParam); 119 map = delegationTokenToJSON(dToken); 120 break; 121 case RENEWDELEGATIONTOKEN: 122 case CANCELDELEGATIONTOKEN: 123 String tokenParam = 124 request.getParameter(HttpFSKerberosAuthenticator.TOKEN_PARAM); 125 if (tokenParam == null) { 126 response.sendError(HttpServletResponse.SC_BAD_REQUEST, 127 MessageFormat.format( 128 "Operation [{0}] requires the parameter [{1}]", 129 dtOp, HttpFSKerberosAuthenticator.TOKEN_PARAM)); 130 requestContinues = false; 131 } else { 132 if (dtOp == DelegationTokenOperation.CANCELDELEGATIONTOKEN) { 133 Token<DelegationTokenIdentifier> dt = 134 new Token<DelegationTokenIdentifier>(); 135 dt.decodeFromUrlString(tokenParam); 136 tokenManager.cancelToken(dt, 137 UserGroupInformation.getCurrentUser().getUserName()); 138 } else { 139 Token<DelegationTokenIdentifier> dt = 140 new Token<DelegationTokenIdentifier>(); 141 dt.decodeFromUrlString(tokenParam); 142 long expirationTime = 143 tokenManager.renewToken(dt, token.getUserName()); 144 map = new HashMap(); 145 map.put("long", expirationTime); 146 } 147 } 148 break; 149 } 150 if (requestContinues) { 151 response.setStatus(HttpServletResponse.SC_OK); 152 if (map != null) { 153 response.setContentType(MediaType.APPLICATION_JSON); 154 Writer writer = response.getWriter(); 155 JSONObject.writeJSONString(map, writer); 156 writer.write(ENTER); 157 writer.flush(); 158 159 } 160 requestContinues = false; 161 } 162 } catch (DelegationTokenManagerException ex) { 163 throw new AuthenticationException(ex.toString(), ex); 164 } 165 } 166 } else { 167 response.sendError(HttpServletResponse.SC_BAD_REQUEST, 168 MessageFormat.format( 169 "Wrong HTTP method [{0}] for operation [{1}], it should be [{2}]", 170 request.getMethod(), dtOp, dtOp.getHttpMethod())); 171 requestContinues = false; 172 } 173 } 174 return requestContinues; 175 } 176 177 @SuppressWarnings("unchecked") 178 private static Map delegationTokenToJSON(Token token) throws IOException { 179 Map json = new LinkedHashMap(); 180 json.put(HttpFSKerberosAuthenticator.DELEGATION_TOKEN_URL_STRING_JSON, 181 token.encodeToUrlString()); 182 Map response = new LinkedHashMap(); 183 response.put(HttpFSKerberosAuthenticator.DELEGATION_TOKEN_JSON, json); 184 return response; 185 } 186 187 /** 188 * Authenticates a request looking for the <code>delegation</code> 189 * query-string parameter and verifying it is a valid token. If there is not 190 * <code>delegation</code> query-string parameter, it delegates the 191 * authentication to the {@link KerberosAuthenticationHandler} unless it is 192 * disabled. 193 * 194 * @param request the HTTP client request. 195 * @param response the HTTP client response. 196 * 197 * @return the authentication token for the authenticated request. 198 * @throws IOException thrown if an IO error occurred. 199 * @throws AuthenticationException thrown if the authentication failed. 200 */ 201 @Override 202 public AuthenticationToken authenticate(HttpServletRequest request, 203 HttpServletResponse response) 204 throws IOException, AuthenticationException { 205 AuthenticationToken token; 206 String delegationParam = 207 request.getParameter(HttpFSKerberosAuthenticator.DELEGATION_PARAM); 208 if (delegationParam != null) { 209 try { 210 Token<DelegationTokenIdentifier> dt = 211 new Token<DelegationTokenIdentifier>(); 212 dt.decodeFromUrlString(delegationParam); 213 DelegationTokenManager tokenManager = 214 HttpFSServerWebApp.get().get(DelegationTokenManager.class); 215 UserGroupInformation ugi = tokenManager.verifyToken(dt); 216 final String shortName = ugi.getShortUserName(); 217 218 // creating a ephemeral token 219 token = new AuthenticationToken(shortName, ugi.getUserName(), 220 getType()); 221 token.setExpires(0); 222 } catch (Throwable ex) { 223 throw new AuthenticationException("Could not verify DelegationToken, " + 224 ex.toString(), ex); 225 } 226 } else { 227 token = super.authenticate(request, response); 228 } 229 return token; 230 } 231 232 233 }