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.List;
022import java.util.regex.Pattern;
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.hbase.Coprocessor;
025import org.apache.hadoop.hbase.HBaseCommonTestingUtil;
026import org.apache.hadoop.hbase.HBaseTestingUtil;
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.AccessController;
033import org.apache.hadoop.hbase.security.access.Permission;
034import org.apache.hadoop.hbase.security.access.PermissionStorage;
035import org.apache.hadoop.hbase.security.access.SecureTestUtil;
036import org.apache.hadoop.hbase.util.Bytes;
037import org.junit.AfterClass;
038import org.junit.Assert;
039import org.junit.Before;
040import org.junit.BeforeClass;
041import org.junit.Test;
042
043public abstract class SnapshotWithAclTestBase extends SecureTestUtil {
044
045  private TableName TEST_TABLE = TableName.valueOf(TEST_UTIL.getRandomUUID().toString());
046
047  private static final int ROW_COUNT = 30000;
048
049  private static byte[] TEST_FAMILY = Bytes.toBytes("f1");
050  private static byte[] TEST_QUALIFIER = Bytes.toBytes("cq");
051  private static byte[] TEST_ROW = Bytes.toBytes(0);
052
053  protected static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
054
055  // user is table owner. will have all permissions on table
056  private static User USER_OWNER;
057  // user with rw permissions on column family.
058  private static User USER_RW;
059  // user with read-only permissions
060  private static User USER_RO;
061  // user with none permissions
062  private static User USER_NONE;
063
064  static class AccessReadAction implements AccessTestAction {
065
066    private TableName tableName;
067
068    public AccessReadAction(TableName tableName) {
069      this.tableName = tableName;
070    }
071
072    @Override
073    public Object run() throws Exception {
074      Get g = new Get(TEST_ROW);
075      g.addFamily(TEST_FAMILY);
076      try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
077        Table t = conn.getTable(tableName)) {
078        t.get(g);
079      }
080      return null;
081    }
082  }
083
084  static class AccessWriteAction implements AccessTestAction {
085    private TableName tableName;
086
087    public AccessWriteAction(TableName tableName) {
088      this.tableName = tableName;
089    }
090
091    @Override
092    public Object run() throws Exception {
093      Put p = new Put(TEST_ROW);
094      p.addColumn(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes(0));
095      try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
096        Table t = conn.getTable(tableName)) {
097        t.put(p);
098      }
099      return null;
100    }
101  }
102
103  @BeforeClass
104  public static void setupBeforeClass() throws Exception {
105    Configuration conf = TEST_UTIL.getConfiguration();
106    // Enable security
107    enableSecurity(conf);
108    conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, AccessController.class.getName());
109    // Verify enableSecurity sets up what we require
110    verifyConfiguration(conf);
111    // Enable EXEC permission checking
112    conf.setBoolean(AccessControlConstants.EXEC_PERMISSION_CHECKS_KEY, true);
113    TEST_UTIL.startMiniCluster();
114    TEST_UTIL.waitUntilAllRegionsAssigned(PermissionStorage.ACL_TABLE_NAME);
115    MasterCoprocessorHost cpHost =
116      TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterCoprocessorHost();
117    cpHost.load(AccessController.class, Coprocessor.PRIORITY_HIGHEST, conf);
118
119    USER_OWNER = User.createUserForTesting(conf, "owner", new String[0]);
120    USER_RW = User.createUserForTesting(conf, "rwuser", new String[0]);
121    USER_RO = User.createUserForTesting(conf, "rouser", new String[0]);
122    USER_NONE = User.createUserForTesting(conf, "usernone", new String[0]);
123
124    // Grant table creation permission to USER_OWNER
125    grantGlobal(TEST_UTIL, USER_OWNER.getShortName(), Permission.Action.CREATE);
126  }
127
128  @Before
129  public void setUp() throws Exception {
130    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(TEST_TABLE)
131      .setColumnFamily(
132        ColumnFamilyDescriptorBuilder.newBuilder(TEST_FAMILY).setMaxVersions(100).build()).build();
133    createTable(TEST_UTIL, USER_OWNER, tableDescriptor, new byte[][] { Bytes.toBytes("s") });
134    TEST_UTIL.waitTableEnabled(TEST_TABLE);
135
136    grantOnTable(TEST_UTIL, USER_RW.getShortName(), TEST_TABLE, TEST_FAMILY, null,
137      Permission.Action.READ, Permission.Action.WRITE);
138
139    grantOnTable(TEST_UTIL, USER_RO.getShortName(), TEST_TABLE, TEST_FAMILY, null,
140      Permission.Action.READ);
141  }
142
143  private void loadData() throws IOException {
144    try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
145      try (Table t = conn.getTable(TEST_TABLE)) {
146        for (int i = 0; i < ROW_COUNT; i++) {
147          Put put = new Put(Bytes.toBytes(i));
148          put.addColumn(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes(i));
149          t.put(put);
150        }
151      }
152    }
153  }
154
155  @AfterClass
156  public static void tearDownAfterClass() throws Exception {
157    TEST_UTIL.shutdownMiniCluster();
158  }
159
160  private void verifyRows(TableName tableName) throws IOException {
161    try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
162      Table t = conn.getTable(tableName); ResultScanner scanner = t.getScanner(new Scan())) {
163      Result result;
164      int rowCount = 0;
165      while ((result = scanner.next()) != null) {
166        byte[] value = result.getValue(TEST_FAMILY, TEST_QUALIFIER);
167        Assert.assertArrayEquals(value, Bytes.toBytes(rowCount++));
168      }
169      Assert.assertEquals(ROW_COUNT, rowCount);
170    }
171  }
172
173  protected abstract void snapshot(String snapshotName, TableName tableName) throws Exception;
174
175  protected abstract void cloneSnapshot(String snapshotName, TableName tableName,
176      boolean restoreAcl) throws Exception;
177
178  protected abstract void restoreSnapshot(String snapshotName, boolean restoreAcl) throws Exception;
179
180  @Test
181  public void testRestoreSnapshot() throws Exception {
182    verifyAllowed(new AccessReadAction(TEST_TABLE), USER_OWNER, USER_RO, USER_RW);
183    verifyDenied(new AccessReadAction(TEST_TABLE), USER_NONE);
184    verifyAllowed(new AccessWriteAction(TEST_TABLE), USER_OWNER, USER_RW);
185    verifyDenied(new AccessWriteAction(TEST_TABLE), USER_RO, USER_NONE);
186
187    loadData();
188    verifyRows(TEST_TABLE);
189
190    String snapshotName1 = TEST_UTIL.getRandomUUID().toString();
191    snapshot(snapshotName1, TEST_TABLE);
192
193    // clone snapshot with restoreAcl true.
194    TableName tableName1 = TableName.valueOf(TEST_UTIL.getRandomUUID().toString());
195    cloneSnapshot(snapshotName1, tableName1, true);
196    verifyRows(tableName1);
197    verifyAllowed(new AccessReadAction(tableName1), USER_OWNER, USER_RO, USER_RW);
198    verifyDenied(new AccessReadAction(tableName1), USER_NONE);
199    verifyAllowed(new AccessWriteAction(tableName1), USER_OWNER, USER_RW);
200    verifyDenied(new AccessWriteAction(tableName1), USER_RO, USER_NONE);
201
202    // clone snapshot with restoreAcl false.
203    TableName tableName2 = TableName.valueOf(TEST_UTIL.getRandomUUID().toString());
204    cloneSnapshot(snapshotName1, tableName2, false);
205    verifyRows(tableName2);
206    verifyDenied(new AccessReadAction(tableName2), USER_OWNER);
207    verifyDenied(new AccessReadAction(tableName2), USER_NONE, USER_RO, USER_RW);
208    verifyDenied(new AccessWriteAction(tableName2), USER_OWNER);
209    verifyDenied(new AccessWriteAction(tableName2), USER_RO, USER_RW, USER_NONE);
210
211    // remove read permission for USER_RO.
212    revokeFromTable(TEST_UTIL, USER_RO.getShortName(), TEST_TABLE, TEST_FAMILY, null,
213      Permission.Action.READ);
214    verifyAllowed(new AccessReadAction(TEST_TABLE), USER_OWNER, USER_RW);
215    verifyDenied(new AccessReadAction(TEST_TABLE), USER_RO, USER_NONE);
216    verifyAllowed(new AccessWriteAction(TEST_TABLE), USER_OWNER, USER_RW);
217    verifyDenied(new AccessWriteAction(TEST_TABLE), USER_RO, USER_NONE);
218
219    // restore snapshot with restoreAcl false.
220    TEST_UTIL.getAdmin().disableTable(TEST_TABLE);
221    restoreSnapshot(snapshotName1, false);
222    TEST_UTIL.getAdmin().enableTable(TEST_TABLE);
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 true.
229    TEST_UTIL.getAdmin().disableTable(TEST_TABLE);
230    restoreSnapshot(snapshotName1, true);
231    TEST_UTIL.getAdmin().enableTable(TEST_TABLE);
232    verifyAllowed(new AccessReadAction(TEST_TABLE), USER_OWNER, USER_RO, USER_RW);
233    verifyDenied(new AccessReadAction(TEST_TABLE), USER_NONE);
234    verifyAllowed(new AccessWriteAction(TEST_TABLE), USER_OWNER, USER_RW);
235    verifyDenied(new AccessWriteAction(TEST_TABLE), USER_RO, USER_NONE);
236  }
237
238
239  final class AccessSnapshotAction implements AccessTestAction {
240    private String snapshotName;
241    private AccessSnapshotAction(String snapshotName) {
242      this.snapshotName = snapshotName;
243    }
244    @Override
245    public Object run() throws Exception {
246      try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
247        Admin admin = conn.getAdmin()) {
248        admin.snapshot(this.snapshotName, TEST_TABLE);
249      }
250      return null;
251    }
252  }
253
254  @Test
255  public void testDeleteSnapshot() throws Exception {
256    String testSnapshotName = HBaseCommonTestingUtil.getRandomUUID().toString();
257    verifyAllowed(new AccessSnapshotAction(testSnapshotName), USER_OWNER);
258    verifyDenied(new AccessSnapshotAction(HBaseCommonTestingUtil.getRandomUUID().toString()),
259      USER_RO, USER_RW, USER_NONE);
260    List<SnapshotDescription> snapshotDescriptions = TEST_UTIL.getAdmin().listSnapshots(
261      Pattern.compile(testSnapshotName));
262    Assert.assertEquals(1, snapshotDescriptions.size());
263    Assert.assertEquals(USER_OWNER.getShortName(), snapshotDescriptions.get(0).getOwner());
264    AccessTestAction deleteSnapshotAction = () -> {
265      try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
266        Admin admin = conn.getAdmin()) {
267        admin.deleteSnapshot(testSnapshotName);
268      }
269      return null;
270    };
271    verifyDenied(deleteSnapshotAction, USER_RO, USER_RW, USER_NONE);
272    verifyAllowed(deleteSnapshotAction, USER_OWNER);
273
274    List<SnapshotDescription> snapshotsAfterDelete = TEST_UTIL.getAdmin().listSnapshots(
275      Pattern.compile(testSnapshotName));
276    Assert.assertEquals(0, snapshotsAfterDelete.size());
277  }
278}