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    }