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