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