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 =
131      TableDescriptorBuilder.newBuilder(TEST_TABLE)
132        .setColumnFamily(
133          ColumnFamilyDescriptorBuilder.newBuilder(TEST_FAMILY).setMaxVersions(100).build())
134        .build();
135    createTable(TEST_UTIL, USER_OWNER, tableDescriptor, new byte[][] { Bytes.toBytes("s") });
136    TEST_UTIL.waitTableEnabled(TEST_TABLE);
137
138    grantOnTable(TEST_UTIL, USER_RW.getShortName(), TEST_TABLE, TEST_FAMILY, null,
139      Permission.Action.READ, Permission.Action.WRITE);
140
141    grantOnTable(TEST_UTIL, USER_RO.getShortName(), TEST_TABLE, TEST_FAMILY, null,
142      Permission.Action.READ);
143  }
144
145  private void loadData() throws IOException {
146    try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
147      try (Table t = conn.getTable(TEST_TABLE)) {
148        for (int i = 0; i < ROW_COUNT; i++) {
149          Put put = new Put(Bytes.toBytes(i));
150          put.addColumn(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes(i));
151          t.put(put);
152        }
153      }
154    }
155  }
156
157  @AfterClass
158  public static void tearDownAfterClass() throws Exception {
159    TEST_UTIL.shutdownMiniCluster();
160  }
161
162  private void verifyRows(TableName tableName) throws IOException {
163    try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
164      Table t = conn.getTable(tableName); ResultScanner scanner = t.getScanner(new Scan())) {
165      Result result;
166      int rowCount = 0;
167      while ((result = scanner.next()) != null) {
168        byte[] value = result.getValue(TEST_FAMILY, TEST_QUALIFIER);
169        Assert.assertArrayEquals(value, Bytes.toBytes(rowCount++));
170      }
171      Assert.assertEquals(ROW_COUNT, rowCount);
172    }
173  }
174
175  protected abstract void snapshot(String snapshotName, TableName tableName) throws Exception;
176
177  protected abstract void cloneSnapshot(String snapshotName, TableName tableName,
178    boolean restoreAcl) throws Exception;
179
180  protected abstract void restoreSnapshot(String snapshotName, boolean restoreAcl) throws Exception;
181
182  @Test
183  public void testRestoreSnapshot() throws Exception {
184    verifyAllowed(new AccessReadAction(TEST_TABLE), USER_OWNER, USER_RO, USER_RW);
185    verifyDenied(new AccessReadAction(TEST_TABLE), USER_NONE);
186    verifyAllowed(new AccessWriteAction(TEST_TABLE), USER_OWNER, USER_RW);
187    verifyDenied(new AccessWriteAction(TEST_TABLE), USER_RO, USER_NONE);
188
189    loadData();
190    verifyRows(TEST_TABLE);
191
192    String snapshotName1 = TEST_UTIL.getRandomUUID().toString();
193    snapshot(snapshotName1, TEST_TABLE);
194
195    // clone snapshot with restoreAcl true.
196    TableName tableName1 = TableName.valueOf(TEST_UTIL.getRandomUUID().toString());
197    cloneSnapshot(snapshotName1, tableName1, true);
198    verifyRows(tableName1);
199    verifyAllowed(new AccessReadAction(tableName1), USER_OWNER, USER_RO, USER_RW);
200    verifyDenied(new AccessReadAction(tableName1), USER_NONE);
201    verifyAllowed(new AccessWriteAction(tableName1), USER_OWNER, USER_RW);
202    verifyDenied(new AccessWriteAction(tableName1), USER_RO, USER_NONE);
203
204    // clone snapshot with restoreAcl false.
205    TableName tableName2 = TableName.valueOf(TEST_UTIL.getRandomUUID().toString());
206    cloneSnapshot(snapshotName1, tableName2, false);
207    verifyRows(tableName2);
208    verifyDenied(new AccessReadAction(tableName2), USER_OWNER);
209    verifyDenied(new AccessReadAction(tableName2), USER_NONE, USER_RO, USER_RW);
210    verifyDenied(new AccessWriteAction(tableName2), USER_OWNER);
211    verifyDenied(new AccessWriteAction(tableName2), USER_RO, USER_RW, USER_NONE);
212
213    // remove read permission for USER_RO.
214    revokeFromTable(TEST_UTIL, USER_RO.getShortName(), TEST_TABLE, TEST_FAMILY, null,
215      Permission.Action.READ);
216    verifyAllowed(new AccessReadAction(TEST_TABLE), USER_OWNER, USER_RW);
217    verifyDenied(new AccessReadAction(TEST_TABLE), USER_RO, USER_NONE);
218    verifyAllowed(new AccessWriteAction(TEST_TABLE), USER_OWNER, USER_RW);
219    verifyDenied(new AccessWriteAction(TEST_TABLE), USER_RO, USER_NONE);
220
221    // restore snapshot with restoreAcl false.
222    TEST_UTIL.getAdmin().disableTable(TEST_TABLE);
223    restoreSnapshot(snapshotName1, false);
224    TEST_UTIL.getAdmin().enableTable(TEST_TABLE);
225    verifyAllowed(new AccessReadAction(TEST_TABLE), USER_OWNER, USER_RW);
226    verifyDenied(new AccessReadAction(TEST_TABLE), USER_RO, USER_NONE);
227    verifyAllowed(new AccessWriteAction(TEST_TABLE), USER_OWNER, USER_RW);
228    verifyDenied(new AccessWriteAction(TEST_TABLE), USER_RO, USER_NONE);
229
230    // restore snapshot with restoreAcl true.
231    TEST_UTIL.getAdmin().disableTable(TEST_TABLE);
232    restoreSnapshot(snapshotName1, true);
233    TEST_UTIL.getAdmin().enableTable(TEST_TABLE);
234    verifyAllowed(new AccessReadAction(TEST_TABLE), USER_OWNER, USER_RO, USER_RW);
235    verifyDenied(new AccessReadAction(TEST_TABLE), USER_NONE);
236    verifyAllowed(new AccessWriteAction(TEST_TABLE), USER_OWNER, USER_RW);
237    verifyDenied(new AccessWriteAction(TEST_TABLE), USER_RO, USER_NONE);
238  }
239
240  final class AccessSnapshotAction implements AccessTestAction {
241    private String snapshotName;
242
243    private AccessSnapshotAction(String snapshotName) {
244      this.snapshotName = snapshotName;
245    }
246
247    @Override
248    public Object run() throws Exception {
249      try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
250        Admin admin = conn.getAdmin()) {
251        admin.snapshot(this.snapshotName, TEST_TABLE);
252      }
253      return null;
254    }
255  }
256
257  @Test
258  public void testDeleteSnapshot() throws Exception {
259    String testSnapshotName = HBaseCommonTestingUtil.getRandomUUID().toString();
260    verifyAllowed(new AccessSnapshotAction(testSnapshotName), USER_OWNER);
261    verifyDenied(new AccessSnapshotAction(HBaseCommonTestingUtil.getRandomUUID().toString()),
262      USER_RO, USER_RW, USER_NONE);
263    List<SnapshotDescription> snapshotDescriptions =
264      TEST_UTIL.getAdmin().listSnapshots(Pattern.compile(testSnapshotName));
265    Assert.assertEquals(1, snapshotDescriptions.size());
266    Assert.assertEquals(USER_OWNER.getShortName(), snapshotDescriptions.get(0).getOwner());
267    AccessTestAction deleteSnapshotAction = () -> {
268      try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
269        Admin admin = conn.getAdmin()) {
270        admin.deleteSnapshot(testSnapshotName);
271      }
272      return null;
273    };
274    verifyDenied(deleteSnapshotAction, USER_RO, USER_RW, USER_NONE);
275    verifyAllowed(deleteSnapshotAction, USER_OWNER);
276
277    List<SnapshotDescription> snapshotsAfterDelete =
278      TEST_UTIL.getAdmin().listSnapshots(Pattern.compile(testSnapshotName));
279    Assert.assertEquals(0, snapshotsAfterDelete.size());
280  }
281}