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.security.visibility;
019
020import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
021import static org.junit.Assert.assertTrue;
022
023import java.io.IOException;
024import java.security.PrivilegedExceptionAction;
025import org.apache.hadoop.conf.Configuration;
026import org.apache.hadoop.hbase.HBaseClassTestRule;
027import org.apache.hadoop.hbase.HBaseTestingUtil;
028import org.apache.hadoop.hbase.HConstants;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.client.Admin;
031import org.apache.hadoop.hbase.client.Append;
032import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
033import org.apache.hadoop.hbase.client.Connection;
034import org.apache.hadoop.hbase.client.ConnectionFactory;
035import org.apache.hadoop.hbase.client.Put;
036import org.apache.hadoop.hbase.client.Table;
037import org.apache.hadoop.hbase.client.TableDescriptor;
038import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
039import org.apache.hadoop.hbase.security.User;
040import org.apache.hadoop.hbase.testclassification.MediumTests;
041import org.apache.hadoop.hbase.testclassification.SecurityTests;
042import org.apache.hadoop.hbase.util.Bytes;
043import org.junit.AfterClass;
044import org.junit.Assert;
045import org.junit.BeforeClass;
046import org.junit.ClassRule;
047import org.junit.Rule;
048import org.junit.Test;
049import org.junit.experimental.categories.Category;
050import org.junit.rules.TestName;
051
052import org.apache.hadoop.hbase.shaded.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse;
053
054@Category({ SecurityTests.class, MediumTests.class })
055/**
056 * Test visibility by setting 'hbase.security.visibility.mutations.checkauths' to true
057 */
058public class TestVisibilityWithCheckAuths {
059
060  @ClassRule
061  public static final HBaseClassTestRule CLASS_RULE =
062    HBaseClassTestRule.forClass(TestVisibilityWithCheckAuths.class);
063
064  private static final String TOPSECRET = "TOPSECRET";
065  private static final String PUBLIC = "PUBLIC";
066  public static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
067  private static final byte[] row1 = Bytes.toBytes("row1");
068  private final static byte[] fam = Bytes.toBytes("info");
069  private final static byte[] qual = Bytes.toBytes("qual");
070  private final static byte[] value = Bytes.toBytes("value");
071  public static Configuration conf;
072
073  @Rule
074  public final TestName TEST_NAME = new TestName();
075  public static User SUPERUSER;
076  public static User USER;
077
078  @BeforeClass
079  public static void setupBeforeClass() throws Exception {
080    // setup configuration
081    conf = TEST_UTIL.getConfiguration();
082    VisibilityTestUtil.enableVisiblityLabels(conf);
083    conf.setBoolean(VisibilityConstants.CHECK_AUTHS_FOR_MUTATION, true);
084    conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class,
085      ScanLabelGenerator.class);
086    conf.set("hbase.superuser", "admin");
087    TEST_UTIL.startMiniCluster(2);
088    SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
089    USER = User.createUserForTesting(conf, "user", new String[] {});
090    // Wait for the labels table to become available
091    TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
092    addLabels();
093  }
094
095  @AfterClass
096  public static void tearDownAfterClass() throws Exception {
097    TEST_UTIL.shutdownMiniCluster();
098  }
099
100  public static void addLabels() throws Exception {
101    PrivilegedExceptionAction<VisibilityLabelsResponse> action =
102      new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
103        @Override
104        public VisibilityLabelsResponse run() throws Exception {
105          String[] labels = { TOPSECRET };
106          try (Connection conn = ConnectionFactory.createConnection(conf)) {
107            VisibilityClient.addLabels(conn, labels);
108          } catch (Throwable t) {
109            throw new IOException(t);
110          }
111          return null;
112        }
113      };
114    SUPERUSER.runAs(action);
115  }
116
117  @Test
118  public void testVerifyAccessDeniedForInvalidUserAuths() throws Exception {
119    PrivilegedExceptionAction<VisibilityLabelsResponse> action =
120      new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
121        @Override
122        public VisibilityLabelsResponse run() throws Exception {
123          try (Connection conn = ConnectionFactory.createConnection(conf)) {
124            return VisibilityClient.setAuths(conn, new String[] { TOPSECRET }, USER.getShortName());
125          } catch (Throwable e) {
126          }
127          return null;
128        }
129      };
130    SUPERUSER.runAs(action);
131    final TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
132    Admin hBaseAdmin = TEST_UTIL.getAdmin();
133    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName)
134      .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(fam).setMaxVersions(5).build())
135      .build();
136    hBaseAdmin.createTable(tableDescriptor);
137    try {
138      TEST_UTIL.getAdmin().flush(tableName);
139      PrivilegedExceptionAction<Void> actiona = new PrivilegedExceptionAction<Void>() {
140        @Override
141        public Void run() throws Exception {
142          try (Connection connection = ConnectionFactory.createConnection(conf);
143            Table table = connection.getTable(tableName)) {
144            Put p = new Put(row1);
145            p.setCellVisibility(new CellVisibility(PUBLIC + "&" + TOPSECRET));
146            p.addColumn(fam, qual, 125L, value);
147            table.put(p);
148            Assert.fail("Testcase should fail with AccesDeniedException");
149          } catch (Throwable t) {
150            assertTrue(t.getMessage().contains("AccessDeniedException"));
151          }
152          return null;
153        }
154      };
155      USER.runAs(actiona);
156    } catch (Exception e) {
157      throw new IOException(e);
158    }
159  }
160
161  @Test
162  public void testLabelsWithAppend() throws Throwable {
163    PrivilegedExceptionAction<VisibilityLabelsResponse> action =
164      new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
165        @Override
166        public VisibilityLabelsResponse run() throws Exception {
167          try (Connection conn = ConnectionFactory.createConnection(conf)) {
168            return VisibilityClient.setAuths(conn, new String[] { TOPSECRET }, USER.getShortName());
169          } catch (Throwable e) {
170          }
171          return null;
172        }
173      };
174    SUPERUSER.runAs(action);
175    final TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
176    try (Table table = TEST_UTIL.createTable(tableName, fam)) {
177      final byte[] row1 = Bytes.toBytes("row1");
178      final byte[] val = Bytes.toBytes("a");
179      PrivilegedExceptionAction<Void> actiona = new PrivilegedExceptionAction<Void>() {
180        @Override
181        public Void run() throws Exception {
182          try (Connection connection = ConnectionFactory.createConnection(conf);
183            Table table = connection.getTable(tableName)) {
184            Put put = new Put(row1);
185            put.addColumn(fam, qual, HConstants.LATEST_TIMESTAMP, val);
186            put.setCellVisibility(new CellVisibility(TOPSECRET));
187            table.put(put);
188          }
189          return null;
190        }
191      };
192      USER.runAs(actiona);
193      actiona = new PrivilegedExceptionAction<Void>() {
194        @Override
195        public Void run() throws Exception {
196          try (Connection connection = ConnectionFactory.createConnection(conf);
197            Table table = connection.getTable(tableName)) {
198            Append append = new Append(row1);
199            append.addColumn(fam, qual, Bytes.toBytes("b"));
200            table.append(append);
201          }
202          return null;
203        }
204      };
205      USER.runAs(actiona);
206      actiona = new PrivilegedExceptionAction<Void>() {
207        @Override
208        public Void run() throws Exception {
209          try (Connection connection = ConnectionFactory.createConnection(conf);
210            Table table = connection.getTable(tableName)) {
211            Append append = new Append(row1);
212            append.addColumn(fam, qual, Bytes.toBytes("c"));
213            append.setCellVisibility(new CellVisibility(PUBLIC));
214            table.append(append);
215            Assert.fail("Testcase should fail with AccesDeniedException");
216          } catch (Throwable t) {
217            assertTrue(t.getMessage().contains("AccessDeniedException"));
218          }
219          return null;
220        }
221      };
222      USER.runAs(actiona);
223    }
224  }
225}