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