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    package org.apache.hadoop.fs.http.client;
019    
020    
021    import org.apache.hadoop.classification.InterfaceAudience;
022    import org.apache.hadoop.fs.Path;
023    import org.apache.hadoop.security.SecurityUtil;
024    import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
025    import org.apache.hadoop.security.authentication.client.AuthenticationException;
026    import org.apache.hadoop.security.authentication.client.Authenticator;
027    import org.apache.hadoop.security.authentication.client.KerberosAuthenticator;
028    import org.apache.hadoop.security.token.Token;
029    import org.apache.hadoop.security.token.delegation.AbstractDelegationTokenIdentifier;
030    import org.json.simple.JSONArray;
031    import org.json.simple.JSONObject;
032    
033    import java.io.IOException;
034    import java.net.HttpURLConnection;
035    import java.net.InetSocketAddress;
036    import java.net.URI;
037    import java.net.URL;
038    import java.util.ArrayList;
039    import java.util.HashMap;
040    import java.util.List;
041    import java.util.Map;
042    
043    /**
044     * A <code>KerberosAuthenticator</code> subclass that fallback to
045     * {@link HttpFSPseudoAuthenticator}.
046     */
047    @InterfaceAudience.Private
048    public class HttpFSKerberosAuthenticator extends KerberosAuthenticator {
049    
050      /**
051       * Returns the fallback authenticator if the server does not use
052       * Kerberos SPNEGO HTTP authentication.
053       *
054       * @return a {@link HttpFSPseudoAuthenticator} instance.
055       */
056      @Override
057      protected Authenticator getFallBackAuthenticator() {
058        return new HttpFSPseudoAuthenticator();
059      }
060    
061      private static final String HTTP_GET = "GET";
062      private static final String HTTP_PUT = "PUT";
063    
064      public static final String DELEGATION_PARAM = "delegation";
065      public static final String TOKEN_PARAM = "token";
066      public static final String RENEWER_PARAM = "renewer";
067      public static final String DELEGATION_TOKEN_JSON = "Token";
068      public static final String DELEGATION_TOKEN_URL_STRING_JSON = "urlString";
069      public static final String RENEW_DELEGATION_TOKEN_JSON = "long";
070    
071      /**
072       * DelegationToken operations.
073       */
074      @InterfaceAudience.Private
075      public static enum DelegationTokenOperation {
076        GETDELEGATIONTOKEN(HTTP_GET, true),
077        RENEWDELEGATIONTOKEN(HTTP_PUT, true),
078        CANCELDELEGATIONTOKEN(HTTP_PUT, false);
079    
080        private String httpMethod;
081        private boolean requiresKerberosCredentials;
082    
083        private DelegationTokenOperation(String httpMethod,
084                                         boolean requiresKerberosCredentials) {
085          this.httpMethod = httpMethod;
086          this.requiresKerberosCredentials = requiresKerberosCredentials;
087        }
088    
089        public String getHttpMethod() {
090          return httpMethod;
091        }
092    
093        public boolean requiresKerberosCredentials() {
094          return requiresKerberosCredentials;
095        }
096    
097      }
098    
099      public static void injectDelegationToken(Map<String, String> params,
100                                              Token<?> dtToken)
101        throws IOException {
102        if (dtToken != null) {
103          params.put(DELEGATION_PARAM, dtToken.encodeToUrlString());
104        }
105      }
106    
107      private boolean hasDelegationToken(URL url) {
108        return url.getQuery().contains(DELEGATION_PARAM + "=");
109      }
110    
111      @Override
112      public void authenticate(URL url, AuthenticatedURL.Token token)
113        throws IOException, AuthenticationException {
114        if (!hasDelegationToken(url)) {
115          super.authenticate(url, token);
116        }
117      }
118    
119      public static final String OP_PARAM = "op";
120    
121      public static Token<?> getDelegationToken(URI fsURI,
122        InetSocketAddress httpFSAddr, AuthenticatedURL.Token token,
123        String renewer) throws IOException {
124        DelegationTokenOperation op = 
125          DelegationTokenOperation.GETDELEGATIONTOKEN;
126        Map<String, String> params = new HashMap<String, String>();
127        params.put(OP_PARAM, op.toString());
128        params.put(RENEWER_PARAM,renewer);
129        URL url = HttpFSUtils.createHttpURL(new Path(fsURI), params);
130        AuthenticatedURL aUrl =
131          new AuthenticatedURL(new HttpFSKerberosAuthenticator());
132        try {
133          HttpURLConnection conn = aUrl.openConnection(url, token);
134          conn.setRequestMethod(op.getHttpMethod());
135          HttpFSUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
136          JSONObject json = (JSONObject) ((JSONObject)
137            HttpFSUtils.jsonParse(conn)).get(DELEGATION_TOKEN_JSON);
138          String tokenStr = (String)
139            json.get(DELEGATION_TOKEN_URL_STRING_JSON);
140          Token<AbstractDelegationTokenIdentifier> dToken =
141            new Token<AbstractDelegationTokenIdentifier>();
142          dToken.decodeFromUrlString(tokenStr);
143          SecurityUtil.setTokenService(dToken, httpFSAddr);
144          return dToken;
145        } catch (AuthenticationException ex) {
146          throw new IOException(ex.toString(), ex);
147        }
148      }
149    
150      public static long renewDelegationToken(URI fsURI,
151        AuthenticatedURL.Token token, Token<?> dToken) throws IOException {
152        Map<String, String> params = new HashMap<String, String>();
153        params.put(OP_PARAM,
154                   DelegationTokenOperation.RENEWDELEGATIONTOKEN.toString());
155        params.put(TOKEN_PARAM, dToken.encodeToUrlString());
156        URL url = HttpFSUtils.createHttpURL(new Path(fsURI), params);
157        AuthenticatedURL aUrl =
158          new AuthenticatedURL(new HttpFSKerberosAuthenticator());
159        try {
160          HttpURLConnection conn = aUrl.openConnection(url, token);
161          conn.setRequestMethod(
162            DelegationTokenOperation.RENEWDELEGATIONTOKEN.getHttpMethod());
163          HttpFSUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
164          JSONObject json = (JSONObject) ((JSONObject)
165            HttpFSUtils.jsonParse(conn)).get(DELEGATION_TOKEN_JSON);
166          return (Long)(json.get(RENEW_DELEGATION_TOKEN_JSON));
167        } catch (AuthenticationException ex) {
168          throw new IOException(ex.toString(), ex);
169        }
170      }
171    
172      public static void cancelDelegationToken(URI fsURI,
173        AuthenticatedURL.Token token, Token<?> dToken) throws IOException {
174        Map<String, String> params = new HashMap<String, String>();
175        params.put(OP_PARAM,
176                   DelegationTokenOperation.CANCELDELEGATIONTOKEN.toString());
177        params.put(TOKEN_PARAM, dToken.encodeToUrlString());
178        URL url = HttpFSUtils.createHttpURL(new Path(fsURI), params);
179        AuthenticatedURL aUrl =
180          new AuthenticatedURL(new HttpFSKerberosAuthenticator());
181        try {
182          HttpURLConnection conn = aUrl.openConnection(url, token);
183          conn.setRequestMethod(
184            DelegationTokenOperation.CANCELDELEGATIONTOKEN.getHttpMethod());
185          HttpFSUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
186        } catch (AuthenticationException ex) {
187          throw new IOException(ex.toString(), ex);
188        }
189      }
190    
191    }