View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.security.token;
20  
21  import java.io.IOException;
22  
23  import com.google.protobuf.RpcCallback;
24  import com.google.protobuf.RpcController;
25  import com.google.protobuf.Service;
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.hbase.classification.InterfaceAudience;
29  import org.apache.hadoop.hbase.Coprocessor;
30  import org.apache.hadoop.hbase.CoprocessorEnvironment;
31  import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
32  import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
33  import org.apache.hadoop.hbase.ipc.RpcServer;
34  import org.apache.hadoop.hbase.ipc.RequestContext;
35  import org.apache.hadoop.hbase.ipc.RpcServerInterface;
36  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
37  import org.apache.hadoop.hbase.protobuf.ResponseConverter;
38  import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos;
39  import org.apache.hadoop.hbase.security.AccessDeniedException;
40  import org.apache.hadoop.hbase.security.User;
41  import org.apache.hadoop.security.UserGroupInformation;
42  import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
43  import org.apache.hadoop.security.token.SecretManager;
44  import org.apache.hadoop.security.token.Token;
45  
46  /**
47   * Provides a service for obtaining authentication tokens via the
48   * {@link AuthenticationProtos} AuthenticationService coprocessor service.
49   */
50  @InterfaceAudience.Private
51  public class TokenProvider implements AuthenticationProtos.AuthenticationService.Interface,
52      Coprocessor, CoprocessorService {
53  
54    private static Log LOG = LogFactory.getLog(TokenProvider.class);
55  
56    private AuthenticationTokenSecretManager secretManager;
57  
58  
59    @Override
60    public void start(CoprocessorEnvironment env) {
61      // if running at region
62      if (env instanceof RegionCoprocessorEnvironment) {
63        RegionCoprocessorEnvironment regionEnv =
64            (RegionCoprocessorEnvironment)env;
65        RpcServerInterface server = regionEnv.getRegionServerServices().getRpcServer();
66        SecretManager<?> mgr = ((RpcServer)server).getSecretManager();
67        if (mgr instanceof AuthenticationTokenSecretManager) {
68          secretManager = (AuthenticationTokenSecretManager)mgr;
69        }
70      }
71    }
72  
73    @Override
74    public void stop(CoprocessorEnvironment env) throws IOException {
75    }
76  
77    /**
78     * @param ugi A user group information.
79     * @return true if delegation token operation is allowed
80     */
81    private boolean isAllowedDelegationTokenOp(UserGroupInformation ugi) throws IOException {
82      AuthenticationMethod authMethod = ugi.getAuthenticationMethod();
83      if (authMethod == AuthenticationMethod.PROXY) {
84        authMethod = ugi.getRealUser().getAuthenticationMethod();
85      }
86      if (authMethod != AuthenticationMethod.KERBEROS
87          && authMethod != AuthenticationMethod.KERBEROS_SSL
88          && authMethod != AuthenticationMethod.CERTIFICATE) {
89        return false;
90      }
91      return true;
92    }
93  
94    // AuthenticationService implementation
95  
96    @Override
97    public Service getService() {
98      return AuthenticationProtos.AuthenticationService.newReflectiveService(this);
99    }
100 
101   @Override
102   public void getAuthenticationToken(RpcController controller,
103                                      AuthenticationProtos.GetAuthenticationTokenRequest request,
104                                      RpcCallback<AuthenticationProtos.GetAuthenticationTokenResponse> done) {
105     AuthenticationProtos.GetAuthenticationTokenResponse.Builder response =
106         AuthenticationProtos.GetAuthenticationTokenResponse.newBuilder();
107 
108     try {
109       if (secretManager == null) {
110         throw new IOException(
111             "No secret manager configured for token authentication");
112       }
113 
114       User currentUser = RequestContext.getRequestUser();
115       UserGroupInformation ugi = null;
116       if (currentUser != null) {
117         ugi = currentUser.getUGI();
118       }
119       if (currentUser == null) {
120         throw new AccessDeniedException("No authenticated user for request!");
121       } else if (!isAllowedDelegationTokenOp(ugi)) {
122         LOG.warn("Token generation denied for user="+currentUser.getName()
123             +", authMethod="+ugi.getAuthenticationMethod());
124         throw new AccessDeniedException(
125             "Token generation only allowed for Kerberos authenticated clients");
126       }
127 
128       Token<AuthenticationTokenIdentifier> token =
129           secretManager.generateToken(currentUser.getName());
130       response.setToken(ProtobufUtil.toToken(token)).build();
131     } catch (IOException ioe) {
132       ResponseConverter.setControllerException(controller, ioe);
133     }
134     done.run(response.build());
135   }
136 
137   @Override
138   public void whoAmI(RpcController controller, AuthenticationProtos.WhoAmIRequest request,
139                      RpcCallback<AuthenticationProtos.WhoAmIResponse> done) {
140     User requestUser = RequestContext.getRequestUser();
141     AuthenticationProtos.WhoAmIResponse.Builder response =
142         AuthenticationProtos.WhoAmIResponse.newBuilder();
143     if (requestUser != null) {
144       response.setUsername(requestUser.getShortName());
145       AuthenticationMethod method = requestUser.getUGI().getAuthenticationMethod();
146       if (method != null) {
147         response.setAuthMethod(method.name());
148       }
149     }
150     done.run(response.build());
151   }
152 }