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  @BeforeClass
076  public static void setupBeforeClass() throws Exception {
077    // setup configuration
078    conf = TEST_UTIL.getConfiguration();
079    VisibilityTestUtil.enableVisiblityLabels(conf);
080    conf.setBoolean(VisibilityConstants.CHECK_AUTHS_FOR_MUTATION, true);
081    conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class,
082        ScanLabelGenerator.class);
083    conf.set("hbase.superuser", "admin");
084    TEST_UTIL.startMiniCluster(2);
085    SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
086    USER = User.createUserForTesting(conf, "user", new String[]{});
087    // Wait for the labels table to become available
088    TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
089    addLabels();
090  }
091
092  @AfterClass
093  public static void tearDownAfterClass() throws Exception {
094    TEST_UTIL.shutdownMiniCluster();
095  }
096
097  public static void addLabels() throws Exception {
098    PrivilegedExceptionAction<VisibilityLabelsResponse> action =
099        new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
100      @Override
101      public VisibilityLabelsResponse run() throws Exception {
102        String[] labels = { TOPSECRET };
103        try (Connection conn = ConnectionFactory.createConnection(conf)) {
104          VisibilityClient.addLabels(conn, labels);
105        } catch (Throwable t) {
106          throw new IOException(t);
107        }
108        return null;
109      }
110    };
111    SUPERUSER.runAs(action);
112  }
113
114  @Test
115  public void testVerifyAccessDeniedForInvalidUserAuths() throws Exception {
116    PrivilegedExceptionAction<VisibilityLabelsResponse> action =
117        new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
118      @Override
119      public VisibilityLabelsResponse run() throws Exception {
120        try (Connection conn = ConnectionFactory.createConnection(conf)) {
121          return VisibilityClient.setAuths(conn, new String[] { TOPSECRET },
122              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 },
168              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}