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 019 package org.apache.hadoop.hdfs.server.common; 020 021 import com.google.common.base.Charsets; 022 023 import org.apache.commons.logging.Log; 024 import org.apache.commons.logging.LogFactory; 025 import org.apache.hadoop.classification.InterfaceAudience; 026 import org.apache.hadoop.conf.Configuration; 027 import org.apache.hadoop.fs.Path; 028 import org.apache.hadoop.hdfs.BlockReader; 029 import org.apache.hadoop.hdfs.BlockReaderFactory; 030 import org.apache.hadoop.hdfs.DFSClient; 031 import org.apache.hadoop.hdfs.DFSUtil; 032 import org.apache.hadoop.hdfs.net.TcpPeerServer; 033 import org.apache.hadoop.hdfs.protocol.*; 034 import org.apache.hadoop.hdfs.security.token.block.BlockTokenIdentifier; 035 import org.apache.hadoop.hdfs.security.token.block.DataEncryptionKey; 036 import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; 037 import org.apache.hadoop.hdfs.server.blockmanagement.DatanodeDescriptor; 038 import org.apache.hadoop.hdfs.server.datanode.CachingStrategy; 039 import org.apache.hadoop.hdfs.server.namenode.NameNode; 040 import org.apache.hadoop.hdfs.server.namenode.NameNodeHttpServer; 041 import org.apache.hadoop.hdfs.web.resources.DelegationParam; 042 import org.apache.hadoop.hdfs.web.resources.DoAsParam; 043 import org.apache.hadoop.hdfs.web.resources.UserParam; 044 import org.apache.hadoop.http.HtmlQuoting; 045 import org.apache.hadoop.io.IOUtils; 046 import org.apache.hadoop.net.NetUtils; 047 import org.apache.hadoop.security.AccessControlException; 048 import org.apache.hadoop.security.SecurityUtil; 049 import org.apache.hadoop.security.UserGroupInformation; 050 import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod; 051 import org.apache.hadoop.security.authentication.util.KerberosName; 052 import org.apache.hadoop.security.authorize.ProxyUsers; 053 import org.apache.hadoop.security.token.Token; 054 import org.apache.hadoop.util.VersionInfo; 055 056 import javax.servlet.ServletContext; 057 import javax.servlet.http.HttpServletRequest; 058 import javax.servlet.jsp.JspWriter; 059 060 import java.io.ByteArrayInputStream; 061 import java.io.DataInputStream; 062 import java.io.IOException; 063 import java.io.UnsupportedEncodingException; 064 import java.net.InetSocketAddress; 065 import java.net.Socket; 066 import java.net.URL; 067 import java.net.URLEncoder; 068 import java.util.*; 069 070 import static org.apache.hadoop.fs.CommonConfigurationKeys.DEFAULT_HADOOP_HTTP_STATIC_USER; 071 import static org.apache.hadoop.fs.CommonConfigurationKeys.HADOOP_HTTP_STATIC_USER; 072 073 @InterfaceAudience.Private 074 public class JspHelper { 075 public static final String CURRENT_CONF = "current.conf"; 076 public static final String DELEGATION_PARAMETER_NAME = DelegationParam.NAME; 077 public static final String NAMENODE_ADDRESS = "nnaddr"; 078 static final String SET_DELEGATION = "&" + DELEGATION_PARAMETER_NAME + 079 "="; 080 private static final Log LOG = LogFactory.getLog(JspHelper.class); 081 082 /** Private constructor for preventing creating JspHelper object. */ 083 private JspHelper() {} 084 085 // data structure to count number of blocks on datanodes. 086 private static class NodeRecord extends DatanodeInfo { 087 int frequency; 088 089 public NodeRecord(DatanodeInfo info, int count) { 090 super(info); 091 this.frequency = count; 092 } 093 094 @Override 095 public boolean equals(Object obj) { 096 // Sufficient to use super equality as datanodes are uniquely identified 097 // by DatanodeID 098 return (this == obj) || super.equals(obj); 099 } 100 @Override 101 public int hashCode() { 102 // Super implementation is sufficient 103 return super.hashCode(); 104 } 105 } 106 107 // compare two records based on their frequency 108 private static class NodeRecordComparator implements Comparator<NodeRecord> { 109 110 @Override 111 public int compare(NodeRecord o1, NodeRecord o2) { 112 if (o1.frequency < o2.frequency) { 113 return -1; 114 } else if (o1.frequency > o2.frequency) { 115 return 1; 116 } 117 return 0; 118 } 119 } 120 121 /** 122 * convenience method for canonicalizing host name. 123 * @param addr name:port or name 124 * @return canonicalized host name 125 */ 126 public static String canonicalize(String addr) { 127 // default port 1 is supplied to allow addr without port. 128 // the port will be ignored. 129 return NetUtils.createSocketAddr(addr, 1).getAddress() 130 .getCanonicalHostName(); 131 } 132 133 /** 134 * A helper class that generates the correct URL for different schema. 135 * 136 */ 137 public static final class Url { 138 public static String authority(String scheme, DatanodeID d) { 139 String fqdn = canonicalize(d.getIpAddr()); 140 if (scheme.equals("http")) { 141 return fqdn + ":" + d.getInfoPort(); 142 } else if (scheme.equals("https")) { 143 return fqdn + ":" + d.getInfoSecurePort(); 144 } else { 145 throw new IllegalArgumentException("Unknown scheme:" + scheme); 146 } 147 } 148 149 public static String url(String scheme, DatanodeID d) { 150 return scheme + "://" + authority(scheme, d); 151 } 152 } 153 154 public static DatanodeInfo bestNode(LocatedBlocks blks, Configuration conf) 155 throws IOException { 156 HashMap<DatanodeInfo, NodeRecord> map = 157 new HashMap<DatanodeInfo, NodeRecord>(); 158 for (LocatedBlock block : blks.getLocatedBlocks()) { 159 DatanodeInfo[] nodes = block.getLocations(); 160 for (DatanodeInfo node : nodes) { 161 NodeRecord record = map.get(node); 162 if (record == null) { 163 map.put(node, new NodeRecord(node, 1)); 164 } else { 165 record.frequency++; 166 } 167 } 168 } 169 NodeRecord[] nodes = map.values().toArray(new NodeRecord[map.size()]); 170 Arrays.sort(nodes, new NodeRecordComparator()); 171 return bestNode(nodes, false, conf); 172 } 173 174 public static DatanodeInfo bestNode(LocatedBlock blk, Configuration conf) 175 throws IOException { 176 DatanodeInfo[] nodes = blk.getLocations(); 177 return bestNode(nodes, true, conf); 178 } 179 180 public static DatanodeInfo bestNode(DatanodeInfo[] nodes, boolean doRandom, 181 Configuration conf) throws IOException { 182 TreeSet<DatanodeInfo> deadNodes = new TreeSet<DatanodeInfo>(); 183 DatanodeInfo chosenNode = null; 184 int failures = 0; 185 Socket s = null; 186 int index = -1; 187 if (nodes == null || nodes.length == 0) { 188 throw new IOException("No nodes contain this block"); 189 } 190 while (s == null) { 191 if (chosenNode == null) { 192 do { 193 if (doRandom) { 194 index = DFSUtil.getRandom().nextInt(nodes.length); 195 } else { 196 index++; 197 } 198 chosenNode = nodes[index]; 199 } while (deadNodes.contains(chosenNode)); 200 } 201 chosenNode = nodes[index]; 202 203 //just ping to check whether the node is alive 204 InetSocketAddress targetAddr = NetUtils.createSocketAddr( 205 chosenNode.getInfoAddr()); 206 207 try { 208 s = NetUtils.getDefaultSocketFactory(conf).createSocket(); 209 s.connect(targetAddr, HdfsServerConstants.READ_TIMEOUT); 210 s.setSoTimeout(HdfsServerConstants.READ_TIMEOUT); 211 } catch (IOException e) { 212 deadNodes.add(chosenNode); 213 IOUtils.closeSocket(s); 214 s = null; 215 failures++; 216 } 217 if (failures == nodes.length) 218 throw new IOException("Could not reach the block containing the data. Please try again"); 219 220 } 221 s.close(); 222 return chosenNode; 223 } 224 225 public static void streamBlockInAscii(InetSocketAddress addr, String poolId, 226 long blockId, Token<BlockTokenIdentifier> blockToken, long genStamp, 227 long blockSize, long offsetIntoBlock, long chunkSizeToView, 228 JspWriter out, Configuration conf, DFSClient.Conf dfsConf, 229 DataEncryptionKey encryptionKey) 230 throws IOException { 231 if (chunkSizeToView == 0) return; 232 Socket s = NetUtils.getDefaultSocketFactory(conf).createSocket(); 233 s.connect(addr, HdfsServerConstants.READ_TIMEOUT); 234 s.setSoTimeout(HdfsServerConstants.READ_TIMEOUT); 235 236 int amtToRead = (int)Math.min(chunkSizeToView, blockSize - offsetIntoBlock); 237 238 // Use the block name for file name. 239 String file = BlockReaderFactory.getFileName(addr, poolId, blockId); 240 BlockReader blockReader = BlockReaderFactory.newBlockReader(dfsConf, file, 241 new ExtendedBlock(poolId, blockId, 0, genStamp), blockToken, 242 offsetIntoBlock, amtToRead, true, 243 "JspHelper", TcpPeerServer.peerFromSocketAndKey(s, encryptionKey), 244 new DatanodeID(addr.getAddress().getHostAddress(), 245 addr.getHostName(), poolId, addr.getPort(), 0, 0, 0), null, 246 null, null, false, CachingStrategy.newDefaultStrategy()); 247 248 final byte[] buf = new byte[amtToRead]; 249 int readOffset = 0; 250 int retries = 2; 251 while ( amtToRead > 0 ) { 252 int numRead = amtToRead; 253 try { 254 blockReader.readFully(buf, readOffset, amtToRead); 255 } 256 catch (IOException e) { 257 retries--; 258 if (retries == 0) 259 throw new IOException("Could not read data from datanode"); 260 continue; 261 } 262 amtToRead -= numRead; 263 readOffset += numRead; 264 } 265 blockReader.close(); 266 out.print(HtmlQuoting.quoteHtmlChars(new String(buf, Charsets.UTF_8))); 267 } 268 269 public static void addTableHeader(JspWriter out) throws IOException { 270 out.print("<table border=\"1\""+ 271 " cellpadding=\"2\" cellspacing=\"2\">"); 272 out.print("<tbody>"); 273 } 274 public static void addTableRow(JspWriter out, String[] columns) throws IOException { 275 out.print("<tr>"); 276 for (int i = 0; i < columns.length; i++) { 277 out.print("<td style=\"vertical-align: top;\"><B>"+columns[i]+"</B><br></td>"); 278 } 279 out.print("</tr>"); 280 } 281 public static void addTableRow(JspWriter out, String[] columns, int row) throws IOException { 282 out.print("<tr>"); 283 284 for (int i = 0; i < columns.length; i++) { 285 if (row/2*2 == row) {//even 286 out.print("<td style=\"vertical-align: top;background-color:LightGrey;\"><B>"+columns[i]+"</B><br></td>"); 287 } else { 288 out.print("<td style=\"vertical-align: top;background-color:LightBlue;\"><B>"+columns[i]+"</B><br></td>"); 289 290 } 291 } 292 out.print("</tr>"); 293 } 294 public static void addTableFooter(JspWriter out) throws IOException { 295 out.print("</tbody></table>"); 296 } 297 298 public static void sortNodeList(final List<DatanodeDescriptor> nodes, 299 String field, String order) { 300 301 class NodeComapare implements Comparator<DatanodeDescriptor> { 302 static final int 303 FIELD_NAME = 1, 304 FIELD_LAST_CONTACT = 2, 305 FIELD_BLOCKS = 3, 306 FIELD_CAPACITY = 4, 307 FIELD_USED = 5, 308 FIELD_PERCENT_USED = 6, 309 FIELD_NONDFS_USED = 7, 310 FIELD_REMAINING = 8, 311 FIELD_PERCENT_REMAINING = 9, 312 FIELD_ADMIN_STATE = 10, 313 FIELD_DECOMMISSIONED = 11, 314 SORT_ORDER_ASC = 1, 315 SORT_ORDER_DSC = 2; 316 317 int sortField = FIELD_NAME; 318 int sortOrder = SORT_ORDER_ASC; 319 320 public NodeComapare(String field, String order) { 321 if (field.equals("lastcontact")) { 322 sortField = FIELD_LAST_CONTACT; 323 } else if (field.equals("capacity")) { 324 sortField = FIELD_CAPACITY; 325 } else if (field.equals("used")) { 326 sortField = FIELD_USED; 327 } else if (field.equals("nondfsused")) { 328 sortField = FIELD_NONDFS_USED; 329 } else if (field.equals("remaining")) { 330 sortField = FIELD_REMAINING; 331 } else if (field.equals("pcused")) { 332 sortField = FIELD_PERCENT_USED; 333 } else if (field.equals("pcremaining")) { 334 sortField = FIELD_PERCENT_REMAINING; 335 } else if (field.equals("blocks")) { 336 sortField = FIELD_BLOCKS; 337 } else if (field.equals("adminstate")) { 338 sortField = FIELD_ADMIN_STATE; 339 } else if (field.equals("decommissioned")) { 340 sortField = FIELD_DECOMMISSIONED; 341 } else { 342 sortField = FIELD_NAME; 343 } 344 345 if (order.equals("DSC")) { 346 sortOrder = SORT_ORDER_DSC; 347 } else { 348 sortOrder = SORT_ORDER_ASC; 349 } 350 } 351 352 @Override 353 public int compare(DatanodeDescriptor d1, 354 DatanodeDescriptor d2) { 355 int ret = 0; 356 switch (sortField) { 357 case FIELD_LAST_CONTACT: 358 ret = (int) (d2.getLastUpdate() - d1.getLastUpdate()); 359 break; 360 case FIELD_CAPACITY: 361 long dlong = d1.getCapacity() - d2.getCapacity(); 362 ret = (dlong < 0) ? -1 : ((dlong > 0) ? 1 : 0); 363 break; 364 case FIELD_USED: 365 dlong = d1.getDfsUsed() - d2.getDfsUsed(); 366 ret = (dlong < 0) ? -1 : ((dlong > 0) ? 1 : 0); 367 break; 368 case FIELD_NONDFS_USED: 369 dlong = d1.getNonDfsUsed() - d2.getNonDfsUsed(); 370 ret = (dlong < 0) ? -1 : ((dlong > 0) ? 1 : 0); 371 break; 372 case FIELD_REMAINING: 373 dlong = d1.getRemaining() - d2.getRemaining(); 374 ret = (dlong < 0) ? -1 : ((dlong > 0) ? 1 : 0); 375 break; 376 case FIELD_PERCENT_USED: 377 double ddbl =((d1.getDfsUsedPercent())- 378 (d2.getDfsUsedPercent())); 379 ret = (ddbl < 0) ? -1 : ((ddbl > 0) ? 1 : 0); 380 break; 381 case FIELD_PERCENT_REMAINING: 382 ddbl =((d1.getRemainingPercent())- 383 (d2.getRemainingPercent())); 384 ret = (ddbl < 0) ? -1 : ((ddbl > 0) ? 1 : 0); 385 break; 386 case FIELD_BLOCKS: 387 ret = d1.numBlocks() - d2.numBlocks(); 388 break; 389 case FIELD_ADMIN_STATE: 390 ret = d1.getAdminState().toString().compareTo( 391 d2.getAdminState().toString()); 392 break; 393 case FIELD_DECOMMISSIONED: 394 ret = DFSUtil.DECOM_COMPARATOR.compare(d1, d2); 395 break; 396 case FIELD_NAME: 397 ret = d1.getHostName().compareTo(d2.getHostName()); 398 break; 399 default: 400 throw new IllegalArgumentException("Invalid sortField"); 401 } 402 return (sortOrder == SORT_ORDER_DSC) ? -ret : ret; 403 } 404 } 405 406 Collections.sort(nodes, new NodeComapare(field, order)); 407 } 408 409 public static void printPathWithLinks(String dir, JspWriter out, 410 int namenodeInfoPort, 411 String tokenString, 412 String nnAddress 413 ) throws IOException { 414 try { 415 String[] parts = dir.split(Path.SEPARATOR); 416 StringBuilder tempPath = new StringBuilder(dir.length()); 417 out.print("<a href=\"browseDirectory.jsp" + "?dir="+ Path.SEPARATOR 418 + "&namenodeInfoPort=" + namenodeInfoPort 419 + getDelegationTokenUrlParam(tokenString) 420 + getUrlParam(NAMENODE_ADDRESS, nnAddress) + "\">" + Path.SEPARATOR 421 + "</a>"); 422 tempPath.append(Path.SEPARATOR); 423 for (int i = 0; i < parts.length-1; i++) { 424 if (!parts[i].equals("")) { 425 tempPath.append(parts[i]); 426 out.print("<a href=\"browseDirectory.jsp" + "?dir=" 427 + HtmlQuoting.quoteHtmlChars(tempPath.toString()) + "&namenodeInfoPort=" + namenodeInfoPort 428 + getDelegationTokenUrlParam(tokenString) 429 + getUrlParam(NAMENODE_ADDRESS, nnAddress)); 430 out.print("\">" + HtmlQuoting.quoteHtmlChars(parts[i]) + "</a>" + Path.SEPARATOR); 431 tempPath.append(Path.SEPARATOR); 432 } 433 } 434 if(parts.length > 0) { 435 out.print(HtmlQuoting.quoteHtmlChars(parts[parts.length-1])); 436 } 437 } 438 catch (UnsupportedEncodingException ex) { 439 ex.printStackTrace(); 440 } 441 } 442 443 public static void printGotoForm(JspWriter out, 444 int namenodeInfoPort, 445 String tokenString, 446 String file, 447 String nnAddress) throws IOException { 448 out.print("<form action=\"browseDirectory.jsp\" method=\"get\" name=\"goto\">"); 449 out.print("Goto : "); 450 out.print("<input name=\"dir\" type=\"text\" width=\"50\" id=\"dir\" value=\""+ HtmlQuoting.quoteHtmlChars(file)+"\"/>"); 451 out.print("<input name=\"go\" type=\"submit\" value=\"go\"/>"); 452 out.print("<input name=\"namenodeInfoPort\" type=\"hidden\" " 453 + "value=\"" + namenodeInfoPort + "\"/>"); 454 if (UserGroupInformation.isSecurityEnabled()) { 455 out.print("<input name=\"" + DELEGATION_PARAMETER_NAME 456 + "\" type=\"hidden\" value=\"" + tokenString + "\"/>"); 457 } 458 out.print("<input name=\""+ NAMENODE_ADDRESS +"\" type=\"hidden\" " 459 + "value=\"" + nnAddress + "\"/>"); 460 out.print("</form>"); 461 } 462 463 public static void createTitle(JspWriter out, 464 HttpServletRequest req, 465 String file) throws IOException{ 466 if(file == null) file = ""; 467 int start = Math.max(0,file.length() - 100); 468 if(start != 0) 469 file = "..." + file.substring(start, file.length()); 470 out.print("<title>HDFS:" + file + "</title>"); 471 } 472 473 /** Convert a String to chunk-size-to-view. */ 474 public static int string2ChunkSizeToView(String s, int defaultValue) { 475 int n = s == null? 0: Integer.parseInt(s); 476 return n > 0? n: defaultValue; 477 } 478 479 /** Return a table containing version information. */ 480 public static String getVersionTable() { 481 return "<div class='dfstable'><table>" 482 + "\n <tr><td class='col1'>Version:</td><td>" + VersionInfo.getVersion() + ", " + VersionInfo.getRevision() + "</td></tr>" 483 + "\n <tr><td class='col1'>Compiled:</td><td>" + VersionInfo.getDate() + " by " + VersionInfo.getUser() + " from " + VersionInfo.getBranch() + "</td></tr>" 484 + "\n</table></div>"; 485 } 486 487 /** 488 * Validate filename. 489 * @return null if the filename is invalid. 490 * Otherwise, return the validated filename. 491 */ 492 public static String validatePath(String p) { 493 return p == null || p.length() == 0? 494 null: new Path(p).toUri().getPath(); 495 } 496 497 /** 498 * Validate a long value. 499 * @return null if the value is invalid. 500 * Otherwise, return the validated Long object. 501 */ 502 public static Long validateLong(String value) { 503 return value == null? null: Long.parseLong(value); 504 } 505 506 /** 507 * Validate a URL. 508 * @return null if the value is invalid. 509 * Otherwise, return the validated URL String. 510 */ 511 public static String validateURL(String value) { 512 try { 513 return URLEncoder.encode(new URL(value).toString(), "UTF-8"); 514 } catch (IOException e) { 515 return null; 516 } 517 } 518 519 /** 520 * If security is turned off, what is the default web user? 521 * @param conf the configuration to look in 522 * @return the remote user that was configuration 523 */ 524 public static UserGroupInformation getDefaultWebUser(Configuration conf 525 ) throws IOException { 526 return UserGroupInformation.createRemoteUser(getDefaultWebUserName(conf)); 527 } 528 529 private static String getDefaultWebUserName(Configuration conf 530 ) throws IOException { 531 String user = conf.get( 532 HADOOP_HTTP_STATIC_USER, DEFAULT_HADOOP_HTTP_STATIC_USER); 533 if (user == null || user.length() == 0) { 534 throw new IOException("Cannot determine UGI from request or conf"); 535 } 536 return user; 537 } 538 539 private static InetSocketAddress getNNServiceAddress(ServletContext context, 540 HttpServletRequest request) { 541 String namenodeAddressInUrl = request.getParameter(NAMENODE_ADDRESS); 542 InetSocketAddress namenodeAddress = null; 543 if (namenodeAddressInUrl != null) { 544 namenodeAddress = NetUtils.createSocketAddr(namenodeAddressInUrl); 545 } else if (context != null) { 546 namenodeAddress = NameNodeHttpServer.getNameNodeAddressFromContext( 547 context); 548 } 549 if (namenodeAddress != null) { 550 return namenodeAddress; 551 } 552 return null; 553 } 554 555 /** Same as getUGI(null, request, conf). */ 556 public static UserGroupInformation getUGI(HttpServletRequest request, 557 Configuration conf) throws IOException { 558 return getUGI(null, request, conf); 559 } 560 561 /** Same as getUGI(context, request, conf, KERBEROS_SSL, true). */ 562 public static UserGroupInformation getUGI(ServletContext context, 563 HttpServletRequest request, Configuration conf) throws IOException { 564 return getUGI(context, request, conf, AuthenticationMethod.KERBEROS_SSL, true); 565 } 566 567 /** 568 * Get {@link UserGroupInformation} and possibly the delegation token out of 569 * the request. 570 * @param context the ServletContext that is serving this request. 571 * @param request the http request 572 * @param conf configuration 573 * @param secureAuthMethod the AuthenticationMethod used in secure mode. 574 * @param tryUgiParameter Should it try the ugi parameter? 575 * @return a new user from the request 576 * @throws AccessControlException if the request has no token 577 */ 578 public static UserGroupInformation getUGI(ServletContext context, 579 HttpServletRequest request, Configuration conf, 580 final AuthenticationMethod secureAuthMethod, 581 final boolean tryUgiParameter) throws IOException { 582 UserGroupInformation ugi = null; 583 final String usernameFromQuery = getUsernameFromQuery(request, tryUgiParameter); 584 final String doAsUserFromQuery = request.getParameter(DoAsParam.NAME); 585 final String remoteUser; 586 587 if (UserGroupInformation.isSecurityEnabled()) { 588 remoteUser = request.getRemoteUser(); 589 final String tokenString = request.getParameter(DELEGATION_PARAMETER_NAME); 590 if (tokenString != null) { 591 // Token-based connections need only verify the effective user, and 592 // disallow proxying to different user. Proxy authorization checks 593 // are not required since the checks apply to issuing a token. 594 ugi = getTokenUGI(context, request, tokenString, conf); 595 checkUsername(ugi.getShortUserName(), usernameFromQuery); 596 checkUsername(ugi.getShortUserName(), doAsUserFromQuery); 597 } else if (remoteUser == null) { 598 throw new IOException( 599 "Security enabled but user not authenticated by filter"); 600 } 601 } else { 602 // Security's not on, pull from url or use default web user 603 remoteUser = (usernameFromQuery == null) 604 ? getDefaultWebUserName(conf) // not specified in request 605 : usernameFromQuery; 606 } 607 608 if (ugi == null) { // security is off, or there's no token 609 ugi = UserGroupInformation.createRemoteUser(remoteUser); 610 checkUsername(ugi.getShortUserName(), usernameFromQuery); 611 if (UserGroupInformation.isSecurityEnabled()) { 612 // This is not necessarily true, could have been auth'ed by user-facing 613 // filter 614 ugi.setAuthenticationMethod(secureAuthMethod); 615 } 616 if (doAsUserFromQuery != null) { 617 // create and attempt to authorize a proxy user 618 ugi = UserGroupInformation.createProxyUser(doAsUserFromQuery, ugi); 619 ProxyUsers.authorize(ugi, request.getRemoteAddr(), conf); 620 } 621 } 622 623 if(LOG.isDebugEnabled()) 624 LOG.debug("getUGI is returning: " + ugi.getShortUserName()); 625 return ugi; 626 } 627 628 private static UserGroupInformation getTokenUGI(ServletContext context, 629 HttpServletRequest request, 630 String tokenString, 631 Configuration conf) 632 throws IOException { 633 final Token<DelegationTokenIdentifier> token = 634 new Token<DelegationTokenIdentifier>(); 635 token.decodeFromUrlString(tokenString); 636 InetSocketAddress serviceAddress = getNNServiceAddress(context, request); 637 if (serviceAddress != null) { 638 SecurityUtil.setTokenService(token, serviceAddress); 639 token.setKind(DelegationTokenIdentifier.HDFS_DELEGATION_KIND); 640 } 641 642 ByteArrayInputStream buf = 643 new ByteArrayInputStream(token.getIdentifier()); 644 DataInputStream in = new DataInputStream(buf); 645 DelegationTokenIdentifier id = new DelegationTokenIdentifier(); 646 id.readFields(in); 647 if (context != null) { 648 final NameNode nn = NameNodeHttpServer.getNameNodeFromContext(context); 649 if (nn != null) { 650 // Verify the token. 651 nn.getNamesystem().verifyToken(id, token.getPassword()); 652 } 653 } 654 UserGroupInformation ugi = id.getUser(); 655 ugi.addToken(token); 656 return ugi; 657 } 658 659 /** 660 * Expected user name should be a short name. 661 */ 662 private static void checkUsername(final String expected, final String name 663 ) throws IOException { 664 if (expected == null && name != null) { 665 throw new IOException("Usernames not matched: expecting null but name=" 666 + name); 667 } 668 if (name == null) { //name is optional, null is okay 669 return; 670 } 671 KerberosName u = new KerberosName(name); 672 String shortName = u.getShortName(); 673 if (!shortName.equals(expected)) { 674 throw new IOException("Usernames not matched: name=" + shortName 675 + " != expected=" + expected); 676 } 677 } 678 679 private static String getUsernameFromQuery(final HttpServletRequest request, 680 final boolean tryUgiParameter) { 681 String username = request.getParameter(UserParam.NAME); 682 if (username == null && tryUgiParameter) { 683 //try ugi parameter 684 final String ugiStr = request.getParameter("ugi"); 685 if (ugiStr != null) { 686 username = ugiStr.split(",")[0]; 687 } 688 } 689 return username; 690 } 691 692 /** 693 * Returns the url parameter for the given token string. 694 * @param tokenString 695 * @return url parameter 696 */ 697 public static String getDelegationTokenUrlParam(String tokenString) { 698 if (tokenString == null ) { 699 return ""; 700 } 701 if (UserGroupInformation.isSecurityEnabled()) { 702 return SET_DELEGATION + tokenString; 703 } else { 704 return ""; 705 } 706 } 707 708 /** 709 * Returns the url parameter for the given string, prefixed with 710 * paramSeparator. 711 * 712 * @param name parameter name 713 * @param val parameter value 714 * @param paramSeparator URL parameter prefix, i.e. either '?' or '&' 715 * @return url parameter 716 */ 717 public static String getUrlParam(String name, String val, String paramSeparator) { 718 return val == null ? "" : paramSeparator + name + "=" + val; 719 } 720 721 /** 722 * Returns the url parameter for the given string, prefixed with '?' if 723 * firstParam is true, prefixed with '&' if firstParam is false. 724 * 725 * @param name parameter name 726 * @param val parameter value 727 * @param firstParam true if this is the first parameter in the list, false otherwise 728 * @return url parameter 729 */ 730 public static String getUrlParam(String name, String val, boolean firstParam) { 731 return getUrlParam(name, val, firstParam ? "?" : "&"); 732 } 733 734 /** 735 * Returns the url parameter for the given string, prefixed with '&'. 736 * 737 * @param name parameter name 738 * @param val parameter value 739 * @return url parameter 740 */ 741 public static String getUrlParam(String name, String val) { 742 return getUrlParam(name, val, false); 743 } 744 }