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