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 }