You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ga...@apache.org on 2011/11/19 01:25:38 UTC

svn commit: r1203915 [3/3] - in /hbase/branches/0.92: ./ security/src/main/java/org/apache/hadoop/hbase/security/access/ security/src/test/java/org/apache/hadoop/hbase/security/access/ src/main/java/org/apache/hadoop/hbase/ src/main/java/org/apache/had...

Added: hbase/branches/0.92/security/src/test/java/org/apache/hadoop/hbase/security/access/TestTablePermissions.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/security/src/test/java/org/apache/hadoop/hbase/security/access/TestTablePermissions.java?rev=1203915&view=auto
==============================================================================
--- hbase/branches/0.92/security/src/test/java/org/apache/hadoop/hbase/security/access/TestTablePermissions.java (added)
+++ hbase/branches/0.92/security/src/test/java/org/apache/hadoop/hbase/security/access/TestTablePermissions.java Sat Nov 19 00:25:37 2011
@@ -0,0 +1,305 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.security.access;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.Abortable;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.*;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test the reading and writing of access permissions on {@code _acl_} table.
+ */
+public class TestTablePermissions {
+  private static final Log LOG = LogFactory.getLog(TestTablePermissions.class);
+  private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
+  private static ZooKeeperWatcher ZKW;
+  private final static Abortable ABORTABLE = new Abortable() {
+    private final AtomicBoolean abort = new AtomicBoolean(false);
+
+    @Override
+    public void abort(String why, Throwable e) {
+      LOG.info(why, e);
+      abort.set(true);
+    }
+
+    @Override
+    public boolean isAborted() {
+      return abort.get();
+    }
+  };
+
+  private static byte[] TEST_TABLE = Bytes.toBytes("perms_test");
+  private static byte[] TEST_TABLE2 = Bytes.toBytes("perms_test2");
+  private static byte[] TEST_FAMILY = Bytes.toBytes("f1");
+  private static byte[] TEST_QUALIFIER = Bytes.toBytes("col1");
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    // setup configuration
+    Configuration conf = UTIL.getConfiguration();
+    SecureTestUtil.enableSecurity(conf);
+
+    UTIL.startMiniCluster();
+    ZKW = new ZooKeeperWatcher(UTIL.getConfiguration(),
+      "TestTablePermissions", ABORTABLE);
+
+    UTIL.createTable(TEST_TABLE, TEST_FAMILY);
+    UTIL.createTable(TEST_TABLE2, TEST_FAMILY);
+  }
+
+  @AfterClass
+  public static void afterClass() throws Exception {
+    UTIL.shutdownMiniCluster();
+  }
+
+  @Test
+  public void testBasicWrite() throws Exception {
+    Configuration conf = UTIL.getConfiguration();
+    // add some permissions
+    AccessControlLists.addTablePermission(conf, TEST_TABLE,
+        "george", new TablePermission(TEST_TABLE, null,
+            TablePermission.Action.READ, TablePermission.Action.WRITE));
+    AccessControlLists.addTablePermission(conf, TEST_TABLE,
+        "hubert", new TablePermission(TEST_TABLE, null,
+            TablePermission.Action.READ));
+    AccessControlLists.addTablePermission(conf, TEST_TABLE,
+        "humphrey", new TablePermission(TEST_TABLE, TEST_FAMILY, TEST_QUALIFIER,
+            TablePermission.Action.READ));
+
+    // retrieve the same
+    ListMultimap<String,TablePermission> perms =
+        AccessControlLists.getTablePermissions(conf, TEST_TABLE);
+    List<TablePermission> userPerms = perms.get("george");
+    assertNotNull("Should have permissions for george", userPerms);
+    assertEquals("Should have 1 permission for george", 1, userPerms.size());
+    TablePermission permission = userPerms.get(0);
+    assertTrue("Permission should be for " + TEST_TABLE,
+        Bytes.equals(TEST_TABLE, permission.getTable()));
+    assertNull("Column family should be empty", permission.getFamily());
+
+    // check actions
+    assertNotNull(permission.getActions());
+    assertEquals(2, permission.getActions().length);
+    List<TablePermission.Action> actions = Arrays.asList(permission.getActions());
+    assertTrue(actions.contains(TablePermission.Action.READ));
+    assertTrue(actions.contains(TablePermission.Action.WRITE));
+
+    userPerms = perms.get("hubert");
+    assertNotNull("Should have permissions for hubert", userPerms);
+    assertEquals("Should have 1 permission for hubert", 1, userPerms.size());
+    permission = userPerms.get(0);
+    assertTrue("Permission should be for " + TEST_TABLE,
+        Bytes.equals(TEST_TABLE, permission.getTable()));
+    assertNull("Column family should be empty", permission.getFamily());
+
+    // check actions
+    assertNotNull(permission.getActions());
+    assertEquals(1, permission.getActions().length);
+    actions = Arrays.asList(permission.getActions());
+    assertTrue(actions.contains(TablePermission.Action.READ));
+    assertFalse(actions.contains(TablePermission.Action.WRITE));
+
+    userPerms = perms.get("humphrey");
+    assertNotNull("Should have permissions for humphrey", userPerms);
+    assertEquals("Should have 1 permission for humphrey", 1, userPerms.size());
+    permission = userPerms.get(0);
+    assertTrue("Permission should be for " + TEST_TABLE,
+        Bytes.equals(TEST_TABLE, permission.getTable()));
+    assertTrue("Permission should be for family " + TEST_FAMILY,
+        Bytes.equals(TEST_FAMILY, permission.getFamily()));
+    assertTrue("Permission should be for qualifier " + TEST_QUALIFIER,
+        Bytes.equals(TEST_QUALIFIER, permission.getQualifier()));
+
+    // check actions
+    assertNotNull(permission.getActions());
+    assertEquals(1, permission.getActions().length);
+    actions = Arrays.asList(permission.getActions());
+    assertTrue(actions.contains(TablePermission.Action.READ));
+    assertFalse(actions.contains(TablePermission.Action.WRITE));
+
+    // table 2 permissions
+    AccessControlLists.addTablePermission(conf, TEST_TABLE2, "hubert",
+        new TablePermission(TEST_TABLE2, null,
+            TablePermission.Action.READ, TablePermission.Action.WRITE));
+
+    // check full load
+    Map<byte[],ListMultimap<String,TablePermission>> allPerms =
+        AccessControlLists.loadAll(conf);
+    assertEquals("Full permission map should have entries for both test tables",
+        2, allPerms.size());
+
+    userPerms = allPerms.get(TEST_TABLE).get("hubert");
+    assertNotNull(userPerms);
+    assertEquals(1, userPerms.size());
+    permission = userPerms.get(0);
+    assertTrue(Bytes.equals(TEST_TABLE, permission.getTable()));
+    assertEquals(1, permission.getActions().length);
+    assertEquals(TablePermission.Action.READ, permission.getActions()[0]);
+
+    userPerms = allPerms.get(TEST_TABLE2).get("hubert");
+    assertNotNull(userPerms);
+    assertEquals(1, userPerms.size());
+    permission = userPerms.get(0);
+    assertTrue(Bytes.equals(TEST_TABLE2, permission.getTable()));
+    assertEquals(2, permission.getActions().length);
+    actions = Arrays.asList(permission.getActions());
+    assertTrue(actions.contains(TablePermission.Action.READ));
+    assertTrue(actions.contains(TablePermission.Action.WRITE));
+  }
+
+  @Test
+  public void testPersistence() throws Exception {
+    Configuration conf = UTIL.getConfiguration();
+    AccessControlLists.addTablePermission(conf, TEST_TABLE, "albert",
+        new TablePermission(TEST_TABLE, null, TablePermission.Action.READ));
+    AccessControlLists.addTablePermission(conf, TEST_TABLE, "betty",
+        new TablePermission(TEST_TABLE, null, TablePermission.Action.READ,
+            TablePermission.Action.WRITE));
+    AccessControlLists.addTablePermission(conf, TEST_TABLE, "clark",
+        new TablePermission(TEST_TABLE, TEST_FAMILY, TablePermission.Action.READ));
+    AccessControlLists.addTablePermission(conf, TEST_TABLE, "dwight",
+        new TablePermission(TEST_TABLE, TEST_FAMILY, TEST_QUALIFIER,
+            TablePermission.Action.WRITE));
+
+    // verify permissions survive changes in table metadata
+    ListMultimap<String,TablePermission> preperms =
+        AccessControlLists.getTablePermissions(conf, TEST_TABLE);
+
+    HTable table = new HTable(conf, TEST_TABLE);
+    table.put(new Put(Bytes.toBytes("row1"))
+        .add(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes("v1")));
+    table.put(new Put(Bytes.toBytes("row2"))
+        .add(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes("v2")));
+    HBaseAdmin admin = UTIL.getHBaseAdmin();
+    admin.split(TEST_TABLE);
+
+    // wait for split
+    Thread.sleep(10000);
+
+    ListMultimap<String,TablePermission> postperms =
+        AccessControlLists.getTablePermissions(conf, TEST_TABLE);
+
+    checkMultimapEqual(preperms, postperms);
+  }
+
+  @Test
+  public void testSerialization() throws Exception {
+    Configuration conf = UTIL.getConfiguration();
+    ListMultimap<String,TablePermission> permissions = ArrayListMultimap.create();
+    permissions.put("george", new TablePermission(TEST_TABLE, null,
+        TablePermission.Action.READ));
+    permissions.put("george", new TablePermission(TEST_TABLE, TEST_FAMILY,
+        TablePermission.Action.WRITE));
+    permissions.put("george", new TablePermission(TEST_TABLE2, null,
+        TablePermission.Action.READ));
+    permissions.put("hubert", new TablePermission(TEST_TABLE2, null,
+        TablePermission.Action.READ, TablePermission.Action.WRITE));
+
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    AccessControlLists.writePermissions(new DataOutputStream(bos),
+        permissions, conf);
+
+    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+    ListMultimap<String,TablePermission> copy =
+        AccessControlLists.readPermissions(new DataInputStream(bis), conf);
+
+    checkMultimapEqual(permissions, copy);
+  }
+
+  public void checkMultimapEqual(ListMultimap<String,TablePermission> first,
+      ListMultimap<String,TablePermission> second) {
+    assertEquals(first.size(), second.size());
+    for (String key : first.keySet()) {
+      List<TablePermission> firstPerms = first.get(key);
+      List<TablePermission> secondPerms = second.get(key);
+      assertNotNull(secondPerms);
+      assertEquals(firstPerms.size(), secondPerms.size());
+      LOG.info("First permissions: "+firstPerms.toString());
+      LOG.info("Second permissions: "+secondPerms.toString());
+      for (TablePermission p : firstPerms) {
+        assertTrue("Permission "+p.toString()+" not found", secondPerms.contains(p));
+      }
+    }
+  }
+
+  @Test
+  public void testEquals() throws Exception {
+    TablePermission p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ);
+    TablePermission p2 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ);
+    assertTrue(p1.equals(p2));
+    assertTrue(p2.equals(p1));
+
+    p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ, TablePermission.Action.WRITE);
+    p2 = new TablePermission(TEST_TABLE, null, TablePermission.Action.WRITE, TablePermission.Action.READ);
+    assertTrue(p1.equals(p2));
+    assertTrue(p2.equals(p1));
+
+    p1 = new TablePermission(TEST_TABLE, TEST_FAMILY, TablePermission.Action.READ, TablePermission.Action.WRITE);
+    p2 = new TablePermission(TEST_TABLE, TEST_FAMILY, TablePermission.Action.WRITE, TablePermission.Action.READ);
+    assertTrue(p1.equals(p2));
+    assertTrue(p2.equals(p1));
+
+    p1 = new TablePermission(TEST_TABLE, TEST_FAMILY, TEST_QUALIFIER, TablePermission.Action.READ, TablePermission.Action.WRITE);
+    p2 = new TablePermission(TEST_TABLE, TEST_FAMILY, TEST_QUALIFIER, TablePermission.Action.WRITE, TablePermission.Action.READ);
+    assertTrue(p1.equals(p2));
+    assertTrue(p2.equals(p1));
+
+    p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ);
+    p2 = new TablePermission(TEST_TABLE, TEST_FAMILY, TablePermission.Action.READ);
+    assertFalse(p1.equals(p2));
+    assertFalse(p2.equals(p1));
+
+    p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ);
+    p2 = new TablePermission(TEST_TABLE, null, TablePermission.Action.WRITE);
+    assertFalse(p1.equals(p2));
+    assertFalse(p2.equals(p1));
+    p2 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ, TablePermission.Action.WRITE);
+    assertFalse(p1.equals(p2));
+    assertFalse(p2.equals(p1));
+
+    p1 = new TablePermission(TEST_TABLE, null, TablePermission.Action.READ);
+    p2 = new TablePermission(TEST_TABLE2, null, TablePermission.Action.READ);
+    assertFalse(p1.equals(p2));
+    assertFalse(p2.equals(p1));
+
+    p2 = new TablePermission(TEST_TABLE, null);
+    assertFalse(p1.equals(p2));
+    assertFalse(p2.equals(p1));
+  }
+}

Added: hbase/branches/0.92/security/src/test/java/org/apache/hadoop/hbase/security/access/TestZKPermissionsWatcher.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/security/src/test/java/org/apache/hadoop/hbase/security/access/TestZKPermissionsWatcher.java?rev=1203915&view=auto
==============================================================================
--- hbase/branches/0.92/security/src/test/java/org/apache/hadoop/hbase/security/access/TestZKPermissionsWatcher.java (added)
+++ hbase/branches/0.92/security/src/test/java/org/apache/hadoop/hbase/security/access/TestZKPermissionsWatcher.java Sat Nov 19 00:25:37 2011
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.security.access;
+
+import static org.junit.Assert.*;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.Abortable;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Test the reading and writing of access permissions to and from zookeeper.
+ */
+public class TestZKPermissionsWatcher {
+  private static final Log LOG = LogFactory.getLog(TestZKPermissionsWatcher.class);
+  private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
+  private static TableAuthManager AUTH_A;
+  private static TableAuthManager AUTH_B;
+  private final static Abortable ABORTABLE = new Abortable() {
+    private final AtomicBoolean abort = new AtomicBoolean(false);
+
+    @Override
+    public void abort(String why, Throwable e) {
+      LOG.info(why, e);
+      abort.set(true);
+    }
+
+    @Override
+    public boolean isAborted() {
+      return abort.get();
+    }
+  };
+
+  private static byte[] TEST_TABLE = Bytes.toBytes("perms_test");
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    // setup configuration
+    Configuration conf = UTIL.getConfiguration();
+    SecureTestUtil.enableSecurity(conf);
+
+    // start minicluster
+    UTIL.startMiniCluster();
+    AUTH_A = TableAuthManager.get(new ZooKeeperWatcher(conf,
+      "TestZKPermissionsWatcher_1", ABORTABLE), conf);
+    AUTH_B = TableAuthManager.get(new ZooKeeperWatcher(conf,
+      "TestZKPermissionsWatcher_2", ABORTABLE), conf);
+  }
+
+  @AfterClass
+  public static void afterClass() throws Exception {
+    UTIL.shutdownMiniCluster();
+  }
+
+  @Test
+  public void testPermissionsWatcher() throws Exception {
+    assertFalse(AUTH_A.authorizeUser("george", TEST_TABLE, null,
+      TablePermission.Action.READ));
+    assertFalse(AUTH_A.authorizeUser("george", TEST_TABLE, null,
+      TablePermission.Action.WRITE));
+    assertFalse(AUTH_A.authorizeUser("hubert", TEST_TABLE, null,
+      TablePermission.Action.READ));
+    assertFalse(AUTH_A.authorizeUser("hubert", TEST_TABLE, null,
+      TablePermission.Action.WRITE));
+
+    assertFalse(AUTH_B.authorizeUser("george", TEST_TABLE, null,
+      TablePermission.Action.READ));
+    assertFalse(AUTH_B.authorizeUser("george", TEST_TABLE, null,
+      TablePermission.Action.WRITE));
+    assertFalse(AUTH_B.authorizeUser("hubert", TEST_TABLE, null,
+      TablePermission.Action.READ));
+    assertFalse(AUTH_B.authorizeUser("hubert", TEST_TABLE, null,
+      TablePermission.Action.WRITE));
+
+    // update ACL: george RW
+    List<TablePermission> acl = new ArrayList<TablePermission>();
+    acl.add(new TablePermission(TEST_TABLE, null, TablePermission.Action.READ,
+      TablePermission.Action.WRITE));
+    AUTH_A.setUserPermissions("george", TEST_TABLE, acl);
+    Thread.sleep(100);
+
+    // check it
+    assertTrue(AUTH_A.authorizeUser("george", TEST_TABLE, null,
+      TablePermission.Action.READ));
+    assertTrue(AUTH_A.authorizeUser("george", TEST_TABLE, null,
+      TablePermission.Action.WRITE));
+    assertTrue(AUTH_B.authorizeUser("george", TEST_TABLE, null,
+      TablePermission.Action.READ));
+    assertTrue(AUTH_B.authorizeUser("george", TEST_TABLE, null,
+      TablePermission.Action.WRITE));
+    assertFalse(AUTH_A.authorizeUser("hubert", TEST_TABLE, null,
+      TablePermission.Action.READ));
+    assertFalse(AUTH_A.authorizeUser("hubert", TEST_TABLE, null,
+      TablePermission.Action.WRITE));
+    assertFalse(AUTH_B.authorizeUser("hubert", TEST_TABLE, null,
+      TablePermission.Action.READ));
+    assertFalse(AUTH_B.authorizeUser("hubert", TEST_TABLE, null,
+      TablePermission.Action.WRITE));
+
+    // update ACL: hubert R
+    acl = new ArrayList<TablePermission>();
+    acl.add(new TablePermission(TEST_TABLE, null, TablePermission.Action.READ));
+    AUTH_B.setUserPermissions("hubert", TEST_TABLE, acl);
+    Thread.sleep(100);
+
+    // check it
+    assertTrue(AUTH_A.authorizeUser("george", TEST_TABLE, null,
+      TablePermission.Action.READ));
+    assertTrue(AUTH_A.authorizeUser("george", TEST_TABLE, null,
+      TablePermission.Action.WRITE));
+    assertTrue(AUTH_B.authorizeUser("george", TEST_TABLE, null,
+      TablePermission.Action.READ));
+    assertTrue(AUTH_B.authorizeUser("george", TEST_TABLE, null,
+      TablePermission.Action.WRITE));
+    assertTrue(AUTH_A.authorizeUser("hubert", TEST_TABLE, null,
+      TablePermission.Action.READ));
+    assertFalse(AUTH_A.authorizeUser("hubert", TEST_TABLE, null,
+      TablePermission.Action.WRITE));
+    assertTrue(AUTH_B.authorizeUser("hubert", TEST_TABLE, null,
+      TablePermission.Action.READ));
+    assertFalse(AUTH_B.authorizeUser("hubert", TEST_TABLE, null,
+      TablePermission.Action.WRITE));
+  }
+}

Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java?rev=1203915&r1=1203914&r2=1203915&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java Sat Nov 19 00:25:37 2011
@@ -35,6 +35,7 @@ import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.hbase.io.hfile.Compression;
 import org.apache.hadoop.hbase.regionserver.StoreFile;
+import org.apache.hadoop.hbase.security.User;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.io.WritableComparable;
 
@@ -80,7 +81,11 @@ public class HTableDescriptor implements
   public static final String MAX_FILESIZE = "MAX_FILESIZE";
   private static final ImmutableBytesWritable MAX_FILESIZE_KEY =
     new ImmutableBytesWritable(Bytes.toBytes(MAX_FILESIZE));
-  
+
+  public static final String OWNER = "OWNER";
+  public static final ImmutableBytesWritable OWNER_KEY =
+    new ImmutableBytesWritable(Bytes.toBytes(OWNER));
+
   /**
    * <em>INTERNAL</em> Used by rest interface to access this metadata 
    * attribute which denotes if the table is Read Only
@@ -90,7 +95,7 @@ public class HTableDescriptor implements
   public static final String READONLY = "READONLY";
   private static final ImmutableBytesWritable READONLY_KEY =
     new ImmutableBytesWritable(Bytes.toBytes(READONLY));
-  
+
   /**
    * <em>INTERNAL</em> Used by HBase Shell interface to access this metadata 
    * attribute which represents the maximum size of the memstore after which 
@@ -101,7 +106,7 @@ public class HTableDescriptor implements
   public static final String MEMSTORE_FLUSHSIZE = "MEMSTORE_FLUSHSIZE";
   private static final ImmutableBytesWritable MEMSTORE_FLUSHSIZE_KEY =
     new ImmutableBytesWritable(Bytes.toBytes(MEMSTORE_FLUSHSIZE));
-  
+
   /**
    * <em>INTERNAL</em> Used by rest interface to access this metadata 
    * attribute which denotes if the table is a -ROOT- region or not
@@ -111,7 +116,7 @@ public class HTableDescriptor implements
   public static final String IS_ROOT = "IS_ROOT";
   private static final ImmutableBytesWritable IS_ROOT_KEY =
     new ImmutableBytesWritable(Bytes.toBytes(IS_ROOT));
-  
+
   /**
    * <em>INTERNAL</em> Used by rest interface to access this metadata 
    * attribute which denotes if it is a catalog table, either
@@ -1064,7 +1069,7 @@ public class HTableDescriptor implements
   public static final HTableDescriptor ROOT_TABLEDESC = new HTableDescriptor(
       HConstants.ROOT_TABLE_NAME,
       new HColumnDescriptor[] { new HColumnDescriptor(HConstants.CATALOG_FAMILY,
-          10,  // Ten is arbitrary number.  Keep versions to help debuggging.
+          10,  // Ten is arbitrary number.  Keep versions to help debugging.
           Compression.Algorithm.NONE.getName(), true, true, 8 * 1024,
           HConstants.FOREVER, StoreFile.BloomType.NONE.toString(),  
           HConstants.REPLICATION_SCOPE_LOCAL) });
@@ -1073,8 +1078,31 @@ public class HTableDescriptor implements
   public static final HTableDescriptor META_TABLEDESC = new HTableDescriptor(
       HConstants.META_TABLE_NAME, new HColumnDescriptor[] {
           new HColumnDescriptor(HConstants.CATALOG_FAMILY,
-            10, // Ten is arbitrary number.  Keep versions to help debuggging.
+            10, // Ten is arbitrary number.  Keep versions to help debugging.
             Compression.Algorithm.NONE.getName(), true, true, 8 * 1024,
             HConstants.FOREVER, StoreFile.BloomType.NONE.toString(),
             HConstants.REPLICATION_SCOPE_LOCAL)});
+
+
+  public void setOwner(User owner) {
+    setOwnerString(owner != null ? owner.getShortName() : null);
+  }
+
+  // used by admin.rb:alter(table_name,*args) to update owner.
+  public void setOwnerString(String ownerString) {
+    if (ownerString != null) {
+      setValue(OWNER_KEY, Bytes.toBytes(ownerString));
+    } else {
+      values.remove(OWNER_KEY);
+    }
+  }
+
+  public String getOwnerString() {
+    if (getValue(OWNER_KEY) != null) {
+      return Bytes.toString(getValue(OWNER_KEY));
+    }
+    // Note that every table should have an owner (i.e. should have OWNER_KEY set).
+    // .META. and -ROOT- should return system user as owner, not null (see MasterFileSystem.java:bootstrap()).
+    return null;
+  }
 }

Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java?rev=1203915&r1=1203914&r2=1203915&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java Sat Nov 19 00:25:37 2011
@@ -48,10 +48,10 @@ import java.io.IOException;
  */
 public abstract class BaseRegionObserver implements RegionObserver {
   @Override
-  public void start(CoprocessorEnvironment e) { }
+  public void start(CoprocessorEnvironment e) throws IOException { }
 
   @Override
-  public void stop(CoprocessorEnvironment e) { }
+  public void stop(CoprocessorEnvironment e) throws IOException { }
 
   @Override
   public void preOpen(ObserverContext<RegionCoprocessorEnvironment> e) { }

Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java?rev=1203915&r1=1203914&r2=1203915&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java Sat Nov 19 00:25:37 2011
@@ -381,6 +381,9 @@ public class ZKUtil {
     public String toString() {
       return node + " (" + RegionTransitionData.fromBytes(data) + ")";
     }
+    public boolean isEmpty() {
+      return (data.length == 0);
+    }
   }
 
   /**

Modified: hbase/branches/0.92/src/main/resources/hbase-default.xml
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/resources/hbase-default.xml?rev=1203915&r1=1203914&r2=1203915&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/resources/hbase-default.xml (original)
+++ hbase/branches/0.92/src/main/resources/hbase-default.xml Sat Nov 19 00:25:37 2011
@@ -622,6 +622,12 @@
   </property>
 
   <property>
+    <name>zookeeper.znode.acl.parent</name>
+    <value>acl</value>
+    <description>Root ZNode for access control lists.</description>
+  </property>
+  
+  <property>
     <name>hbase.coprocessor.region.classes</name>
     <value></value>
     <description>A comma-separated list of Coprocessors that are loaded by

Modified: hbase/branches/0.92/src/main/ruby/hbase.rb
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/ruby/hbase.rb?rev=1203915&r1=1203914&r2=1203915&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/ruby/hbase.rb (original)
+++ hbase/branches/0.92/src/main/ruby/hbase.rb Sat Nov 19 00:25:37 2011
@@ -73,3 +73,4 @@ require 'hbase/hbase'
 require 'hbase/admin'
 require 'hbase/table'
 require 'hbase/replication_admin'
+require 'hbase/security'

Modified: hbase/branches/0.92/src/main/ruby/hbase/admin.rb
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/ruby/hbase/admin.rb?rev=1203915&r1=1203914&r2=1203915&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/ruby/hbase/admin.rb (original)
+++ hbase/branches/0.92/src/main/ruby/hbase/admin.rb Sat Nov 19 00:25:37 2011
@@ -281,7 +281,6 @@ module Hbase
       @admin.createTable(table_description)
     end
 
-    #----------------------------------------------------------------------------------------------
     # Check the status of alter command (number of regions reopened)
     def alter_status(table_name)
       # Table name should be a string
@@ -328,7 +327,17 @@ module Hbase
 
         # No method parameter, try to use the args as a column definition
         unless method = arg.delete(METHOD)
+          # Note that we handle owner here, and also below (see (2)) as part of the "METHOD => 'table_att'" table attributes.
+          # In other words, if OWNER is specified, then METHOD is set to table_att.
+          #   alter 'tablename', {OWNER => 'username'} (that is, METHOD => 'table_att' is not specified).
+          if arg[OWNER]
+            htd.setOwnerString(arg[OWNER])
+            @admin.modifyTable(table_name.to_java_bytes, htd)
+            return
+          end
+
           descriptor = hcd(arg, htd)
+
           if arg[COMPRESSION_COMPACT]
             descriptor.setValue(COMPRESSION_COMPACT, arg[COMPRESSION_COMPACT])
           end
@@ -368,6 +377,8 @@ module Hbase
           htd.setReadOnly(JBoolean.valueOf(arg[READONLY])) if arg[READONLY]
           htd.setMemStoreFlushSize(JLong.valueOf(arg[MEMSTORE_FLUSHSIZE])) if arg[MEMSTORE_FLUSHSIZE]
           htd.setDeferredLogFlush(JBoolean.valueOf(arg[DEFERRED_LOG_FLUSH])) if arg[DEFERRED_LOG_FLUSH]
+          # (2) Here, we handle the alternate syntax of ownership setting, where method => 'table_att' is specified.
+          htd.setOwnerString(arg[OWNER]) if arg[OWNER]
 
           # set a coprocessor attribute
           if arg.kind_of?(Hash)

Modified: hbase/branches/0.92/src/main/ruby/hbase/hbase.rb
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/ruby/hbase/hbase.rb?rev=1203915&r1=1203914&r2=1203915&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/ruby/hbase/hbase.rb (original)
+++ hbase/branches/0.92/src/main/ruby/hbase/hbase.rb Sat Nov 19 00:25:37 2011
@@ -22,6 +22,7 @@ include Java
 
 require 'hbase/admin'
 require 'hbase/table'
+require 'hbase/security'
 
 module Hbase
   class Hbase
@@ -52,5 +53,8 @@ module Hbase
       ::Hbase::RepAdmin.new(configuration, formatter)
     end
 
+    def security_admin(formatter)
+      ::Hbase::SecurityAdmin.new(configuration, formatter)
+    end
   end
 end

Added: hbase/branches/0.92/src/main/ruby/hbase/security.rb
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/ruby/hbase/security.rb?rev=1203915&view=auto
==============================================================================
--- hbase/branches/0.92/src/main/ruby/hbase/security.rb (added)
+++ hbase/branches/0.92/src/main/ruby/hbase/security.rb Sat Nov 19 00:25:37 2011
@@ -0,0 +1,128 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+include Java
+
+# Wrapper for org.apache.hadoop.hbase.client.HBaseAdmin
+
+module Hbase
+  class SecurityAdmin
+    include HBaseConstants
+
+    def initialize(configuration, formatter)
+      @config = configuration
+      @admin = org.apache.hadoop.hbase.client.HBaseAdmin.new(configuration)
+      @formatter = formatter
+    end
+
+    #----------------------------------------------------------------------------------------------
+    def grant(user, permissions, table_name, family=nil, qualifier=nil)
+      security_available?
+
+      # Table should exist
+      raise(ArgumentError, "Can't find a table: #{table_name}") unless exists?(table_name)
+
+      htd = @admin.getTableDescriptor(table_name.to_java_bytes)
+
+      if (family != nil)
+        raise(ArgumentError, "Can't find a family: #{family}") unless htd.hasFamily(family.to_java_bytes)
+      end
+
+      #TODO: need to validate user name
+
+      # invoke cp endpoint to perform access control
+      fambytes = family.to_java_bytes if (family != nil)
+      qualbytes = qualifier.to_java_bytes if (qualifier != nil)
+      tp = org.apache.hadoop.hbase.security.access.TablePermission.new(table_name.to_java_bytes, fambytes, qualbytes, permissions.to_java_bytes)
+      meta_table = org.apache.hadoop.hbase.client.HTable.new(@config, org.apache.hadoop.hbase.security.access.AccessControlLists::ACL_TABLE_NAME)
+      protocol = meta_table.coprocessorProxy(org.apache.hadoop.hbase.security.access.AccessControllerProtocol.java_class,
+                                             org.apache.hadoop.hbase.HConstants::EMPTY_START_ROW)
+      protocol.grant(user.to_java_bytes, tp)
+    end
+
+    #----------------------------------------------------------------------------------------------
+    def revoke(user, table_name, family=nil, qualifier=nil)
+      security_available?
+
+      # Table should exist
+      raise(ArgumentError, "Can't find table: #{table_name}") unless exists?(table_name)
+
+      htd = @admin.getTableDescriptor(table_name.to_java_bytes)
+
+      if (family != nil)
+        raise(ArgumentError, "Can't find a family: #{family}") unless htd.hasFamily(family.to_java_bytes)
+      end
+
+      fambytes = family.to_java_bytes if (family != nil)
+      qualbytes = qualifier.to_java_bytes if (qualifier != nil)
+      tp = org.apache.hadoop.hbase.security.access.TablePermission.new(table_name.to_java_bytes, fambytes, qualbytes, "".to_java_bytes)
+      meta_table = org.apache.hadoop.hbase.client.HTable.new(@config, org.apache.hadoop.hbase.security.access.AccessControlLists::ACL_TABLE_NAME)
+      protocol = meta_table.coprocessorProxy(org.apache.hadoop.hbase.security.access.AccessControllerProtocol.java_class,
+                                             org.apache.hadoop.hbase.HConstants::EMPTY_START_ROW)
+      protocol.revoke(user.to_java_bytes, tp)
+    end
+
+    #----------------------------------------------------------------------------------------------
+    def user_permission(table_name)
+      security_available?
+
+      raise(ArgumentError, "Can't find table: #{table_name}") unless exists?(table_name)
+
+      meta_table = org.apache.hadoop.hbase.client.HTable.new(@config, org.apache.hadoop.hbase.security.access.AccessControlLists::ACL_TABLE_NAME)
+      protocol = meta_table.coprocessorProxy(org.apache.hadoop.hbase.security.access.AccessControllerProtocol.java_class,
+                                             org.apache.hadoop.hbase.HConstants::EMPTY_START_ROW)
+      perms = protocol.getUserPermissions(table_name.to_java_bytes)
+
+      res = {}
+      count  = 0
+      perms.each do |value|
+        user_name = String.from_java_bytes(value.getUser)
+        table = (value.getTable != nil) ? org.apache.hadoop.hbase.util.Bytes::toStringBinary(value.getTable) : ''
+        family = (value.getFamily != nil) ? org.apache.hadoop.hbase.util.Bytes::toStringBinary(value.getFamily) : ''
+        qualifier = (value.getQualifier != nil) ? org.apache.hadoop.hbase.util.Bytes::toStringBinary(value.getQualifier) : ''
+
+        action = org.apache.hadoop.hbase.security.access.Permission.new value.getActions
+
+        if block_given?
+          yield(user_name, "#{table},#{family},#{qualifier}: #{action.to_s}")
+        else
+          res[user_name] ||= {}
+          res[user_name][family + ":" +qualifier] = action
+        end
+        count += 1
+      end
+      
+      return ((block_given?) ? count : res)
+    end
+
+    # Does table exist?
+    def exists?(table_name)
+      @admin.tableExists(table_name)
+    end
+
+    # Make sure that security classes are available
+    def security_available?()
+      begin
+        org.apache.hadoop.hbase.security.access.AccessControllerProtocol
+      rescue NameError
+        raise(ArgumentError, "DISABLED: Security features are not available in this build of HBase")
+      end
+    end
+
+  end
+end

Modified: hbase/branches/0.92/src/main/ruby/shell.rb
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/ruby/shell.rb?rev=1203915&r1=1203914&r2=1203915&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/ruby/shell.rb (original)
+++ hbase/branches/0.92/src/main/ruby/shell.rb Sat Nov 19 00:25:37 2011
@@ -87,6 +87,10 @@ module Shell
       @hbase_replication_admin ||= hbase.replication_admin(formatter)
     end
 
+    def hbase_security_admin
+      @hbase_security_admin ||= hbase.security_admin(formatter)
+    end
+
     def export_commands(where)
       ::Shell.commands.keys.each do |cmd|
         where.send :instance_eval, <<-EOF
@@ -284,3 +288,14 @@ Shell.load_command_group(
   ]
 )
 
+Shell.load_command_group(
+  'security',
+  :full_name => 'SECURITY TOOLS',
+  :comment => "NOTE: Above commands are only applicable if running with the AccessController coprocessor",
+  :commands => %w[
+    grant
+    revoke
+    user_permission
+  ]
+)
+

Modified: hbase/branches/0.92/src/main/ruby/shell/commands.rb
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/ruby/shell/commands.rb?rev=1203915&r1=1203914&r2=1203915&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/ruby/shell/commands.rb (original)
+++ hbase/branches/0.92/src/main/ruby/shell/commands.rb Sat Nov 19 00:25:37 2011
@@ -53,6 +53,10 @@ module Shell
         shell.hbase_replication_admin
       end
 
+      def security_admin
+        shell.hbase_security_admin
+      end
+
       #----------------------------------------------------------------------
 
       def formatter

Added: hbase/branches/0.92/src/main/ruby/shell/commands/grant.rb
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/ruby/shell/commands/grant.rb?rev=1203915&view=auto
==============================================================================
--- hbase/branches/0.92/src/main/ruby/shell/commands/grant.rb (added)
+++ hbase/branches/0.92/src/main/ruby/shell/commands/grant.rb Sat Nov 19 00:25:37 2011
@@ -0,0 +1,43 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+module Shell
+  module Commands
+    class Grant < Command
+      def help
+        return <<-EOF
+Grant users specific rights to tables.
+Syntax : grant <user> <permissions> <table> <column family> <column qualifier>
+
+permissions is either zero or more letters from the set "RWXCA".
+READ('R'), WRITE('W'), EXEC('X'), CREATE('C'), ADMIN('A')
+
+For example:
+
+    hbase> grant 'bobsmith', 'RW', 't1', 'f1', 'col1'
+EOF
+      end
+
+      def command(user, rights, table_name, family=nil, qualifier=nil)
+        format_simple_command do
+          security_admin.grant(user, rights, table_name, family, qualifier)
+        end
+      end
+    end
+  end
+end

Added: hbase/branches/0.92/src/main/ruby/shell/commands/revoke.rb
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/ruby/shell/commands/revoke.rb?rev=1203915&view=auto
==============================================================================
--- hbase/branches/0.92/src/main/ruby/shell/commands/revoke.rb (added)
+++ hbase/branches/0.92/src/main/ruby/shell/commands/revoke.rb Sat Nov 19 00:25:37 2011
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+module Shell
+  module Commands
+    class Revoke < Command
+      def help
+        return <<-EOF
+Revoke a user's access rights to tables.
+Syntax : revoke <user> <table> <column family>
+For example:
+
+    hbase> revoke 'bobsmith', 't1', 'f1'
+EOF
+      end
+
+      def command(user, table_name, family=nil, qualifier=nil)
+        format_simple_command do
+          security_admin.revoke(user, table_name, family, qualifier)
+        end
+      end
+    end
+  end
+end

Added: hbase/branches/0.92/src/main/ruby/shell/commands/user_permission.rb
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/ruby/shell/commands/user_permission.rb?rev=1203915&view=auto
==============================================================================
--- hbase/branches/0.92/src/main/ruby/shell/commands/user_permission.rb (added)
+++ hbase/branches/0.92/src/main/ruby/shell/commands/user_permission.rb Sat Nov 19 00:25:37 2011
@@ -0,0 +1,46 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+module Shell
+  module Commands
+    class UserPermission < Command
+      def help
+        return <<-EOF
+Show all table access permissions for the particular user.
+Syntax : user_permission <table>
+For example:
+
+    hbase> user_permission 'table1'
+EOF
+      end
+
+      def command(table)
+        #format_simple_command do
+        #admin.user_permission(table)
+        now = Time.now
+        formatter.header(["User", "Table,Family,Qualifier:Permission"])
+
+        count = security_admin.user_permission(table) do |user, permission|
+          formatter.row([ user, permission])
+        end
+
+        formatter.footer(now, count)
+      end
+    end
+  end
+end