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,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.hadoop.hbase.security.access;
20  
21  import java.io.IOException;
22  import java.net.InetAddress;
23  import java.util.Collection;
24  import java.util.Map;
25  
26  import org.apache.hadoop.conf.Configuration;
27  import org.apache.hadoop.hbase.TableName;
28  import org.apache.hadoop.hbase.classification.InterfaceAudience;
29  import org.apache.hadoop.hbase.ipc.RpcServer;
30  import org.apache.hadoop.hbase.security.AccessDeniedException;
31  import org.apache.hadoop.hbase.security.User;
32  import org.apache.hadoop.hbase.security.access.Permission.Action;
33  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  @InterfaceAudience.Private
38  public final class AccessChecker {
39    private static final Logger AUDITLOG =
40        LoggerFactory.getLogger("SecurityLogger." + AccessChecker.class.getName());
41    private TableAuthManager authManager;
42    /**
43     * if we are active, usually false, only true if "hbase.security.authorization"
44     * has been set to true in site configuration.see HBASE-19483.
45     */
46    private boolean authorizationEnabled;
47  
48    public static boolean isAuthorizationSupported(Configuration conf) {
49      return conf.getBoolean(User.HBASE_SECURITY_AUTHORIZATION_CONF_KEY, false);
50    }
51  
52    /**
53     * Constructor with existing configuration
54     *
55     * @param conf Existing configuration to use
56     * @param zkw reference to the {@link ZooKeeperWatcher}
57     */
58    public AccessChecker(final Configuration conf, final ZooKeeperWatcher zkw)
59        throws RuntimeException {
60      // If zk is null or IOException while obtaining auth manager,
61      // throw RuntimeException so that the coprocessor is unloaded.
62      if (zkw != null) {
63        try {
64          this.authManager = TableAuthManager.getOrCreate(zkw, conf);
65        } catch (IOException ioe) {
66          throw new RuntimeException("Error obtaining AccessChecker", ioe);
67        }
68      } else {
69        throw new NullPointerException("Error obtaining AccessChecker, zk found null.");
70      }
71      authorizationEnabled = isAuthorizationSupported(conf);
72    }
73  
74    public TableAuthManager getAuthManager() {
75      return authManager;
76    }
77  
78    public void logResult(AuthResult result) {
79      if (AUDITLOG.isTraceEnabled()) {
80        InetAddress remoteAddr = RpcServer.getRemoteAddress();
81        AUDITLOG.trace("Access " + (result.isAllowed() ? "allowed" : "denied") +
82            " for user " + (result.getUser() != null ? result.getUser().getShortName() : "UNKNOWN") +
83            "; reason: " + result.getReason() +
84            "; remote address: " + (remoteAddr != null ? remoteAddr : "") +
85            "; request: " + result.getRequest() +
86            "; context: " + result.toContextString());
87      }
88    }
89  
90    /**
91     * Authorizes that the current user has any of the given permissions for the
92     * given table, column family and column qualifier.
93     * @param tableName Table requested
94     * @param family Column family requested
95     * @param qualifier Column qualifier requested
96     * @throws IOException if obtaining the current user fails
97     * @throws AccessDeniedException if user has no authorization
98     */
99    public void requirePermission(User user, String request, TableName tableName, byte[] family,
100       byte[] qualifier, Action... permissions) throws IOException {
101     AuthResult result = null;
102 
103     for (Action permission : permissions) {
104       if (authManager.authorize(user, tableName, family, qualifier, permission)) {
105         result = AuthResult.allow(request, "Table permission granted", user,
106             permission, tableName, family, qualifier);
107         break;
108       } else {
109         // rest of the world
110         result = AuthResult.deny(request, "Insufficient permissions", user,
111             permission, tableName, family, qualifier);
112       }
113     }
114     logResult(result);
115     if (authorizationEnabled && !result.isAllowed()) {
116       throw new AccessDeniedException("Insufficient permissions " + result.toContextString());
117     }
118   }
119 
120   /**
121    * Authorizes that the current user has any of the given permissions for the
122    * given table, column family and column qualifier.
123    * @param tableName Table requested
124    * @param family Column family param
125    * @param qualifier Column qualifier param
126    * @throws IOException if obtaining the current user fails
127    * @throws AccessDeniedException if user has no authorization
128    */
129   public void requireTablePermission(User user, String request, TableName tableName, byte[] family,
130       byte[] qualifier, Action... permissions) throws IOException {
131     AuthResult result = null;
132 
133     for (Action permission : permissions) {
134       if (authManager.authorize(user, tableName, null, null, permission)) {
135         result = AuthResult.allow(request, "Table permission granted", user,
136             permission, tableName, null, null);
137         result.getParams().setFamily(family).setQualifier(qualifier);
138         break;
139       } else {
140         // rest of the world
141         result = AuthResult.deny(request, "Insufficient permissions", user,
142             permission, tableName, family, qualifier);
143         result.getParams().setFamily(family).setQualifier(qualifier);
144       }
145     }
146     logResult(result);
147     if (authorizationEnabled && !result.isAllowed()) {
148       throw new AccessDeniedException("Insufficient permissions " + result.toContextString());
149     }
150   }
151 
152   /**
153    * Authorizes that the current user has any of the given permissions to access the table.
154    *
155    * @param tableName Table requested
156    * @param permissions Actions being requested
157    * @throws IOException if obtaining the current user fails
158    * @throws AccessDeniedException if user has no authorization
159    */
160   public void requireAccess(User user, String request, TableName tableName,
161       Action... permissions) throws IOException {
162     AuthResult result = null;
163 
164     for (Action permission : permissions) {
165       if (authManager.hasAccess(user, tableName, permission)) {
166         result = AuthResult.allow(request, "Table permission granted", user,
167             permission, tableName, null, null);
168         break;
169       } else {
170         // rest of the world
171         result = AuthResult.deny(request, "Insufficient permissions", user,
172             permission, tableName, null, null);
173       }
174     }
175     logResult(result);
176     if (authorizationEnabled && !result.isAllowed()) {
177       throw new AccessDeniedException("Insufficient permissions " + result.toContextString());
178     }
179   }
180 
181   /**
182    * Authorizes that the current user has global privileges for the given action.
183    * @param perm The action being requested
184    * @throws IOException if obtaining the current user fails
185    * @throws AccessDeniedException if authorization is denied
186    */
187   public void requirePermission(User user, String request, Action perm) throws IOException {
188     requireGlobalPermission(user, request, perm, null, null);
189   }
190 
191   /**
192    * Checks that the user has the given global permission. The generated
193    * audit log message will contain context information for the operation
194    * being authorized, based on the given parameters.
195    * @param perm Action being requested
196    * @param tableName Affected table name.
197    * @param familyMap Affected column families.
198    */
199   public void requireGlobalPermission(User user, String request, Action perm, TableName tableName,
200       Map<byte[], ? extends Collection<byte[]>> familyMap) throws IOException {
201     AuthResult result = null;
202     if (authManager.authorize(user, perm)) {
203       result = AuthResult.allow(request, "Global check allowed", user, perm, tableName, familyMap);
204       result.getParams().setTableName(tableName).setFamilies(familyMap);
205       logResult(result);
206     } else {
207       result = AuthResult.deny(request, "Global check failed", user, perm, tableName, familyMap);
208       result.getParams().setTableName(tableName).setFamilies(familyMap);
209       logResult(result);
210       if (authorizationEnabled) {
211         throw new AccessDeniedException("Insufficient permissions for user '" +
212             (user != null ? user.getShortName() : "null") +"' (global, action=" +
213             perm.toString() + ")");
214       }
215     }
216   }
217 
218   /**
219    * Checks that the user has the given global permission. The generated
220    * audit log message will contain context information for the operation
221    * being authorized, based on the given parameters.
222    * @param perm Action being requested
223    * @param namespace  The given namespace
224    */
225   public void requireGlobalPermission(User user, String request, Action perm,
226       String namespace) throws IOException {
227     AuthResult authResult = null;
228     if (authManager.authorize(user, perm)) {
229       authResult = AuthResult.allow(request, "Global check allowed", user, perm, null);
230       authResult.getParams().setNamespace(namespace);
231       logResult(authResult);
232     } else {
233       authResult = AuthResult.deny(request, "Global check failed", user, perm, null);
234       authResult.getParams().setNamespace(namespace);
235       logResult(authResult);
236       if (authorizationEnabled) {
237         throw new AccessDeniedException("Insufficient permissions for user '" +
238             (user != null ? user.getShortName() : "null") +"' (global, action=" +
239             perm.toString() + ")");
240       }
241     }
242   }
243 
244   /**
245    * Checks that the user has the given global or namespace permission.
246    * @param namespace  The given namespace
247    * @param permissions Actions being requested
248    */
249   public void requireNamespacePermission(User user, String request, String namespace,
250       Action... permissions) throws IOException {
251     AuthResult result = null;
252 
253     for (Action permission : permissions) {
254       if (authManager.authorize(user, namespace, permission)) {
255         result = AuthResult.allow(request, "Namespace permission granted",
256             user, permission, namespace);
257         break;
258       } else {
259         // rest of the world
260         result = AuthResult.deny(request, "Insufficient permissions", user,
261             permission, namespace);
262       }
263     }
264     logResult(result);
265     if (authorizationEnabled && !result.isAllowed()) {
266       throw new AccessDeniedException("Insufficient permissions "
267           + result.toContextString());
268     }
269   }
270 
271   /**
272    * Checks that the user has the given global or namespace permission.
273    * @param namespace   The given namespace
274    * @param permissions Actions being requested
275    */
276   public void requireNamespacePermission(User user, String request, String namespace, TableName tableName,
277       Map<byte[], ? extends Collection<byte[]>> familyMap, Action... permissions)
278       throws IOException {
279     AuthResult result = null;
280 
281     for (Action permission : permissions) {
282       if (authManager.authorize(user, namespace, permission)) {
283         result = AuthResult.allow(request, "Namespace permission granted",
284             user, permission, namespace);
285         result.getParams().setTableName(tableName).setFamilies(familyMap);
286         break;
287       } else {
288         // rest of the world
289         result = AuthResult.deny(request, "Insufficient permissions", user,
290             permission, namespace);
291         result.getParams().setTableName(tableName).setFamilies(familyMap);
292       }
293     }
294     logResult(result);
295     if (authorizationEnabled && !result.isAllowed()) {
296       throw new AccessDeniedException("Insufficient permissions "
297           + result.toContextString());
298     }
299   }
300 }