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.Coprocessor;
29  import org.apache.hadoop.hbase.CoprocessorEnvironment;
30  import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
31  import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
32  import org.apache.hadoop.hbase.ipc.RpcServer;
33  import org.apache.hadoop.hbase.ipc.RequestContext;
34  import org.apache.hadoop.hbase.ipc.RpcServerInterface;
35  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
36  import org.apache.hadoop.hbase.protobuf.ResponseConverter;
37  import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos;
38  import org.apache.hadoop.hbase.security.AccessDeniedException;
39  import org.apache.hadoop.hbase.security.User;
40  import org.apache.hadoop.security.UserGroupInformation;
41  import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
42  import org.apache.hadoop.security.token.SecretManager;
43  import org.apache.hadoop.security.token.Token;
44  
45  /**
46   * Provides a service for obtaining authentication tokens via the
47   * {@link AuthenticationProtos} AuthenticationService coprocessor service.
48   */
49  public class TokenProvider implements AuthenticationProtos.AuthenticationService.Interface,
50      Coprocessor, CoprocessorService {
51  
52    private static Log LOG = LogFactory.getLog(TokenProvider.class);
53  
54    private AuthenticationTokenSecretManager secretManager;
55  
56  
57    @Override
58    public void start(CoprocessorEnvironment env) {
59      // if running at region
60      if (env instanceof RegionCoprocessorEnvironment) {
61        RegionCoprocessorEnvironment regionEnv =
62            (RegionCoprocessorEnvironment)env;
63        RpcServerInterface server = regionEnv.getRegionServerServices().getRpcServer();
64        SecretManager<?> mgr = ((RpcServer)server).getSecretManager();
65        if (mgr instanceof AuthenticationTokenSecretManager) {
66          secretManager = (AuthenticationTokenSecretManager)mgr;
67        }
68      }
69    }
70  
71    @Override
72    public void stop(CoprocessorEnvironment env) throws IOException {
73    }
74  
75    /**
76     * @param ugi A user group information.
77     * @return true if delegation token operation is allowed
78     */
79    private boolean isAllowedDelegationTokenOp(UserGroupInformation ugi) throws IOException {
80      AuthenticationMethod authMethod = ugi.getAuthenticationMethod();
81      if (authMethod == AuthenticationMethod.PROXY) {
82        authMethod = ugi.getRealUser().getAuthenticationMethod();
83      }
84      if (authMethod != AuthenticationMethod.KERBEROS
85          && authMethod != AuthenticationMethod.KERBEROS_SSL
86          && authMethod != AuthenticationMethod.CERTIFICATE) {
87        return false;
88      }
89      return true;
90    }
91  
92    // AuthenticationService implementation
93  
94    @Override
95    public Service getService() {
96      return AuthenticationProtos.AuthenticationService.newReflectiveService(this);
97    }
98  
99    @Override
100   public void getAuthenticationToken(RpcController controller,
101                                      AuthenticationProtos.GetAuthenticationTokenRequest request,
102                                      RpcCallback<AuthenticationProtos.GetAuthenticationTokenResponse> done) {
103     AuthenticationProtos.GetAuthenticationTokenResponse.Builder response =
104         AuthenticationProtos.GetAuthenticationTokenResponse.newBuilder();
105 
106     try {
107       if (secretManager == null) {
108         throw new IOException(
109             "No secret manager configured for token authentication");
110       }
111 
112       User currentUser = RequestContext.getRequestUser();
113       UserGroupInformation ugi = null;
114       if (currentUser != null) {
115         ugi = currentUser.getUGI();
116       }
117       if (currentUser == null) {
118         throw new AccessDeniedException("No authenticated user for request!");
119       } else if (!isAllowedDelegationTokenOp(ugi)) {
120         LOG.warn("Token generation denied for user="+currentUser.getName()
121             +", authMethod="+ugi.getAuthenticationMethod());
122         throw new AccessDeniedException(
123             "Token generation only allowed for Kerberos authenticated clients");
124       }
125 
126       Token<AuthenticationTokenIdentifier> token =
127           secretManager.generateToken(currentUser.getName());
128       response.setToken(ProtobufUtil.toToken(token)).build();
129     } catch (IOException ioe) {
130       ResponseConverter.setControllerException(controller, ioe);
131     }
132     done.run(response.build());
133   }
134 
135   @Override
136   public void whoAmI(RpcController controller, AuthenticationProtos.WhoAmIRequest request,
137                      RpcCallback<AuthenticationProtos.WhoAmIResponse> done) {
138     User requestUser = RequestContext.getRequestUser();
139     AuthenticationProtos.WhoAmIResponse.Builder response =
140         AuthenticationProtos.WhoAmIResponse.newBuilder();
141     if (requestUser != null) {
142       response.setUsername(requestUser.getShortName());
143       AuthenticationMethod method = requestUser.getUGI().getAuthenticationMethod();
144       if (method != null) {
145         response.setAuthMethod(method.name());
146       }
147     }
148     done.run(response.build());
149   }
150 }