Review Board 1.7.22


HBASE-5371. Introduce AccessControllerProtocol.checkPermissions(Permission[] permissons) API

Review Request #3829 - Created Feb. 9, 2012 and updated

enis
HBASE-5371
Reviewers
hbase
hbase-git
We need to introduce something like AccessControllerProtocol.checkPermissions(Permission[] permissions) API, so that clients can check access rights before carrying out the operations. We need this kind of operation for HCATALOG-245, which introduces authorization providers for hbase over hcat. We cannot use getUserPermissions() since it requires ADMIN permissions on the global/table level.

 
security/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
Revision 5091b7d New Change
1
/*
1
/*
2
 * Licensed under the Apache License, Version 2.0 (the "License");
2
 * Licensed under the Apache License, Version 2.0 (the "License");
3
 * you may not use this file except in compliance with the License.
3
 * you may not use this file except in compliance with the License.
4
 * You may obtain a copy of the License at
4
 * You may obtain a copy of the License at
5
 *
5
 *
6
 *     http://www.apache.org/licenses/LICENSE-2.0
6
 *     http://www.apache.org/licenses/LICENSE-2.0
7
 *
7
 *
8
 * Unless required by applicable law or agreed to in writing, software
8
 * Unless required by applicable law or agreed to in writing, software
9
 * distributed under the License is distributed on an "AS IS" BASIS,
9
 * distributed under the License is distributed on an "AS IS" BASIS,
10
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
 * See the License for the specific language governing permissions and
11
 * See the License for the specific language governing permissions and
12
 * limitations under the License.
12
 * limitations under the License.
13
 */
13
 */
14

    
   
14

   
15
package org.apache.hadoop.hbase.security.access;
15
package org.apache.hadoop.hbase.security.access;
16

    
   
16

   
17
import com.google.common.collect.ListMultimap;
17
import java.io.IOException;
18
import com.google.common.collect.Lists;
18
import java.util.Arrays;
19
import com.google.common.collect.MapMaker;
19
import java.util.Collection;

    
   
20
import java.util.HashMap;

    
   
21
import java.util.HashSet;

    
   
22
import java.util.List;

    
   
23
import java.util.Map;

    
   
24
import java.util.Set;

    
   
25

   
20
import org.apache.commons.logging.Log;
26
import org.apache.commons.logging.Log;
21
import org.apache.commons.logging.LogFactory;
27
import org.apache.commons.logging.LogFactory;
22
import org.apache.hadoop.hbase.CoprocessorEnvironment;
28
import org.apache.hadoop.hbase.CoprocessorEnvironment;
23
import org.apache.hadoop.hbase.HColumnDescriptor;
29
import org.apache.hadoop.hbase.HColumnDescriptor;
24
import org.apache.hadoop.hbase.HConstants;
30
import org.apache.hadoop.hbase.HRegionInfo;
25
import org.apache.hadoop.hbase.HTableDescriptor;
31
import org.apache.hadoop.hbase.HTableDescriptor;
26
import org.apache.hadoop.hbase.KeyValue;
32
import org.apache.hadoop.hbase.KeyValue;
27
import org.apache.hadoop.hbase.HRegionInfo;

   
28
import org.apache.hadoop.hbase.ServerName;
33
import org.apache.hadoop.hbase.ServerName;
29
import org.apache.hadoop.hbase.client.Delete;
34
import org.apache.hadoop.hbase.client.Delete;
30
import org.apache.hadoop.hbase.client.Get;
35
import org.apache.hadoop.hbase.client.Get;
31
import org.apache.hadoop.hbase.client.Increment;
36
import org.apache.hadoop.hbase.client.Increment;
32
import org.apache.hadoop.hbase.client.Put;
37
import org.apache.hadoop.hbase.client.Put;
33
import org.apache.hadoop.hbase.client.Result;
38
import org.apache.hadoop.hbase.client.Result;
34
import org.apache.hadoop.hbase.client.Scan;
39
import org.apache.hadoop.hbase.client.Scan;
35
import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
40
import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
36
import org.apache.hadoop.hbase.coprocessor.CoprocessorException;
41
import org.apache.hadoop.hbase.coprocessor.CoprocessorException;
37
import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
42
import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
38
import org.apache.hadoop.hbase.coprocessor.MasterObserver;
43
import org.apache.hadoop.hbase.coprocessor.MasterObserver;
39
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
44
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
40
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
45
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
41
import org.apache.hadoop.hbase.filter.CompareFilter;
46
import org.apache.hadoop.hbase.filter.CompareFilter;
42
import org.apache.hadoop.hbase.filter.FilterList;
47
import org.apache.hadoop.hbase.filter.FilterList;
43
import org.apache.hadoop.hbase.filter.WritableByteArrayComparable;
48
import org.apache.hadoop.hbase.filter.WritableByteArrayComparable;
44
import org.apache.hadoop.hbase.ipc.HBaseRPC;
49
import org.apache.hadoop.hbase.ipc.HBaseRPC;
45
import org.apache.hadoop.hbase.ipc.ProtocolSignature;
50
import org.apache.hadoop.hbase.ipc.ProtocolSignature;
46
import org.apache.hadoop.hbase.ipc.RequestContext;
51
import org.apache.hadoop.hbase.ipc.RequestContext;
47
import org.apache.hadoop.hbase.regionserver.HRegion;
52
import org.apache.hadoop.hbase.regionserver.HRegion;
48
import org.apache.hadoop.hbase.regionserver.InternalScanner;
53
import org.apache.hadoop.hbase.regionserver.InternalScanner;
49
import org.apache.hadoop.hbase.regionserver.RegionScanner;
54
import org.apache.hadoop.hbase.regionserver.RegionScanner;
50
import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
55
import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
51
import org.apache.hadoop.hbase.security.AccessDeniedException;
56
import org.apache.hadoop.hbase.security.AccessDeniedException;
52
import org.apache.hadoop.hbase.security.User;
57
import org.apache.hadoop.hbase.security.User;
53
import org.apache.hadoop.hbase.util.Bytes;
58
import org.apache.hadoop.hbase.util.Bytes;
54

    
   
59

   
55
import java.io.IOException;
60
import com.google.common.collect.ListMultimap;
56
import java.util.*;
61
import com.google.common.collect.Lists;

    
   
62
import com.google.common.collect.MapMaker;

    
   
63
import com.google.common.collect.Maps;

    
   
64
import com.google.common.collect.Sets;
57

    
   
65

   
58
/**
66
/**
59
 * Provides basic authorization checks for data access and administrative
67
 * Provides basic authorization checks for data access and administrative
60
 * operations.
68
 * operations.
61
 *
69
 *
62
 * <p>
70
 * <p>
63
 * {@code AccessController} performs authorization checks for HBase operations
71
 * {@code AccessController} performs authorization checks for HBase operations
64
 * based on:
72
 * based on:
65
 * <ul>
73
 * <ul>
66
 *   <li>the identity of the user performing the operation</li>
74
 *   <li>the identity of the user performing the operation</li>
67
 *   <li>the scope over which the operation is performed, in increasing
75
 *   <li>the scope over which the operation is performed, in increasing
68
 *   specificity: global, table, column family, or qualifier</li>
76
 *   specificity: global, table, column family, or qualifier</li>
69
 *   <li>the type of action being performed (as mapped to
77
 *   <li>the type of action being performed (as mapped to
70
 *   {@link Permission.Action} values)</li>
78
 *   {@link Permission.Action} values)</li>
71
 * </ul>
79
 * </ul>
72
 * If the authorization check fails, an {@link AccessDeniedException}
80
 * If the authorization check fails, an {@link AccessDeniedException}
73
 * will be thrown for the operation.
81
 * will be thrown for the operation.
74
 * </p>
82
 * </p>
75
 *
83
 *
76
 * <p>
84
 * <p>
77
 * To perform authorization checks, {@code AccessController} relies on the
85
 * To perform authorization checks, {@code AccessController} relies on the
78
 * {@link org.apache.hadoop.hbase.ipc.SecureRpcEngine} being loaded to provide
86
 * {@link org.apache.hadoop.hbase.ipc.SecureRpcEngine} being loaded to provide
79
 * the user identities for remote requests.
87
 * the user identities for remote requests.
80
 * </p>
88
 * </p>
81
 *
89
 *
82
 * <p>
90
 * <p>
83
 * The access control lists used for authorization can be manipulated via the
91
 * The access control lists used for authorization can be manipulated via the
84
 * exposed {@link AccessControllerProtocol} implementation, and the associated
92
 * exposed {@link AccessControllerProtocol} implementation, and the associated
85
 * {@code grant}, {@code revoke}, and {@code user_permission} HBase shell
93
 * {@code grant}, {@code revoke}, and {@code user_permission} HBase shell
86
 * commands.
94
 * commands.
87
 * </p>
95
 * </p>
88
 */
96
 */
89
public class AccessController extends BaseRegionObserver
97
public class AccessController extends BaseRegionObserver
90
    implements MasterObserver, AccessControllerProtocol {
98
    implements MasterObserver, AccessControllerProtocol {
91
  /**
99
  /**
92
   * Represents the result of an authorization check for logging and error
100
   * Represents the result of an authorization check for logging and error
93
   * reporting.
101
   * reporting.
94
   */
102
   */
95
  private static class AuthResult {
103
  private static class AuthResult {
96
    private final boolean allowed;
104
    private final boolean allowed;
97
    private final byte[] table;
105
    private final byte[] table;
98
    private final byte[] family;
106
    private final byte[] family;
99
    private final byte[] qualifier;
107
    private final byte[] qualifier;
100
    private final Permission.Action action;
108
    private final Permission.Action action;
101
    private final String reason;
109
    private final String reason;
102
    private final User user;
110
    private final User user;
103

    
   
111

   
104
    public AuthResult(boolean allowed, String reason,  User user,
112
    public AuthResult(boolean allowed, String reason,  User user,
105
        Permission.Action action, byte[] table, byte[] family, byte[] qualifier) {
113
        Permission.Action action, byte[] table, byte[] family, byte[] qualifier) {
106
      this.allowed = allowed;
114
      this.allowed = allowed;
107
      this.reason = reason;
115
      this.reason = reason;
108
      this.user = user;
116
      this.user = user;
109
      this.table = table;
117
      this.table = table;
110
      this.family = family;
118
      this.family = family;
111
      this.qualifier = qualifier;
119
      this.qualifier = qualifier;
112
      this.action = action;
120
      this.action = action;
113
    }
121
    }
114

    
   
122

   
115
    public boolean isAllowed() { return allowed; }
123
    public boolean isAllowed() { return allowed; }
116

    
   
124

   
117
    public User getUser() { return user; }
125
    public User getUser() { return user; }
118

    
   
126

   
119
    public String getReason() { return reason; }
127
    public String getReason() { return reason; }
120

    
   
128

   
121
    public String toContextString() {
129
    public String toContextString() {
122
      return "(user=" + (user != null ? user.getName() : "UNKNOWN") + ", " +
130
      return "(user=" + (user != null ? user.getName() : "UNKNOWN") + ", " +
123
          "scope=" + (table == null ? "GLOBAL" : Bytes.toString(table)) + ", " +
131
          "scope=" + (table == null ? "GLOBAL" : Bytes.toString(table)) + ", " +
124
          "family=" + (family != null ? Bytes.toString(family) : "") + ", " +
132
          "family=" + (family != null ? Bytes.toString(family) : "") + ", " +
125
          "qualifer=" + (qualifier != null ? Bytes.toString(qualifier) : "") + ", " +
133
          "qualifer=" + (qualifier != null ? Bytes.toString(qualifier) : "") + ", " +
126
          "action=" + (action != null ? action.toString() : "") + ")";
134
          "action=" + (action != null ? action.toString() : "") + ")";
127
    }
135
    }
128

    
   
136

   
129
    public String toString() {
137
    public String toString() {
130
      return new StringBuilder("AuthResult")
138
      return new StringBuilder("AuthResult")
131
          .append(toContextString()).toString();
139
          .append(toContextString()).toString();
132
    }
140
    }
133

    
   
141

   
134
    public static AuthResult allow(String reason, User user,
142
    public static AuthResult allow(String reason, User user,
135
        Permission.Action action, byte[] table) {
143
        Permission.Action action, byte[] table) {
136
      return new AuthResult(true, reason, user, action, table, null, null);
144
      return new AuthResult(true, reason, user, action, table, null, null);
137
    }
145
    }
138

    
   
146

   
139
    public static AuthResult deny(String reason, User user,
147
    public static AuthResult deny(String reason, User user,
140
        Permission.Action action, byte[] table) {
148
        Permission.Action action, byte[] table) {
141
      return new AuthResult(false, reason, user, action, table, null, null);
149
      return new AuthResult(false, reason, user, action, table, null, null);
142
    }
150
    }
143

    
   
151

   
144
    public static AuthResult deny(String reason, User user,
152
    public static AuthResult deny(String reason, User user,
145
        Permission.Action action, byte[] table, byte[] family, byte[] qualifier) {
153
        Permission.Action action, byte[] table, byte[] family, byte[] qualifier) {
146
      return new AuthResult(false, reason, user, action, table, family, qualifier);
154
      return new AuthResult(false, reason, user, action, table, family, qualifier);
147
    }
155
    }
148
  }
156
  }
149

    
   
157

   
150
  public static final Log LOG = LogFactory.getLog(AccessController.class);
158
  public static final Log LOG = LogFactory.getLog(AccessController.class);
151

    
   
159

   
152
  private static final Log AUDITLOG =
160
  private static final Log AUDITLOG =
153
    LogFactory.getLog("SecurityLogger."+AccessController.class.getName());
161
    LogFactory.getLog("SecurityLogger."+AccessController.class.getName());
154

    
   
162

   
155
  /**
163
  /**
156
   * Version number for AccessControllerProtocol
164
   * Version number for AccessControllerProtocol
157
   */
165
   */
158
  private static final long PROTOCOL_VERSION = 1L;
166
  private static final long PROTOCOL_VERSION = 2L;
159

    
   
167

   
160
  TableAuthManager authManager = null;
168
  TableAuthManager authManager = null;
161

    
   
169

   
162
  // flags if we are running on a region of the _acl_ table
170
  // flags if we are running on a region of the _acl_ table
163
  boolean aclRegion = false;
171
  boolean aclRegion = false;
164

    
   
172

   
165
  // defined only for Endpoint implementation, so it can have way to
173
  // defined only for Endpoint implementation, so it can have way to
166
  // access region services.
174
  // access region services.
167
  private RegionCoprocessorEnvironment regionEnv;
175
  private RegionCoprocessorEnvironment regionEnv;
168

    
   
176

   
169
  /** Mapping of scanner instances to the user who created them */
177
  /** Mapping of scanner instances to the user who created them */
170
  private Map<InternalScanner,String> scannerOwners =
178
  private Map<InternalScanner,String> scannerOwners =
171
      new MapMaker().weakKeys().makeMap();
179
      new MapMaker().weakKeys().makeMap();
172

    
   
180

   
173
  void initialize(RegionCoprocessorEnvironment e) throws IOException {
181
  void initialize(RegionCoprocessorEnvironment e) throws IOException {
174
    final HRegion region = e.getRegion();
182
    final HRegion region = e.getRegion();
175

    
   
183

   
176
    Map<byte[],ListMultimap<String,TablePermission>> tables =
184
    Map<byte[],ListMultimap<String,TablePermission>> tables =
177
        AccessControlLists.loadAll(region);
185
        AccessControlLists.loadAll(region);
178
    // For each table, write out the table's permissions to the respective
186
    // For each table, write out the table's permissions to the respective
179
    // znode for that table.
187
    // znode for that table.
180
    for (Map.Entry<byte[],ListMultimap<String,TablePermission>> t:
188
    for (Map.Entry<byte[],ListMultimap<String,TablePermission>> t:
181
      tables.entrySet()) {
189
      tables.entrySet()) {
182
      byte[] table = t.getKey();
190
      byte[] table = t.getKey();
183
      String tableName = Bytes.toString(table);
191
      String tableName = Bytes.toString(table);
184
      ListMultimap<String,TablePermission> perms = t.getValue();
192
      ListMultimap<String,TablePermission> perms = t.getValue();
185
      byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms,
193
      byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms,
186
          e.getRegion().getConf());
194
          e.getRegion().getConf());
187
      this.authManager.getZKPermissionWatcher().writeToZookeeper(tableName,
195
      this.authManager.getZKPermissionWatcher().writeToZookeeper(tableName,
188
        serialized);
196
        serialized);
189
    }
197
    }
190
  }
198
  }
191

    
   
199

   
192
  /**
200
  /**
193
   * Writes all table ACLs for the tables in the given Map up into ZooKeeper
201
   * Writes all table ACLs for the tables in the given Map up into ZooKeeper
194
   * znodes.  This is called to synchronize ACL changes following {@code _acl_}
202
   * znodes.  This is called to synchronize ACL changes following {@code _acl_}
195
   * table updates.
203
   * table updates.
196
   */
204
   */
197
  void updateACL(RegionCoprocessorEnvironment e,
205
  void updateACL(RegionCoprocessorEnvironment e,
198
      final Map<byte[], List<KeyValue>> familyMap) {
206
      final Map<byte[], List<KeyValue>> familyMap) {
199
    Set<String> tableSet = new HashSet<String>();
207
    Set<String> tableSet = new HashSet<String>();
200
    for (Map.Entry<byte[], List<KeyValue>> f : familyMap.entrySet()) {
208
    for (Map.Entry<byte[], List<KeyValue>> f : familyMap.entrySet()) {
201
      List<KeyValue> kvs = f.getValue();
209
      List<KeyValue> kvs = f.getValue();
202
      for (KeyValue kv: kvs) {
210
      for (KeyValue kv: kvs) {
203
        if (Bytes.compareTo(kv.getBuffer(), kv.getFamilyOffset(),
211
        if (Bytes.compareTo(kv.getBuffer(), kv.getFamilyOffset(),
204
            kv.getFamilyLength(), AccessControlLists.ACL_LIST_FAMILY, 0,
212
            kv.getFamilyLength(), AccessControlLists.ACL_LIST_FAMILY, 0,
205
            AccessControlLists.ACL_LIST_FAMILY.length) == 0) {
213
            AccessControlLists.ACL_LIST_FAMILY.length) == 0) {
206
          String tableName = Bytes.toString(kv.getRow());
214
          String tableName = Bytes.toString(kv.getRow());
207
          tableSet.add(tableName);
215
          tableSet.add(tableName);
208
        }
216
        }
209
      }
217
      }
210
    }
218
    }
211

    
   
219

   
212
    for (String tableName: tableSet) {
220
    for (String tableName: tableSet) {
213
      try {
221
      try {
214
        ListMultimap<String,TablePermission> perms =
222
        ListMultimap<String,TablePermission> perms =
215
          AccessControlLists.getTablePermissions(regionEnv.getConfiguration(),
223
          AccessControlLists.getTablePermissions(regionEnv.getConfiguration(),
216
              Bytes.toBytes(tableName));
224
              Bytes.toBytes(tableName));
217
        byte[] serialized = AccessControlLists.writePermissionsAsBytes(
225
        byte[] serialized = AccessControlLists.writePermissionsAsBytes(
218
            perms, e.getRegion().getConf());
226
            perms, e.getRegion().getConf());
219
        this.authManager.getZKPermissionWatcher().writeToZookeeper(tableName,
227
        this.authManager.getZKPermissionWatcher().writeToZookeeper(tableName,
220
          serialized);
228
          serialized);
221
      } catch (IOException ex) {
229
      } catch (IOException ex) {
222
        LOG.error("Failed updating permissions mirror for '" + tableName +
230
        LOG.error("Failed updating permissions mirror for '" + tableName +
223
          "'", ex);
231
          "'", ex);
224
      }
232
      }
225
    }
233
    }
226
  }
234
  }
227

    
   
235

   
228
  /**
236
  /**
229
   * Check the current user for authorization to perform a specific action
237
   * Check the current user for authorization to perform a specific action
230
   * against the given set of row data.
238
   * against the given set of row data.
231
   *
239
   *
232
   * <p>Note: Ordering of the authorization checks
240
   * <p>Note: Ordering of the authorization checks
233
   * has been carefully optimized to short-circuit the most common requests
241
   * has been carefully optimized to short-circuit the most common requests
234
   * and minimize the amount of processing required.</p>
242
   * and minimize the amount of processing required.</p>
235
   *
243
   *
236
   * @param permRequest the action being requested
244
   * @param permRequest the action being requested
237
   * @param e the coprocessor environment
245
   * @param e the coprocessor environment
238
   * @param families the map of column families to qualifiers present in
246
   * @param families the map of column families to qualifiers present in
239
   * the request
247
   * the request
240
   * @return
248
   * @return
241
   */
249
   */
242
  AuthResult permissionGranted(User user, TablePermission.Action permRequest,
250
  AuthResult permissionGranted(User user, TablePermission.Action permRequest,
243
      RegionCoprocessorEnvironment e,
251
      RegionCoprocessorEnvironment e,
244
      Map<byte [], ? extends Collection<?>> families) {
252
      Map<byte [], ? extends Collection<?>> families) {
245
    HRegionInfo hri = e.getRegion().getRegionInfo();
253
    HRegionInfo hri = e.getRegion().getRegionInfo();
246
    HTableDescriptor htd = e.getRegion().getTableDesc();
254
    HTableDescriptor htd = e.getRegion().getTableDesc();
247
    byte[] tableName = hri.getTableName();
255
    byte[] tableName = hri.getTableName();
248

    
   
256

   
249
    // 1. All users need read access to .META. and -ROOT- tables.
257
    // 1. All users need read access to .META. and -ROOT- tables.
250
    // this is a very common operation, so deal with it quickly.
258
    // this is a very common operation, so deal with it quickly.
251
    if ((hri.isRootRegion() || hri.isMetaRegion()) &&
259
    if ((hri.isRootRegion() || hri.isMetaRegion()) &&
252
        (permRequest == TablePermission.Action.READ)) {
260
        (permRequest == TablePermission.Action.READ)) {
253
      return AuthResult.allow("All users allowed", user, permRequest,
261
      return AuthResult.allow("All users allowed", user, permRequest,
254
          hri.getTableName());
262
          hri.getTableName());
255
    }
263
    }
256

    
   
264

   
257
    if (user == null) {
265
    if (user == null) {
258
      return AuthResult.deny("No user associated with request!", null,
266
      return AuthResult.deny("No user associated with request!", null,
259
          permRequest, hri.getTableName());
267
          permRequest, hri.getTableName());
260
    }
268
    }
261

    
   
269

   
262
    // 2. The table owner has full privileges
270
    // 2. The table owner has full privileges
263
    String owner = htd.getOwnerString();
271
    String owner = htd.getOwnerString();
264
    if (user.getShortName().equals(owner)) {
272
    if (user.getShortName().equals(owner)) {
265
      // owner of the table has full access
273
      // owner of the table has full access
266
      return AuthResult.allow("User is table owner", user, permRequest,
274
      return AuthResult.allow("User is table owner", user, permRequest,
267
          hri.getTableName());
275
          hri.getTableName());
268
    }
276
    }
269

    
   
277

   
270
    // 3. check for the table-level, if successful we can short-circuit
278
    // 3. check for the table-level, if successful we can short-circuit
271
    if (authManager.authorize(user, tableName, (byte[])null, permRequest)) {
279
    if (authManager.authorize(user, tableName, (byte[])null, permRequest)) {
272
      return AuthResult.allow("Table permission granted", user,
280
      return AuthResult.allow("Table permission granted", user,
273
          permRequest, tableName);
281
          permRequest, tableName);
274
    }
282
    }
275

    
   
283

   
276
    // 4. check permissions against the requested families
284
    // 4. check permissions against the requested families
277
    if (families != null && families.size() > 0) {
285
    if (families != null && families.size() > 0) {
278
      // all families must pass
286
      // all families must pass
279
      for (Map.Entry<byte [], ? extends Collection<?>> family : families.entrySet()) {
287
      for (Map.Entry<byte [], ? extends Collection<?>> family : families.entrySet()) {
280
        // a) check for family level access
288
        // a) check for family level access
281
        if (authManager.authorize(user, tableName, family.getKey(),
289
        if (authManager.authorize(user, tableName, family.getKey(),
282
            permRequest)) {
290
            permRequest)) {
283
          continue;  // family-level permission overrides per-qualifier
291
          continue;  // family-level permission overrides per-qualifier
284
        }
292
        }
285

    
   
293

   
286
        // b) qualifier level access can still succeed
294
        // b) qualifier level access can still succeed
287
        if ((family.getValue() != null) && (family.getValue().size() > 0)) {
295
        if ((family.getValue() != null) && (family.getValue().size() > 0)) {
288
          if (family.getValue() instanceof Set) {
296
          if (family.getValue() instanceof Set) {
289
            // for each qualifier of the family
297
            // for each qualifier of the family
290
            Set<byte[]> familySet = (Set<byte[]>)family.getValue();
298
            Set<byte[]> familySet = (Set<byte[]>)family.getValue();
291
            for (byte[] qualifier : familySet) {
299
            for (byte[] qualifier : familySet) {
292
              if (!authManager.authorize(user, tableName, family.getKey(),
300
              if (!authManager.authorize(user, tableName, family.getKey(),
293
                                         qualifier, permRequest)) {
301
                                         qualifier, permRequest)) {
294
                return AuthResult.deny("Failed qualifier check", user,
302
                return AuthResult.deny("Failed qualifier check", user,
295
                    permRequest, tableName, family.getKey(), qualifier);
303
                    permRequest, tableName, family.getKey(), qualifier);
296
              }
304
              }
297
            }
305
            }
298
          } else if (family.getValue() instanceof List) { // List<KeyValue>
306
          } else if (family.getValue() instanceof List) { // List<KeyValue>
299
            List<KeyValue> kvList = (List<KeyValue>)family.getValue();
307
            List<KeyValue> kvList = (List<KeyValue>)family.getValue();
300
            for (KeyValue kv : kvList) {
308
            for (KeyValue kv : kvList) {
301
              if (!authManager.authorize(user, tableName, family.getKey(),
309
              if (!authManager.authorize(user, tableName, family.getKey(),
302
                      kv.getQualifier(), permRequest)) {
310
                      kv.getQualifier(), permRequest)) {
303
                return AuthResult.deny("Failed qualifier check", user,
311
                return AuthResult.deny("Failed qualifier check", user,
304
                    permRequest, tableName, family.getKey(), kv.getQualifier());
312
                    permRequest, tableName, family.getKey(), kv.getQualifier());
305
              }
313
              }
306
            }
314
            }
307
          }
315
          }
308
        } else {
316
        } else {
309
          // no qualifiers and family-level check already failed
317
          // no qualifiers and family-level check already failed
310
          return AuthResult.deny("Failed family check", user, permRequest,
318
          return AuthResult.deny("Failed family check", user, permRequest,
311
              tableName, family.getKey(), null);
319
              tableName, family.getKey(), null);
312
        }
320
        }
313
      }
321
      }
314

    
   
322

   
315
      // all family checks passed
323
      // all family checks passed
316
      return AuthResult.allow("All family checks passed", user, permRequest,
324
      return AuthResult.allow("All family checks passed", user, permRequest,
317
          tableName);
325
          tableName);
318
    }
326
    }
319

    
   
327

   
320
    // 5. no families to check and table level access failed
328
    // 5. no families to check and table level access failed
321
    return AuthResult.deny("No families to check and table permission failed",
329
    return AuthResult.deny("No families to check and table permission failed",
322
        user, permRequest, tableName);
330
        user, permRequest, tableName);
323
  }
331
  }
324

    
   
332

   
325
  private void logResult(AuthResult result) {
333
  private void logResult(AuthResult result) {
326
    if (AUDITLOG.isTraceEnabled()) {
334
    if (AUDITLOG.isTraceEnabled()) {
327
      AUDITLOG.trace("Access " + (result.isAllowed() ? "allowed" : "denied") +
335
      AUDITLOG.trace("Access " + (result.isAllowed() ? "allowed" : "denied") +
328
          " for user " + (result.getUser() != null ? result.getUser().getShortName() : "UNKNOWN") +
336
          " for user " + (result.getUser() != null ? result.getUser().getShortName() : "UNKNOWN") +
329
          "; reason: " + result.getReason() +
337
          "; reason: " + result.getReason() +
330
          "; context: " + result.toContextString());
338
          "; context: " + result.toContextString());
331
    }
339
    }
332
  }
340
  }
333

    
   
341

   
334
  /**
342
  /**
335
   * Returns the active user to which authorization checks should be applied.
343
   * Returns the active user to which authorization checks should be applied.
336
   * If we are in the context of an RPC call, the remote user is used,
344
   * If we are in the context of an RPC call, the remote user is used,
337
   * otherwise the currently logged in user is used.
345
   * otherwise the currently logged in user is used.
338
   */
346
   */
339
  private User getActiveUser() throws IOException {
347
  private User getActiveUser() throws IOException {
340
    User user = RequestContext.getRequestUser();
348
    User user = RequestContext.getRequestUser();
341
    if (!RequestContext.isInRequestContext()) {
349
    if (!RequestContext.isInRequestContext()) {
342
      // for non-rpc handling, fallback to system user
350
      // for non-rpc handling, fallback to system user
343
      user = User.getCurrent();
351
      user = User.getCurrent();
344
    }
352
    }
345
    return user;
353
    return user;
346
  }
354
  }
347

    
   
355

   
348
  /**
356
  /**
349
   * Authorizes that the current user has global privileges for the given action.
357
   * Authorizes that the current user has global privileges for the given action.
350
   * @param perm The action being requested
358
   * @param perm The action being requested
351
   * @throws IOException if obtaining the current user fails
359
   * @throws IOException if obtaining the current user fails
352
   * @throws AccessDeniedException if authorization is denied
360
   * @throws AccessDeniedException if authorization is denied
353
   */
361
   */
354
  private void requirePermission(Permission.Action perm) throws IOException {
362
  private void requirePermission(Permission.Action perm) throws IOException {
355
    User user = getActiveUser();
363
    User user = getActiveUser();
356
    if (authManager.authorize(user, perm)) {
364
    if (authManager.authorize(user, perm)) {
357
      logResult(AuthResult.allow("Global check allowed", user, perm, null));
365
      logResult(AuthResult.allow("Global check allowed", user, perm, null));
358
    } else {
366
    } else {
359
      logResult(AuthResult.deny("Global check failed", user, perm, null));
367
      logResult(AuthResult.deny("Global check failed", user, perm, null));
360
      throw new AccessDeniedException("Insufficient permissions for user '" +
368
      throw new AccessDeniedException("Insufficient permissions for user '" +
361
          (user != null ? user.getShortName() : "null") +"' (global, action=" +
369
          (user != null ? user.getShortName() : "null") +"' (global, action=" +
362
          perm.toString() + ")");
370
          perm.toString() + ")");
363
    }
371
    }
364
  }
372
  }
365

    
   
373

   
366
  /**
374
  /**
367
   * Authorizes that the current user has permission to perform the given
375
   * Authorizes that the current user has permission to perform the given
368
   * action on the set of table column families.
376
   * action on the set of table column families.
369
   * @param perm Action that is required
377
   * @param perm Action that is required
370
   * @param env The current coprocessor environment
378
   * @param env The current coprocessor environment
371
   * @param families The set of column families present/required in the request
379
   * @param families The set of column families present/required in the request
372
   * @throws AccessDeniedException if the authorization check failed
380
   * @throws AccessDeniedException if the authorization check failed
373
   */
381
   */
374
  private void requirePermission(Permission.Action perm,
382
  private void requirePermission(Permission.Action perm,
375
        RegionCoprocessorEnvironment env, Collection<byte[]> families)
383
        RegionCoprocessorEnvironment env, Collection<byte[]> families)
376
      throws IOException {
384
      throws IOException {
377
    // create a map of family-qualifier
385
    // create a map of family-qualifier
378
    HashMap<byte[], Set<byte[]>> familyMap = new HashMap<byte[], Set<byte[]>>();
386
    HashMap<byte[], Set<byte[]>> familyMap = new HashMap<byte[], Set<byte[]>>();
379
    for (byte[] family : families) {
387
    for (byte[] family : families) {
380
      familyMap.put(family, null);
388
      familyMap.put(family, null);
381
    }
389
    }
382
    requirePermission(perm, env, familyMap);
390
    requirePermission(perm, env, familyMap);
383
  }
391
  }
384

    
   
392

   
385
  /**
393
  /**
386
   * Authorizes that the current user has permission to perform the given
394
   * Authorizes that the current user has permission to perform the given
387
   * action on the set of table column families.
395
   * action on the set of table column families.
388
   * @param perm Action that is required
396
   * @param perm Action that is required
389
   * @param env The current coprocessor environment
397
   * @param env The current coprocessor environment
390
   * @param families The map of column families-qualifiers.
398
   * @param families The map of column families-qualifiers.
391
   * @throws AccessDeniedException if the authorization check failed
399
   * @throws AccessDeniedException if the authorization check failed
392
   */
400
   */
393
  private void requirePermission(Permission.Action perm,
401
  private void requirePermission(Permission.Action perm,
394
        RegionCoprocessorEnvironment env,
402
        RegionCoprocessorEnvironment env,
395
        Map<byte[], ? extends Collection<?>> families)
403
        Map<byte[], ? extends Collection<?>> families)
396
      throws IOException {
404
      throws IOException {
397
    User user = getActiveUser();
405
    User user = getActiveUser();
398
    AuthResult result = permissionGranted(user, perm, env, families);
406
    AuthResult result = permissionGranted(user, perm, env, families);
399
    logResult(result);
407
    logResult(result);
400

    
   
408

   
401
    if (!result.isAllowed()) {
409
    if (!result.isAllowed()) {
402
      StringBuffer sb = new StringBuffer("");
410
      StringBuffer sb = new StringBuffer("");
403
      if ((families != null && families.size() > 0)) {
411
      if ((families != null && families.size() > 0)) {
404
        for (byte[] familyName : families.keySet()) {
412
        for (byte[] familyName : families.keySet()) {
405
          if (sb.length() != 0) {
413
          if (sb.length() != 0) {
406
            sb.append(", ");
414
            sb.append(", ");
407
          }
415
          }
408
          sb.append(Bytes.toString(familyName));
416
          sb.append(Bytes.toString(familyName));
409
        }
417
        }
410
      }
418
      }
411
      throw new AccessDeniedException("Insufficient permissions (table=" +
419
      throw new AccessDeniedException("Insufficient permissions (table=" +
412
        env.getRegion().getTableDesc().getNameAsString()+
420
        env.getRegion().getTableDesc().getNameAsString()+
413
        ((families != null && families.size() > 0) ? ", family: " +
421
        ((families != null && families.size() > 0) ? ", family: " +
414
        sb.toString() : "") + ", action=" +
422
        sb.toString() : "") + ", action=" +
415
        perm.toString() + ")");
423
        perm.toString() + ")");
416
    }
424
    }
417
  }
425
  }
418

    
   
426

   
419
  /**
427
  /**
420
   * Returns <code>true</code> if the current user is allowed the given action
428
   * Returns <code>true</code> if the current user is allowed the given action
421
   * over at least one of the column qualifiers in the given column families.
429
   * over at least one of the column qualifiers in the given column families.
422
   */
430
   */
423
  private boolean hasFamilyQualifierPermission(User user,
431
  private boolean hasFamilyQualifierPermission(User user,
424
      TablePermission.Action perm,
432
      TablePermission.Action perm,
425
      RegionCoprocessorEnvironment env,
433
      RegionCoprocessorEnvironment env,
426
      Map<byte[], ? extends Set<byte[]>> familyMap)
434
      Map<byte[], ? extends Set<byte[]>> familyMap)
427
    throws IOException {
435
    throws IOException {
428
    HRegionInfo hri = env.getRegion().getRegionInfo();
436
    HRegionInfo hri = env.getRegion().getRegionInfo();
429
    byte[] tableName = hri.getTableName();
437
    byte[] tableName = hri.getTableName();
430

    
   
438

   
431
    if (user == null) {
439
    if (user == null) {
432
      return false;
440
      return false;
433
    }
441
    }
434

    
   
442

   
435
    if (familyMap != null && familyMap.size() > 0) {
443
    if (familyMap != null && familyMap.size() > 0) {
436
      // at least one family must be allowed
444
      // at least one family must be allowed
437
      for (Map.Entry<byte[], ? extends Set<byte[]>> family :
445
      for (Map.Entry<byte[], ? extends Set<byte[]>> family :
438
          familyMap.entrySet()) {
446
          familyMap.entrySet()) {
439
        if (family.getValue() != null && !family.getValue().isEmpty()) {
447
        if (family.getValue() != null && !family.getValue().isEmpty()) {
440
          for (byte[] qualifier : family.getValue()) {
448
          for (byte[] qualifier : family.getValue()) {
441
            if (authManager.matchPermission(user, tableName,
449
            if (authManager.matchPermission(user, tableName,
442
                family.getKey(), qualifier, perm)) {
450
                family.getKey(), qualifier, perm)) {
443
              return true;
451
              return true;
444
            }
452
            }
445
          }
453
          }
446
        } else {
454
        } else {
447
          if (authManager.matchPermission(user, tableName, family.getKey(),
455
          if (authManager.matchPermission(user, tableName, family.getKey(),
448
              perm)) {
456
              perm)) {
449
            return true;
457
            return true;
450
          }
458
          }
451
        }
459
        }
452
      }
460
      }
453
    } else if (LOG.isDebugEnabled()) {
461
    } else if (LOG.isDebugEnabled()) {
454
      LOG.debug("Empty family map passed for permission check");
462
      LOG.debug("Empty family map passed for permission check");
455
    }
463
    }
456

    
   
464

   
457
    return false;
465
    return false;
458
  }
466
  }
459

    
   
467

   
460
  /* ---- MasterObserver implementation ---- */
468
  /* ---- MasterObserver implementation ---- */
461
  public void start(CoprocessorEnvironment env) throws IOException {
469
  public void start(CoprocessorEnvironment env) throws IOException {
462
    // if running on HMaster
470
    // if running on HMaster
463
    if (env instanceof MasterCoprocessorEnvironment) {
471
    if (env instanceof MasterCoprocessorEnvironment) {
464
      MasterCoprocessorEnvironment e = (MasterCoprocessorEnvironment)env;
472
      MasterCoprocessorEnvironment e = (MasterCoprocessorEnvironment)env;
465
      this.authManager = TableAuthManager.get(
473
      this.authManager = TableAuthManager.get(
466
          e.getMasterServices().getZooKeeper(),
474
          e.getMasterServices().getZooKeeper(),
467
          e.getConfiguration());
475
          e.getConfiguration());
468
    }
476
    }
469

    
   
477

   
470
    // if running at region
478
    // if running at region
471
    if (env instanceof RegionCoprocessorEnvironment) {
479
    if (env instanceof RegionCoprocessorEnvironment) {
472
      regionEnv = (RegionCoprocessorEnvironment)env;
480
      regionEnv = (RegionCoprocessorEnvironment)env;
473
    }
481
    }
474
  }
482
  }
475

    
   
483

   
476
  public void stop(CoprocessorEnvironment env) {
484
  public void stop(CoprocessorEnvironment env) {
477

    
   
485

   
478
  }
486
  }
479

    
   
487

   
480
  @Override
488
  @Override
481
  public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> c,
489
  public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> c,
482
      HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
490
      HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
483
    requirePermission(Permission.Action.CREATE);
491
    requirePermission(Permission.Action.CREATE);
484

    
   
492

   
485
    // default the table owner if not specified
493
    // default the table owner if not specified
486
    User owner = getActiveUser();
494
    User owner = getActiveUser();
487
    if (desc.getOwnerString() == null ||
495
    if (desc.getOwnerString() == null ||
488
        desc.getOwnerString().equals("")) {
496
        desc.getOwnerString().equals("")) {
489
      desc.setOwner(owner);
497
      desc.setOwner(owner);
490
    }
498
    }
491
  }
499
  }
492

    
   
500

   
493
  @Override
501
  @Override
494
  public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> c,
502
  public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> c,
495
      HTableDescriptor desc, HRegionInfo[] regions) throws IOException {}
503
      HTableDescriptor desc, HRegionInfo[] regions) throws IOException {}
496

    
   
504

   
497
  @Override
505
  @Override
498
  public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c,
506
  public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c,
499
      byte[] tableName) throws IOException {
507
      byte[] tableName) throws IOException {
500
    requirePermission(Permission.Action.CREATE);
508
    requirePermission(Permission.Action.CREATE);
501
  }
509
  }
502
  @Override
510
  @Override
503
  public void postDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c,
511
  public void postDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c,
504
      byte[] tableName) throws IOException {}
512
      byte[] tableName) throws IOException {}
505

    
   
513

   
506

    
   
514

   
507
  @Override
515
  @Override
508
  public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> c,
516
  public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> c,
509
      byte[] tableName, HTableDescriptor htd) throws IOException {
517
      byte[] tableName, HTableDescriptor htd) throws IOException {
510
    requirePermission(Permission.Action.CREATE);
518
    requirePermission(Permission.Action.CREATE);
511
  }
519
  }
512
  @Override
520
  @Override
513
  public void postModifyTable(ObserverContext<MasterCoprocessorEnvironment> c,
521
  public void postModifyTable(ObserverContext<MasterCoprocessorEnvironment> c,
514
      byte[] tableName, HTableDescriptor htd) throws IOException {}
522
      byte[] tableName, HTableDescriptor htd) throws IOException {}
515

    
   
523

   
516

    
   
524

   
517
  @Override
525
  @Override
518
  public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> c,
526
  public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> c,
519
      byte[] tableName, HColumnDescriptor column) throws IOException {
527
      byte[] tableName, HColumnDescriptor column) throws IOException {
520
    requirePermission(Permission.Action.CREATE);
528
    requirePermission(Permission.Action.CREATE);
521
  }
529
  }
522
  @Override
530
  @Override
523
  public void postAddColumn(ObserverContext<MasterCoprocessorEnvironment> c,
531
  public void postAddColumn(ObserverContext<MasterCoprocessorEnvironment> c,
524
      byte[] tableName, HColumnDescriptor column) throws IOException {}
532
      byte[] tableName, HColumnDescriptor column) throws IOException {}
525

    
   
533

   
526

    
   
534

   
527
  @Override
535
  @Override
528
  public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> c,
536
  public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> c,
529
      byte[] tableName, HColumnDescriptor descriptor) throws IOException {
537
      byte[] tableName, HColumnDescriptor descriptor) throws IOException {
530
    requirePermission(Permission.Action.CREATE);
538
    requirePermission(Permission.Action.CREATE);
531
  }
539
  }
532
  @Override
540
  @Override
533
  public void postModifyColumn(ObserverContext<MasterCoprocessorEnvironment> c,
541
  public void postModifyColumn(ObserverContext<MasterCoprocessorEnvironment> c,
534
      byte[] tableName, HColumnDescriptor descriptor) throws IOException {}
542
      byte[] tableName, HColumnDescriptor descriptor) throws IOException {}
535

    
   
543

   
536

    
   
544

   
537
  @Override
545
  @Override
538
  public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c,
546
  public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c,
539
      byte[] tableName, byte[] col) throws IOException {
547
      byte[] tableName, byte[] col) throws IOException {
540
    requirePermission(Permission.Action.CREATE);
548
    requirePermission(Permission.Action.CREATE);
541
  }
549
  }
542
  @Override
550
  @Override
543
  public void postDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c,
551
  public void postDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c,
544
      byte[] tableName, byte[] col) throws IOException {}
552
      byte[] tableName, byte[] col) throws IOException {}
545

    
   
553

   
546

    
   
554

   
547
  @Override
555
  @Override
548
  public void preEnableTable(ObserverContext<MasterCoprocessorEnvironment> c,
556
  public void preEnableTable(ObserverContext<MasterCoprocessorEnvironment> c,
549
      byte[] tableName) throws IOException {
557
      byte[] tableName) throws IOException {
550
    /* TODO: Allow for users with global CREATE permission and the table owner */
558
    /* TODO: Allow for users with global CREATE permission and the table owner */
551
    requirePermission(Permission.Action.ADMIN);
559
    requirePermission(Permission.Action.ADMIN);
552
  }
560
  }
553
  @Override
561
  @Override
554
  public void postEnableTable(ObserverContext<MasterCoprocessorEnvironment> c,
562
  public void postEnableTable(ObserverContext<MasterCoprocessorEnvironment> c,
555
      byte[] tableName) throws IOException {}
563
      byte[] tableName) throws IOException {}
556

    
   
564

   
557
  @Override
565
  @Override
558
  public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> c,
566
  public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> c,
559
      byte[] tableName) throws IOException {
567
      byte[] tableName) throws IOException {
560
    /* TODO: Allow for users with global CREATE permission and the table owner */
568
    /* TODO: Allow for users with global CREATE permission and the table owner */
561
    requirePermission(Permission.Action.ADMIN);
569
    requirePermission(Permission.Action.ADMIN);
562
  }
570
  }
563
  @Override
571
  @Override
564
  public void postDisableTable(ObserverContext<MasterCoprocessorEnvironment> c,
572
  public void postDisableTable(ObserverContext<MasterCoprocessorEnvironment> c,
565
      byte[] tableName) throws IOException {}
573
      byte[] tableName) throws IOException {}
566

    
   
574

   
567
  @Override
575
  @Override
568
  public void preMove(ObserverContext<MasterCoprocessorEnvironment> c,
576
  public void preMove(ObserverContext<MasterCoprocessorEnvironment> c,
569
      HRegionInfo region, ServerName srcServer, ServerName destServer)
577
      HRegionInfo region, ServerName srcServer, ServerName destServer)
570
    throws IOException {
578
    throws IOException {
571
    requirePermission(Permission.Action.ADMIN);
579
    requirePermission(Permission.Action.ADMIN);
572
  }
580
  }
573
  @Override
581
  @Override
574
  public void postMove(ObserverContext<MasterCoprocessorEnvironment> c,
582
  public void postMove(ObserverContext<MasterCoprocessorEnvironment> c,
575
      HRegionInfo region, ServerName srcServer, ServerName destServer)
583
      HRegionInfo region, ServerName srcServer, ServerName destServer)
576
    throws IOException {}
584
    throws IOException {}
577

    
   
585

   
578
  @Override
586
  @Override
579
  public void preAssign(ObserverContext<MasterCoprocessorEnvironment> c,
587
  public void preAssign(ObserverContext<MasterCoprocessorEnvironment> c,
580
      HRegionInfo regionInfo) throws IOException {
588
      HRegionInfo regionInfo) throws IOException {
581
    requirePermission(Permission.Action.ADMIN);
589
    requirePermission(Permission.Action.ADMIN);
582
  }
590
  }
583
  @Override
591
  @Override
584
  public void postAssign(ObserverContext<MasterCoprocessorEnvironment> c,
592
  public void postAssign(ObserverContext<MasterCoprocessorEnvironment> c,
585
      HRegionInfo regionInfo) throws IOException {}
593
      HRegionInfo regionInfo) throws IOException {}
586

    
   
594

   
587
  @Override
595
  @Override
588
  public void preUnassign(ObserverContext<MasterCoprocessorEnvironment> c,
596
  public void preUnassign(ObserverContext<MasterCoprocessorEnvironment> c,
589
       HRegionInfo regionInfo, boolean force) throws IOException {
597
       HRegionInfo regionInfo, boolean force) throws IOException {
590
    requirePermission(Permission.Action.ADMIN);
598
    requirePermission(Permission.Action.ADMIN);
591
  }
599
  }
592
  @Override
600
  @Override
593
  public void postUnassign(ObserverContext<MasterCoprocessorEnvironment> c,
601
  public void postUnassign(ObserverContext<MasterCoprocessorEnvironment> c,
594
      HRegionInfo regionInfo, boolean force) throws IOException {}
602
      HRegionInfo regionInfo, boolean force) throws IOException {}
595

    
   
603

   
596
  @Override
604
  @Override
597
  public void preBalance(ObserverContext<MasterCoprocessorEnvironment> c)
605
  public void preBalance(ObserverContext<MasterCoprocessorEnvironment> c)
598
      throws IOException {
606
      throws IOException {
599
    requirePermission(Permission.Action.ADMIN);
607
    requirePermission(Permission.Action.ADMIN);
600
  }
608
  }
601
  @Override
609
  @Override
602
  public void postBalance(ObserverContext<MasterCoprocessorEnvironment> c)
610
  public void postBalance(ObserverContext<MasterCoprocessorEnvironment> c)
603
      throws IOException {}
611
      throws IOException {}
604

    
   
612

   
605
  @Override
613
  @Override
606
  public boolean preBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> c,
614
  public boolean preBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> c,
607
      boolean newValue) throws IOException {
615
      boolean newValue) throws IOException {
608
    requirePermission(Permission.Action.ADMIN);
616
    requirePermission(Permission.Action.ADMIN);
609
    return newValue;
617
    return newValue;
610
  }
618
  }
611
  @Override
619
  @Override
612
  public void postBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> c,
620
  public void postBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> c,
613
      boolean oldValue, boolean newValue) throws IOException {}
621
      boolean oldValue, boolean newValue) throws IOException {}
614

    
   
622

   
615
  @Override
623
  @Override
616
  public void preShutdown(ObserverContext<MasterCoprocessorEnvironment> c)
624
  public void preShutdown(ObserverContext<MasterCoprocessorEnvironment> c)
617
      throws IOException {
625
      throws IOException {
618
    requirePermission(Permission.Action.ADMIN);
626
    requirePermission(Permission.Action.ADMIN);
619
  }
627
  }
620

    
   
628

   
621
  @Override
629
  @Override
622
  public void preStopMaster(ObserverContext<MasterCoprocessorEnvironment> c)
630
  public void preStopMaster(ObserverContext<MasterCoprocessorEnvironment> c)
623
      throws IOException {
631
      throws IOException {
624
    requirePermission(Permission.Action.ADMIN);
632
    requirePermission(Permission.Action.ADMIN);
625
  }
633
  }
626

    
   
634

   
627
  @Override
635
  @Override
628
  public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx)
636
  public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx)
629
      throws IOException {
637
      throws IOException {
630
    // initialize the ACL storage table
638
    // initialize the ACL storage table
631
    AccessControlLists.init(ctx.getEnvironment().getMasterServices());
639
    AccessControlLists.init(ctx.getEnvironment().getMasterServices());
632
  }
640
  }
633

    
   
641

   
634

    
   
642

   
635
  /* ---- RegionObserver implementation ---- */
643
  /* ---- RegionObserver implementation ---- */
636

    
   
644

   
637
  @Override
645
  @Override
638
  public void postOpen(ObserverContext<RegionCoprocessorEnvironment> c) {
646
  public void postOpen(ObserverContext<RegionCoprocessorEnvironment> c) {
639
    RegionCoprocessorEnvironment e = c.getEnvironment();
647
    RegionCoprocessorEnvironment e = c.getEnvironment();
640
    final HRegion region = e.getRegion();
648
    final HRegion region = e.getRegion();
641
    if (region == null) {
649
    if (region == null) {
642
      LOG.error("NULL region from RegionCoprocessorEnvironment in postOpen()");
650
      LOG.error("NULL region from RegionCoprocessorEnvironment in postOpen()");
643
      return;
651
      return;
644
    }
652
    }
645

    
   
653

   
646
    try {
654
    try {
647
      this.authManager = TableAuthManager.get(
655
      this.authManager = TableAuthManager.get(
648
          e.getRegionServerServices().getZooKeeper(),
656
          e.getRegionServerServices().getZooKeeper(),
649
          e.getRegion().getConf());
657
          e.getRegion().getConf());
650
    } catch (IOException ioe) {
658
    } catch (IOException ioe) {
651
      // pass along as a RuntimeException, so that the coprocessor is unloaded
659
      // pass along as a RuntimeException, so that the coprocessor is unloaded
652
      throw new RuntimeException("Error obtaining TableAuthManager", ioe);
660
      throw new RuntimeException("Error obtaining TableAuthManager", ioe);
653
    }
661
    }
654

    
   
662

   
655
    if (AccessControlLists.isAclRegion(region)) {
663
    if (AccessControlLists.isAclRegion(region)) {
656
      aclRegion = true;
664
      aclRegion = true;
657
      try {
665
      try {
658
        initialize(e);
666
        initialize(e);
659
      } catch (IOException ex) {
667
      } catch (IOException ex) {
660
        // if we can't obtain permissions, it's better to fail
668
        // if we can't obtain permissions, it's better to fail
661
        // than perform checks incorrectly
669
        // than perform checks incorrectly
662
        throw new RuntimeException("Failed to initialize permissions cache", ex);
670
        throw new RuntimeException("Failed to initialize permissions cache", ex);
663
      }
671
      }
664
    }
672
    }
665
  }
673
  }
666

    
   
674

   
667
  @Override
675
  @Override
668
  public void preGetClosestRowBefore(final ObserverContext<RegionCoprocessorEnvironment> c,
676
  public void preGetClosestRowBefore(final ObserverContext<RegionCoprocessorEnvironment> c,
669
      final byte [] row, final byte [] family, final Result result)
677
      final byte [] row, final byte [] family, final Result result)
670
      throws IOException {
678
      throws IOException {
671
    requirePermission(TablePermission.Action.READ, c.getEnvironment(),
679
    requirePermission(TablePermission.Action.READ, c.getEnvironment(),
672
        (family != null ? Lists.newArrayList(family) : null));
680
        (family != null ? Lists.newArrayList(family) : null));
673
  }
681
  }
674

    
   
682

   
675
  @Override
683
  @Override
676
  public void preGet(final ObserverContext<RegionCoprocessorEnvironment> c,
684
  public void preGet(final ObserverContext<RegionCoprocessorEnvironment> c,
677
      final Get get, final List<KeyValue> result) throws IOException {
685
      final Get get, final List<KeyValue> result) throws IOException {
678
    /*
686
    /*
679
     if column family level checks fail, check for a qualifier level permission
687
     if column family level checks fail, check for a qualifier level permission
680
     in one of the families.  If it is present, then continue with the AccessControlFilter.
688
     in one of the families.  If it is present, then continue with the AccessControlFilter.
681
      */
689
      */
682
    RegionCoprocessorEnvironment e = c.getEnvironment();
690
    RegionCoprocessorEnvironment e = c.getEnvironment();
683
    User requestUser = getActiveUser();
691
    User requestUser = getActiveUser();
684
    AuthResult authResult = permissionGranted(requestUser,
692
    AuthResult authResult = permissionGranted(requestUser,
685
        TablePermission.Action.READ, e, get.getFamilyMap());
693
        TablePermission.Action.READ, e, get.getFamilyMap());
686
    if (!authResult.isAllowed()) {
694
    if (!authResult.isAllowed()) {
687
      if (hasFamilyQualifierPermission(requestUser,
695
      if (hasFamilyQualifierPermission(requestUser,
688
          TablePermission.Action.READ, e, get.getFamilyMap())) {
696
          TablePermission.Action.READ, e, get.getFamilyMap())) {
689
        byte[] table = getTableName(e);
697
        byte[] table = getTableName(e);
690
        AccessControlFilter filter = new AccessControlFilter(authManager,
698
        AccessControlFilter filter = new AccessControlFilter(authManager,
691
            requestUser, table);
699
            requestUser, table);
692

    
   
700

   
693
        // wrap any existing filter
701
        // wrap any existing filter
694
        if (get.getFilter() != null) {
702
        if (get.getFilter() != null) {
695
          FilterList wrapper = new FilterList(FilterList.Operator.MUST_PASS_ALL,
703
          FilterList wrapper = new FilterList(FilterList.Operator.MUST_PASS_ALL,
696
              Lists.newArrayList(filter, get.getFilter()));
704
              Lists.newArrayList(filter, get.getFilter()));
697
          get.setFilter(wrapper);
705
          get.setFilter(wrapper);
698
        } else {
706
        } else {
699
          get.setFilter(filter);
707
          get.setFilter(filter);
700
        }
708
        }
701
        logResult(AuthResult.allow("Access allowed with filter", requestUser,
709
        logResult(AuthResult.allow("Access allowed with filter", requestUser,
702
            TablePermission.Action.READ, authResult.table));
710
            TablePermission.Action.READ, authResult.table));
703
      } else {
711
      } else {
704
        logResult(authResult);
712
        logResult(authResult);
705
        throw new AccessDeniedException("Insufficient permissions (table=" +
713
        throw new AccessDeniedException("Insufficient permissions (table=" +
706
          e.getRegion().getTableDesc().getNameAsString() + ", action=READ)");
714
          e.getRegion().getTableDesc().getNameAsString() + ", action=READ)");
707
      }
715
      }
708
    } else {
716
    } else {
709
      // log auth success
717
      // log auth success
710
      logResult(authResult);
718
      logResult(authResult);
711
    }
719
    }
712
  }
720
  }
713

    
   
721

   
714
  @Override
722
  @Override
715
  public boolean preExists(final ObserverContext<RegionCoprocessorEnvironment> c,
723
  public boolean preExists(final ObserverContext<RegionCoprocessorEnvironment> c,
716
      final Get get, final boolean exists) throws IOException {
724
      final Get get, final boolean exists) throws IOException {
717
    requirePermission(TablePermission.Action.READ, c.getEnvironment(),
725
    requirePermission(TablePermission.Action.READ, c.getEnvironment(),
718
        get.familySet());
726
        get.familySet());
719
    return exists;
727
    return exists;
720
  }
728
  }
721

    
   
729

   
722
  @Override
730
  @Override
723
  public void prePut(final ObserverContext<RegionCoprocessorEnvironment> c,
731
  public void prePut(final ObserverContext<RegionCoprocessorEnvironment> c,
724
      final Put put, final WALEdit edit, final boolean writeToWAL)
732
      final Put put, final WALEdit edit, final boolean writeToWAL)
725
      throws IOException {
733
      throws IOException {
726
    requirePermission(TablePermission.Action.WRITE, c.getEnvironment(),
734
    requirePermission(TablePermission.Action.WRITE, c.getEnvironment(),
727
        put.getFamilyMap());
735
        put.getFamilyMap());
728
  }
736
  }
729

    
   
737

   
730
  @Override
738
  @Override
731
  public void postPut(final ObserverContext<RegionCoprocessorEnvironment> c,
739
  public void postPut(final ObserverContext<RegionCoprocessorEnvironment> c,
732
      final Put put, final WALEdit edit, final boolean writeToWAL) {
740
      final Put put, final WALEdit edit, final boolean writeToWAL) {
733
    if (aclRegion) {
741
    if (aclRegion) {
734
      updateACL(c.getEnvironment(), put.getFamilyMap());
742
      updateACL(c.getEnvironment(), put.getFamilyMap());
735
    }
743
    }
736
  }
744
  }
737

    
   
745

   
738
  @Override
746
  @Override
739
  public void preDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
747
  public void preDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
740
      final Delete delete, final WALEdit edit, final boolean writeToWAL)
748
      final Delete delete, final WALEdit edit, final boolean writeToWAL)
741
      throws IOException {
749
      throws IOException {
742
    requirePermission(TablePermission.Action.WRITE, c.getEnvironment(),
750
    requirePermission(TablePermission.Action.WRITE, c.getEnvironment(),
743
        delete.getFamilyMap());
751
        delete.getFamilyMap());
744
  }
752
  }
745

    
   
753

   
746
  @Override
754
  @Override
747
  public void postDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
755
  public void postDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
748
      final Delete delete, final WALEdit edit, final boolean writeToWAL)
756
      final Delete delete, final WALEdit edit, final boolean writeToWAL)
749
      throws IOException {
757
      throws IOException {
750
    if (aclRegion) {
758
    if (aclRegion) {
751
      updateACL(c.getEnvironment(), delete.getFamilyMap());
759
      updateACL(c.getEnvironment(), delete.getFamilyMap());
752
    }
760
    }
753
  }
761
  }
754

    
   
762

   
755
  @Override
763
  @Override
756
  public boolean preCheckAndPut(final ObserverContext<RegionCoprocessorEnvironment> c,
764
  public boolean preCheckAndPut(final ObserverContext<RegionCoprocessorEnvironment> c,
757
      final byte [] row, final byte [] family, final byte [] qualifier,
765
      final byte [] row, final byte [] family, final byte [] qualifier,
758
      final CompareFilter.CompareOp compareOp,
766
      final CompareFilter.CompareOp compareOp,
759
      final WritableByteArrayComparable comparator, final Put put,
767
      final WritableByteArrayComparable comparator, final Put put,
760
      final boolean result) throws IOException {
768
      final boolean result) throws IOException {
761
    requirePermission(TablePermission.Action.READ, c.getEnvironment(),
769
    requirePermission(TablePermission.Action.READ, c.getEnvironment(),
762
        Arrays.asList(new byte[][]{family}));
770
        Arrays.asList(new byte[][]{family}));
763
    return result;
771
    return result;
764
  }
772
  }
765

    
   
773

   
766
  @Override
774
  @Override
767
  public boolean preCheckAndDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
775
  public boolean preCheckAndDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
768
      final byte [] row, final byte [] family, final byte [] qualifier,
776
      final byte [] row, final byte [] family, final byte [] qualifier,
769
      final CompareFilter.CompareOp compareOp,
777
      final CompareFilter.CompareOp compareOp,
770
      final WritableByteArrayComparable comparator, final Delete delete,
778
      final WritableByteArrayComparable comparator, final Delete delete,
771
      final boolean result) throws IOException {
779
      final boolean result) throws IOException {
772
    requirePermission(TablePermission.Action.READ, c.getEnvironment(),
780
    requirePermission(TablePermission.Action.READ, c.getEnvironment(),
773
        Arrays.asList( new byte[][] {family}));
781
        Arrays.asList( new byte[][] {family}));
774
    return result;
782
    return result;
775
  }
783
  }
776

    
   
784

   
777
  @Override
785
  @Override
778
  public long preIncrementColumnValue(final ObserverContext<RegionCoprocessorEnvironment> c,
786
  public long preIncrementColumnValue(final ObserverContext<RegionCoprocessorEnvironment> c,
779
      final byte [] row, final byte [] family, final byte [] qualifier,
787
      final byte [] row, final byte [] family, final byte [] qualifier,
780
      final long amount, final boolean writeToWAL)
788
      final long amount, final boolean writeToWAL)
781
      throws IOException {
789
      throws IOException {
782
    requirePermission(TablePermission.Action.WRITE, c.getEnvironment(),
790
    requirePermission(TablePermission.Action.WRITE, c.getEnvironment(),
783
        Arrays.asList(new byte[][]{family}));
791
        Arrays.asList(new byte[][]{family}));
784
    return -1;
792
    return -1;
785
  }
793
  }
786

    
   
794

   
787
  @Override
795
  @Override
788
  public Result preIncrement(final ObserverContext<RegionCoprocessorEnvironment> c,
796
  public Result preIncrement(final ObserverContext<RegionCoprocessorEnvironment> c,
789
      final Increment increment)
797
      final Increment increment)
790
      throws IOException {
798
      throws IOException {
791
    requirePermission(TablePermission.Action.WRITE, c.getEnvironment(),
799
    requirePermission(TablePermission.Action.WRITE, c.getEnvironment(),
792
        increment.getFamilyMap().keySet());
800
        increment.getFamilyMap().keySet());
793
    return null;
801
    return null;
794
  }
802
  }
795

    
   
803

   
796
  @Override
804
  @Override
797
  public RegionScanner preScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
805
  public RegionScanner preScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
798
      final Scan scan, final RegionScanner s) throws IOException {
806
      final Scan scan, final RegionScanner s) throws IOException {
799
    /*
807
    /*
800
     if column family level checks fail, check for a qualifier level permission
808
     if column family level checks fail, check for a qualifier level permission
801
     in one of the families.  If it is present, then continue with the AccessControlFilter.
809
     in one of the families.  If it is present, then continue with the AccessControlFilter.
802
      */
810
      */
803
    RegionCoprocessorEnvironment e = c.getEnvironment();
811
    RegionCoprocessorEnvironment e = c.getEnvironment();
804
    User user = getActiveUser();
812
    User user = getActiveUser();
805
    AuthResult authResult = permissionGranted(user, TablePermission.Action.READ, e,
813
    AuthResult authResult = permissionGranted(user, TablePermission.Action.READ, e,
806
        scan.getFamilyMap());
814
        scan.getFamilyMap());
807
    if (!authResult.isAllowed()) {
815
    if (!authResult.isAllowed()) {
808
      if (hasFamilyQualifierPermission(user, TablePermission.Action.READ, e,
816
      if (hasFamilyQualifierPermission(user, TablePermission.Action.READ, e,
809
          scan.getFamilyMap())) {
817
          scan.getFamilyMap())) {
810
        byte[] table = getTableName(e);
818
        byte[] table = getTableName(e);
811
        AccessControlFilter filter = new AccessControlFilter(authManager,
819
        AccessControlFilter filter = new AccessControlFilter(authManager,
812
            user, table);
820
            user, table);
813

    
   
821

   
814
        // wrap any existing filter
822
        // wrap any existing filter
815
        if (scan.hasFilter()) {
823
        if (scan.hasFilter()) {
816
          FilterList wrapper = new FilterList(FilterList.Operator.MUST_PASS_ALL,
824
          FilterList wrapper = new FilterList(FilterList.Operator.MUST_PASS_ALL,
817
              Lists.newArrayList(filter, scan.getFilter()));
825
              Lists.newArrayList(filter, scan.getFilter()));
818
          scan.setFilter(wrapper);
826
          scan.setFilter(wrapper);
819
        } else {
827
        } else {
820
          scan.setFilter(filter);
828
          scan.setFilter(filter);
821
        }
829
        }
822
        logResult(AuthResult.allow("Access allowed with filter", user,
830
        logResult(AuthResult.allow("Access allowed with filter", user,
823
            TablePermission.Action.READ, authResult.table));
831
            TablePermission.Action.READ, authResult.table));
824
      } else {
832
      } else {
825
        // no table/family level perms and no qualifier level perms, reject
833
        // no table/family level perms and no qualifier level perms, reject
826
        logResult(authResult);
834
        logResult(authResult);
827
        throw new AccessDeniedException("Insufficient permissions for user '"+
835
        throw new AccessDeniedException("Insufficient permissions for user '"+
828
            (user != null ? user.getShortName() : "null")+"' "+
836
            (user != null ? user.getShortName() : "null")+"' "+
829
            "for scanner open on table " + Bytes.toString(getTableName(e)));
837
            "for scanner open on table " + Bytes.toString(getTableName(e)));
830
      }
838
      }
831
    } else {
839
    } else {
832
      // log success
840
      // log success
833
      logResult(authResult);
841
      logResult(authResult);
834
    }
842
    }
835
    return s;
843
    return s;
836
  }
844
  }
837

    
   
845

   
838
  @Override
846
  @Override
839
  public RegionScanner postScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
847
  public RegionScanner postScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
840
      final Scan scan, final RegionScanner s) throws IOException {
848
      final Scan scan, final RegionScanner s) throws IOException {
841
    User user = getActiveUser();
849
    User user = getActiveUser();
842
    if (user != null && user.getShortName() != null) {      // store reference to scanner owner for later checks
850
    if (user != null && user.getShortName() != null) {      // store reference to scanner owner for later checks
843
      scannerOwners.put(s, user.getShortName());
851
      scannerOwners.put(s, user.getShortName());
844
    }
852
    }
845
    return s;
853
    return s;
846
  }
854
  }
847

    
   
855

   
848
  @Override
856
  @Override
849
  public boolean preScannerNext(final ObserverContext<RegionCoprocessorEnvironment> c,
857
  public boolean preScannerNext(final ObserverContext<RegionCoprocessorEnvironment> c,
850
      final InternalScanner s, final List<Result> result,
858
      final InternalScanner s, final List<Result> result,
851
      final int limit, final boolean hasNext) throws IOException {
859
      final int limit, final boolean hasNext) throws IOException {
852
    requireScannerOwner(s);
860
    requireScannerOwner(s);
853
    return hasNext;
861
    return hasNext;
854
  }
862
  }
855

    
   
863

   
856
  @Override
864
  @Override
857
  public void preScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
865
  public void preScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
858
      final InternalScanner s) throws IOException {
866
      final InternalScanner s) throws IOException {
859
    requireScannerOwner(s);
867
    requireScannerOwner(s);
860
  }
868
  }
861

    
   
869

   
862
  @Override
870
  @Override
863
  public void postScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
871
  public void postScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
864
      final InternalScanner s) throws IOException {
872
      final InternalScanner s) throws IOException {
865
    // clean up any associated owner mapping
873
    // clean up any associated owner mapping
866
    scannerOwners.remove(s);
874
    scannerOwners.remove(s);
867
  }
875
  }
868

    
   
876

   
869
  /**
877
  /**
870
   * Verify, when servicing an RPC, that the caller is the scanner owner.
878
   * Verify, when servicing an RPC, that the caller is the scanner owner.
871
   * If so, we assume that access control is correctly enforced based on
879
   * If so, we assume that access control is correctly enforced based on
872
   * the checks performed in preScannerOpen()
880
   * the checks performed in preScannerOpen()
873
   */
881
   */
874
  private void requireScannerOwner(InternalScanner s)
882
  private void requireScannerOwner(InternalScanner s)
875
      throws AccessDeniedException {
883
      throws AccessDeniedException {
876
    if (RequestContext.isInRequestContext()) {
884
    if (RequestContext.isInRequestContext()) {
877
      String owner = scannerOwners.get(s);
885
      String owner = scannerOwners.get(s);
878
      if (owner != null && !owner.equals(RequestContext.getRequestUserName())) {
886
      if (owner != null && !owner.equals(RequestContext.getRequestUserName())) {
879
        throw new AccessDeniedException("User '"+
887
        throw new AccessDeniedException("User '"+
880
            RequestContext.getRequestUserName()+"' is not the scanner owner!");
888
            RequestContext.getRequestUserName()+"' is not the scanner owner!");
881
      }
889
      }
882
    }
890
    }
883
  }
891
  }
884

    
   
892

   
885
  /* ---- AccessControllerProtocol implementation ---- */
893
  /* ---- AccessControllerProtocol implementation ---- */
886
  /*
894
  /*
887
   * These methods are only allowed to be called against the _acl_ region(s).
895
   * These methods are only allowed to be called against the _acl_ region(s).
888
   * This will be restricted by both client side and endpoint implementations.
896
   * This will be restricted by both client side and endpoint implementations.
889
   */
897
   */
890
  @Override
898
  @Override
891
  public void grant(byte[] user, TablePermission permission)
899
  public void grant(byte[] user, TablePermission permission)
892
      throws IOException {
900
      throws IOException {
893
    // verify it's only running at .acl.
901
    // verify it's only running at .acl.
894
    if (aclRegion) {
902
    if (aclRegion) {
895
      if (LOG.isDebugEnabled()) {
903
      if (LOG.isDebugEnabled()) {
896
        LOG.debug("Received request to grant access permission to '"
904
        LOG.debug("Received request to grant access permission to '"
897
            + Bytes.toString(user) + "'. "
905
            + Bytes.toString(user) + "'. "
898
            + permission.toString());
906
            + permission.toString());
899
      }
907
      }
900

    
   
908

   
901
      requirePermission(Permission.Action.ADMIN);
909
      requirePermission(Permission.Action.ADMIN);
902

    
   
910

   
903
      AccessControlLists.addTablePermission(regionEnv.getConfiguration(),
911
      AccessControlLists.addTablePermission(regionEnv.getConfiguration(),
904
          permission.getTable(), Bytes.toString(user), permission);
912
          permission.getTable(), Bytes.toString(user), permission);
905
      if (AUDITLOG.isTraceEnabled()) {
913
      if (AUDITLOG.isTraceEnabled()) {
906
        // audit log should store permission changes in addition to auth results
914
        // audit log should store permission changes in addition to auth results
907
        AUDITLOG.trace("Granted user '" + Bytes.toString(user) + "' permission "
915
        AUDITLOG.trace("Granted user '" + Bytes.toString(user) + "' permission "
908
            + permission.toString());
916
            + permission.toString());
909
      }
917
      }
910
    } else {
918
    } else {
911
      throw new CoprocessorException(AccessController.class, "This method " +
919
      throw new CoprocessorException(AccessController.class, "This method " +
912
          "can only execute at " +
920
          "can only execute at " +
913
          Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
921
          Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
914
    }
922
    }
915
  }
923
  }
916

    
   
924

   
917
  @Override
925
  @Override
918
  public void revoke(byte[] user, TablePermission permission)
926
  public void revoke(byte[] user, TablePermission permission)
919
      throws IOException{
927
      throws IOException{
920
    // only allowed to be called on _acl_ region
928
    // only allowed to be called on _acl_ region
921
    if (aclRegion) {
929
    if (aclRegion) {
922
      if (LOG.isDebugEnabled()) {
930
      if (LOG.isDebugEnabled()) {
923
        LOG.debug("Received request to revoke access permission for '"
931
        LOG.debug("Received request to revoke access permission for '"
924
            + Bytes.toString(user) + "'. "
932
            + Bytes.toString(user) + "'. "
925
            + permission.toString());
933
            + permission.toString());
926
      }
934
      }
927

    
   
935

   
928
      requirePermission(Permission.Action.ADMIN);
936
      requirePermission(Permission.Action.ADMIN);
929

    
   
937

   
930
      AccessControlLists.removeTablePermission(regionEnv.getConfiguration(),
938
      AccessControlLists.removeTablePermission(regionEnv.getConfiguration(),
931
          permission.getTable(), Bytes.toString(user), permission);
939
          permission.getTable(), Bytes.toString(user), permission);
932
      if (AUDITLOG.isTraceEnabled()) {
940
      if (AUDITLOG.isTraceEnabled()) {
933
        // audit log should record all permission changes
941
        // audit log should record all permission changes
934
        AUDITLOG.trace("Revoked user '" + Bytes.toString(user) + "' permission "
942
        AUDITLOG.trace("Revoked user '" + Bytes.toString(user) + "' permission "
935
            + permission.toString());
943
            + permission.toString());
936
      }
944
      }
937
    } else {
945
    } else {
938
      throw new CoprocessorException(AccessController.class, "This method " +
946
      throw new CoprocessorException(AccessController.class, "This method " +
939
          "can only execute at " +
947
          "can only execute at " +
940
          Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
948
          Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
941
    }
949
    }
942
  }
950
  }
943

    
   
951

   
944
  @Override
952
  @Override
945
  public List<UserPermission> getUserPermissions(final byte[] tableName)
953
  public List<UserPermission> getUserPermissions(final byte[] tableName)
946
      throws IOException {
954
      throws IOException {
947
    // only allowed to be called on _acl_ region
955
    // only allowed to be called on _acl_ region
948
    if (aclRegion) {
956
    if (aclRegion) {
949
      requirePermission(Permission.Action.ADMIN);
957
      requirePermission(Permission.Action.ADMIN);
950

    
   
958

   
951
      List<UserPermission> perms = AccessControlLists.getUserPermissions
959
      List<UserPermission> perms = AccessControlLists.getUserPermissions
952
          (regionEnv.getConfiguration(), tableName);
960
          (regionEnv.getConfiguration(), tableName);
953
      return perms;
961
      return perms;
954
    } else {
962
    } else {
955
      throw new CoprocessorException(AccessController.class, "This method " +
963
      throw new CoprocessorException(AccessController.class, "This method " +
956
          "can only execute at " +
964
          "can only execute at " +
957
          Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
965
          Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
958
    }
966
    }
959
  }
967
  }
960

    
   
968

   
961
  @Override
969
  @Override

    
   
970
  public void checkPermissions(Permission[] permissions) throws IOException {

    
   
971
    // TODO: there is space to do some optimization for merging permission families/columns

    
   
972
    for (Permission permission : permissions) {

    
   
973
      if (permission instanceof TablePermission) {

    
   
974
        TablePermission tperm = (TablePermission) permission;

    
   
975
        for (Permission.Action action : permission.getActions()) {

    
   
976
          byte[] tableName = regionEnv.getRegion().getTableDesc().getName();

    
   
977
          if (!Arrays.equals(tperm.getTable(), tableName)) {

    
   
978
            throw new CoprocessorException(AccessController.class, "This method "

    
   
979
                + "can only execute at the table specified in TablePermission.");

    
   
980
          }

    
   
981

   

    
   
982
          HashMap<byte[], Set<byte[]>> familyMap = Maps.newHashMapWithExpectedSize(1);

    
   
983
          if (tperm.getFamily() != null) {

    
   
984
            if (tperm.getQualifier() != null) {

    
   
985
              familyMap.put(tperm.getFamily(), Sets.newHashSet(tperm.getQualifier()));

    
   
986
            } else {

    
   
987
              familyMap.put(tperm.getFamily(), null);

    
   
988
            }

    
   
989
          }

    
   
990

   

    
   
991
          requirePermission(action, regionEnv, familyMap);

    
   
992
        }

    
   
993

   

    
   
994
      } else {

    
   
995
        for (Permission.Action action : permission.getActions()) {

    
   
996
          requirePermission(action);

    
   
997
        }

    
   
998
      }

    
   
999
    }

    
   
1000
  }

    
   
1001

   

    
   
1002
  @Override
962
  public long getProtocolVersion(String protocol, long clientVersion) throws IOException {
1003
  public long getProtocolVersion(String protocol, long clientVersion) throws IOException {
963
    return PROTOCOL_VERSION;
1004
    return PROTOCOL_VERSION;
964
  }
1005
  }
965

    
   
1006

   
966
  @Override
1007
  @Override
967
  public ProtocolSignature getProtocolSignature(String protocol,
1008
  public ProtocolSignature getProtocolSignature(String protocol,
968
      long clientVersion, int clientMethodsHash) throws IOException {
1009
      long clientVersion, int clientMethodsHash) throws IOException {
969
    if (AccessControllerProtocol.class.getName().equals(protocol)) {
1010
    if (AccessControllerProtocol.class.getName().equals(protocol)) {
970
      return new ProtocolSignature(PROTOCOL_VERSION, null);
1011
      return new ProtocolSignature(PROTOCOL_VERSION, null);
971
    }
1012
    }
972
    throw new HBaseRPC.UnknownProtocolException(
1013
    throw new HBaseRPC.UnknownProtocolException(
973
        "Unexpected protocol requested: "+protocol);
1014
        "Unexpected protocol requested: "+protocol);
974
  }
1015
  }
975

    
   
1016

   
976
  private byte[] getTableName(RegionCoprocessorEnvironment e) {
1017
  private byte[] getTableName(RegionCoprocessorEnvironment e) {
977
    HRegion region = e.getRegion();
1018
    HRegion region = e.getRegion();
978
    byte[] tableName = null;
1019
    byte[] tableName = null;
979

    
   
1020

   
980
    if (region != null) {
1021
    if (region != null) {
981
      HRegionInfo regionInfo = region.getRegionInfo();
1022
      HRegionInfo regionInfo = region.getRegionInfo();
982
      if (regionInfo != null) {
1023
      if (regionInfo != null) {
983
        tableName = regionInfo.getTableName();
1024
        tableName = regionInfo.getTableName();
984
      }
1025
      }
985
    }
1026
    }
986
    return tableName;
1027
    return tableName;
987
  }
1028
  }
988
}
1029
}
security/src/main/java/org/apache/hadoop/hbase/security/access/AccessControllerProtocol.java
Revision 5fa2edb New Change
 
security/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java
Revision f864373 New Change
 
  1. security/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java: Loading...
  2. security/src/main/java/org/apache/hadoop/hbase/security/access/AccessControllerProtocol.java: Loading...
  3. security/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java: Loading...