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