001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019
020import java.io.IOException;
021import org.apache.hadoop.conf.Configuration;
022import org.apache.hadoop.hbase.Coprocessor;
023import org.apache.hadoop.hbase.HBaseClassTestRule;
024import org.apache.hadoop.hbase.HBaseTestingUtility;
025import org.apache.hadoop.hbase.HColumnDescriptor;
026import org.apache.hadoop.hbase.HTableDescriptor;
027import org.apache.hadoop.hbase.TableName;
028import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
029import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
030import org.apache.hadoop.hbase.security.User;
031import org.apache.hadoop.hbase.security.access.AccessControlConstants;
032import org.apache.hadoop.hbase.security.access.AccessControlLists;
033import org.apache.hadoop.hbase.security.access.AccessController;
034import org.apache.hadoop.hbase.security.access.Permission;
035import org.apache.hadoop.hbase.security.access.SecureTestUtil;
036import org.apache.hadoop.hbase.testclassification.ClientTests;
037import org.apache.hadoop.hbase.testclassification.MediumTests;
038import org.apache.hadoop.hbase.util.Bytes;
039import org.junit.AfterClass;
040import org.junit.Assert;
041import org.junit.Before;
042import org.junit.BeforeClass;
043import org.junit.ClassRule;
044import org.junit.Test;
045import org.junit.experimental.categories.Category;
046
047@Category({ MediumTests.class, ClientTests.class })
048public class TestSnapshotWithAcl extends SecureTestUtil {
049
050  @ClassRule
051  public static final HBaseClassTestRule CLASS_RULE =
052      HBaseClassTestRule.forClass(TestSnapshotWithAcl.class);
053
054  public TableName TEST_TABLE = TableName.valueOf(TEST_UTIL.getRandomUUID().toString());
055
056  private static final int ROW_COUNT = 30000;
057
058  private static byte[] TEST_FAMILY = Bytes.toBytes("f1");
059  private static byte[] TEST_QUALIFIER = Bytes.toBytes("cq");
060  private static byte[] TEST_ROW = Bytes.toBytes(0);
061  private static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
062  private static Configuration conf;
063  private static HBaseAdmin admin = null;
064
065  // user is table owner. will have all permissions on table
066  private static User USER_OWNER;
067  // user with rw permissions on column family.
068  private static User USER_RW;
069  // user with read-only permissions
070  private static User USER_RO;
071  // user with none permissions
072  private static User USER_NONE;
073
074  static class AccessReadAction implements AccessTestAction {
075
076    private TableName tableName;
077
078    public AccessReadAction(TableName tableName) {
079      this.tableName = tableName;
080    }
081
082    @Override
083    public Object run() throws Exception {
084      Get g = new Get(TEST_ROW);
085      g.addFamily(TEST_FAMILY);
086      try (Connection conn = ConnectionFactory.createConnection(conf)) {
087        try (Table t = conn.getTable(tableName)) {
088          t.get(g);
089        }
090      }
091      return null;
092    }
093  };
094
095  static class AccessWriteAction implements AccessTestAction {
096    private TableName tableName;
097
098    public AccessWriteAction(TableName tableName) {
099      this.tableName = tableName;
100    }
101
102    @Override
103    public Object run() throws Exception {
104      Put p = new Put(TEST_ROW);
105      p.addColumn(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes(0));
106      try (Connection conn = ConnectionFactory.createConnection(conf)) {
107        try (Table t = conn.getTable(tableName)) {
108          t.put(p);
109        }
110      }
111      return null;
112    }
113  }
114
115
116  @BeforeClass
117  public static void setupBeforeClass() throws Exception {
118    conf = TEST_UTIL.getConfiguration();
119    // Enable security
120    enableSecurity(conf);
121    conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, AccessController.class.getName());
122    // Verify enableSecurity sets up what we require
123    verifyConfiguration(conf);
124    // Enable EXEC permission checking
125    conf.setBoolean(AccessControlConstants.EXEC_PERMISSION_CHECKS_KEY, true);
126    TEST_UTIL.startMiniCluster();
127    TEST_UTIL.waitUntilAllRegionsAssigned(AccessControlLists.ACL_TABLE_NAME);
128    MasterCoprocessorHost cpHost =
129        TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterCoprocessorHost();
130    cpHost.load(AccessController.class, Coprocessor.PRIORITY_HIGHEST, conf);
131
132    USER_OWNER = User.createUserForTesting(conf, "owner", new String[0]);
133    USER_RW = User.createUserForTesting(conf, "rwuser", new String[0]);
134    USER_RO = User.createUserForTesting(conf, "rouser", new String[0]);
135    USER_NONE = User.createUserForTesting(conf, "usernone", new String[0]);
136  }
137
138  @Before
139  public void setUp() throws Exception {
140    admin = TEST_UTIL.getHBaseAdmin();
141    HTableDescriptor htd = new HTableDescriptor(TEST_TABLE);
142    HColumnDescriptor hcd = new HColumnDescriptor(TEST_FAMILY);
143    hcd.setMaxVersions(100);
144    htd.addFamily(hcd);
145    htd.setOwner(USER_OWNER);
146    admin.createTable(htd, new byte[][] { Bytes.toBytes("s") });
147    TEST_UTIL.waitTableEnabled(TEST_TABLE);
148
149    grantOnTable(TEST_UTIL, USER_RW.getShortName(), TEST_TABLE, TEST_FAMILY, null,
150            Permission.Action.READ, Permission.Action.WRITE);
151
152    grantOnTable(TEST_UTIL, USER_RO.getShortName(), TEST_TABLE, TEST_FAMILY, null,
153            Permission.Action.READ);
154  }
155
156  private void loadData() throws IOException {
157    try (Connection conn = ConnectionFactory.createConnection(conf)) {
158      try (Table t = conn.getTable(TEST_TABLE)) {
159        for (int i = 0; i < ROW_COUNT; i++) {
160          Put put = new Put(Bytes.toBytes(i));
161          put.addColumn(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes(i));
162          t.put(put);
163        }
164      }
165    }
166  }
167
168  @AfterClass
169  public static void tearDownAfterClass() throws Exception {
170    TEST_UTIL.shutdownMiniCluster();
171  }
172
173  private void verifyRows(TableName tableName) throws IOException {
174    try (Connection conn = ConnectionFactory.createConnection(conf)) {
175      try (Table t = conn.getTable(tableName)) {
176        try (ResultScanner scanner = t.getScanner(new Scan())) {
177          Result result;
178          int rowCount = 0;
179          while ((result = scanner.next()) != null) {
180            byte[] value = result.getValue(TEST_FAMILY, TEST_QUALIFIER);
181            Assert.assertArrayEquals(value, Bytes.toBytes(rowCount++));
182          }
183          Assert.assertEquals(ROW_COUNT, rowCount);
184        }
185      }
186    }
187  }
188
189  @Test
190  public void testRestoreSnapshot() throws Exception {
191    verifyAllowed(new AccessReadAction(TEST_TABLE), USER_OWNER, USER_RO, USER_RW);
192    verifyDenied(new AccessReadAction(TEST_TABLE), USER_NONE);
193    verifyAllowed(new AccessWriteAction(TEST_TABLE), USER_OWNER, USER_RW);
194    verifyDenied(new AccessWriteAction(TEST_TABLE), USER_RO, USER_NONE);
195
196    loadData();
197    verifyRows(TEST_TABLE);
198
199    String snapshotName1 = TEST_UTIL.getRandomUUID().toString();
200    admin.snapshot(snapshotName1, TEST_TABLE);
201
202    // clone snapshot with restoreAcl true.
203    TableName tableName1 = TableName.valueOf(TEST_UTIL.getRandomUUID().toString());
204    admin.cloneSnapshot(snapshotName1, tableName1, true);
205    verifyRows(tableName1);
206    verifyAllowed(new AccessReadAction(tableName1), USER_OWNER, USER_RO, USER_RW);
207    verifyDenied(new AccessReadAction(tableName1), USER_NONE);
208    verifyAllowed(new AccessWriteAction(tableName1), USER_OWNER, USER_RW);
209    verifyDenied(new AccessWriteAction(tableName1), USER_RO, USER_NONE);
210
211    // clone snapshot with restoreAcl false.
212    TableName tableName2 = TableName.valueOf(TEST_UTIL.getRandomUUID().toString());
213    admin.cloneSnapshot(snapshotName1, tableName2, false);
214    verifyRows(tableName2);
215    verifyAllowed(new AccessReadAction(tableName2), USER_OWNER);
216    verifyDenied(new AccessReadAction(tableName2), USER_NONE, USER_RO, USER_RW);
217    verifyAllowed(new AccessWriteAction(tableName2), USER_OWNER);
218    verifyDenied(new AccessWriteAction(tableName2), USER_RO, USER_RW, USER_NONE);
219
220    // remove read permission for USER_RO.
221    revokeFromTable(TEST_UTIL, USER_RO.getShortName(), TEST_TABLE, TEST_FAMILY, null,
222      Permission.Action.READ);
223    verifyAllowed(new AccessReadAction(TEST_TABLE), USER_OWNER, USER_RW);
224    verifyDenied(new AccessReadAction(TEST_TABLE), USER_RO, USER_NONE);
225    verifyAllowed(new AccessWriteAction(TEST_TABLE), USER_OWNER, USER_RW);
226    verifyDenied(new AccessWriteAction(TEST_TABLE), USER_RO, USER_NONE);
227
228    // restore snapshot with restoreAcl false.
229    admin.disableTable(TEST_TABLE);
230    admin.restoreSnapshot(snapshotName1, false, false);
231    admin.enableTable(TEST_TABLE);
232    verifyAllowed(new AccessReadAction(TEST_TABLE), USER_OWNER, USER_RW);
233    verifyDenied(new AccessReadAction(TEST_TABLE), USER_RO, USER_NONE);
234    verifyAllowed(new AccessWriteAction(TEST_TABLE), USER_OWNER, USER_RW);
235    verifyDenied(new AccessWriteAction(TEST_TABLE), USER_RO, USER_NONE);
236
237    // restore snapshot with restoreAcl true.
238    admin.disableTable(TEST_TABLE);
239    admin.restoreSnapshot(snapshotName1, false, true);
240    admin.enableTable(TEST_TABLE);
241    verifyAllowed(new AccessReadAction(TEST_TABLE), USER_OWNER, USER_RO, USER_RW);
242    verifyDenied(new AccessReadAction(TEST_TABLE), USER_NONE);
243    verifyAllowed(new AccessWriteAction(TEST_TABLE), USER_OWNER, USER_RW);
244    verifyDenied(new AccessWriteAction(TEST_TABLE), USER_RO, USER_NONE);
245  }
246}