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.jupiter.api.Assertions.assertEquals;
022import static org.junit.jupiter.api.Assertions.assertTrue;
023import static org.junit.jupiter.api.Assertions.fail;
024
025import java.security.PrivilegedExceptionAction;
026import java.util.ArrayList;
027import java.util.List;
028import org.apache.hadoop.conf.Configuration;
029import org.apache.hadoop.hbase.HBaseTestingUtil;
030import org.apache.hadoop.hbase.HConstants;
031import org.apache.hadoop.hbase.TableName;
032import org.apache.hadoop.hbase.client.Connection;
033import org.apache.hadoop.hbase.client.ConnectionFactory;
034import org.apache.hadoop.hbase.client.Put;
035import org.apache.hadoop.hbase.client.Result;
036import org.apache.hadoop.hbase.client.ResultScanner;
037import org.apache.hadoop.hbase.client.Scan;
038import org.apache.hadoop.hbase.client.Table;
039import org.apache.hadoop.hbase.security.User;
040import org.apache.hadoop.hbase.security.access.SecureTestUtil;
041import org.apache.hadoop.hbase.testclassification.LargeTests;
042import org.apache.hadoop.hbase.testclassification.SecurityTests;
043import org.apache.hadoop.hbase.util.Bytes;
044import org.junit.jupiter.api.AfterAll;
045import org.junit.jupiter.api.BeforeAll;
046import org.junit.jupiter.api.Tag;
047import org.junit.jupiter.api.Test;
048import org.junit.jupiter.api.TestInfo;
049
050import org.apache.hbase.thirdparty.com.google.protobuf.ByteString;
051
052import org.apache.hadoop.hbase.shaded.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse;
053
054@Tag(SecurityTests.TAG)
055@Tag(LargeTests.TAG)
056public class TestWithDisabledAuthorization {
057
058  private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
059
060  private static final String CONFIDENTIAL = "confidential";
061  private static final String SECRET = "secret";
062  private static final String PRIVATE = "private";
063  private static final byte[] TEST_FAMILY = Bytes.toBytes("test");
064  private static final byte[] TEST_QUALIFIER = Bytes.toBytes("q");
065  private static final byte[] ZERO = Bytes.toBytes(0L);
066
067  private static User SUPERUSER;
068  private static User USER_RW;
069  private static Configuration conf;
070
071  @BeforeAll
072  public static void setUpBeforeClass() throws Exception {
073    conf = TEST_UTIL.getConfiguration();
074    // Up the handlers; this test needs more than usual.
075    conf.setInt(HConstants.REGION_SERVER_HIGH_PRIORITY_HANDLER_COUNT, 10);
076    // Set up superuser
077    SecureTestUtil.configureSuperuser(conf);
078
079    // Install the VisibilityController as a system processor
080    VisibilityTestUtil.enableVisiblityLabels(conf);
081
082    // Now, DISABLE active authorization
083    conf.setBoolean(User.HBASE_SECURITY_AUTHORIZATION_CONF_KEY, false);
084
085    TEST_UTIL.startMiniCluster();
086
087    // Wait for the labels table to become available
088    TEST_UTIL.waitUntilAllRegionsAssigned(LABELS_TABLE_NAME);
089
090    // create a set of test users
091    SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
092    USER_RW = User.createUserForTesting(conf, "rwuser", new String[0]);
093
094    // Define test labels
095    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
096      @Override
097      public Void run() throws Exception {
098        try (Connection conn = ConnectionFactory.createConnection(conf)) {
099          VisibilityClient.addLabels(conn, new String[] { SECRET, CONFIDENTIAL, PRIVATE });
100          VisibilityClient.setAuths(conn, new String[] { SECRET, CONFIDENTIAL },
101            USER_RW.getShortName());
102        } catch (Throwable t) {
103          fail("Should not have failed");
104        }
105        return null;
106      }
107    });
108  }
109
110  @AfterAll
111  public static void tearDownAfterClass() throws Exception {
112    TEST_UTIL.shutdownMiniCluster();
113  }
114
115  @Test
116  public void testManageUserAuths() throws Throwable {
117    // Even though authorization is disabled, we should be able to manage user auths
118
119    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
120      @Override
121      public Void run() throws Exception {
122        try (Connection conn = ConnectionFactory.createConnection(conf)) {
123          VisibilityClient.setAuths(conn, new String[] { SECRET, CONFIDENTIAL },
124            USER_RW.getShortName());
125        } catch (Throwable t) {
126          fail("Should not have failed");
127        }
128        return null;
129      }
130    });
131
132    PrivilegedExceptionAction<List<String>> getAuths =
133      new PrivilegedExceptionAction<List<String>>() {
134        @Override
135        public List<String> run() throws Exception {
136          GetAuthsResponse authsResponse = null;
137          try (Connection conn = ConnectionFactory.createConnection(conf)) {
138            authsResponse = VisibilityClient.getAuths(conn, USER_RW.getShortName());
139          } catch (Throwable t) {
140            fail("Should not have failed");
141          }
142          List<String> authsList = new ArrayList<>(authsResponse.getAuthList().size());
143          for (ByteString authBS : authsResponse.getAuthList()) {
144            authsList.add(Bytes.toString(authBS.toByteArray()));
145          }
146          return authsList;
147        }
148      };
149
150    List<String> authsList = SUPERUSER.runAs(getAuths);
151    assertEquals(2, authsList.size());
152    assertTrue(authsList.contains(SECRET));
153    assertTrue(authsList.contains(CONFIDENTIAL));
154
155    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
156      @Override
157      public Void run() throws Exception {
158        try (Connection conn = ConnectionFactory.createConnection(conf)) {
159          VisibilityClient.clearAuths(conn, new String[] { SECRET }, USER_RW.getShortName());
160        } catch (Throwable t) {
161          fail("Should not have failed");
162        }
163        return null;
164      }
165    });
166
167    authsList = SUPERUSER.runAs(getAuths);
168    assertEquals(1, authsList.size());
169    assertTrue(authsList.contains(CONFIDENTIAL));
170
171    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
172      @Override
173      public Void run() throws Exception {
174        try (Connection conn = ConnectionFactory.createConnection(conf)) {
175          VisibilityClient.clearAuths(conn, new String[] { CONFIDENTIAL }, USER_RW.getShortName());
176        } catch (Throwable t) {
177          fail("Should not have failed");
178        }
179        return null;
180      }
181    });
182
183    authsList = SUPERUSER.runAs(getAuths);
184    assertEquals(0, authsList.size());
185  }
186
187  @Test
188  public void testPassiveVisibility(TestInfo testInfo) throws Exception {
189    // No values should be filtered regardless of authorization if we are passive
190    try (Table t =
191      createTableAndWriteDataWithLabels(TableName.valueOf(testInfo.getTestMethod().get().getName()),
192        SECRET, PRIVATE, SECRET + "|" + CONFIDENTIAL, PRIVATE + "|" + CONFIDENTIAL)) {
193      Scan s = new Scan();
194      s.setAuthorizations(new Authorizations());
195      try (ResultScanner scanner = t.getScanner(s)) {
196        Result[] next = scanner.next(10);
197        assertEquals(4, next.length);
198      }
199      s = new Scan();
200      s.setAuthorizations(new Authorizations(SECRET));
201      try (ResultScanner scanner = t.getScanner(s)) {
202        Result[] next = scanner.next(10);
203        assertEquals(4, next.length);
204      }
205      s = new Scan();
206      s.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL));
207      try (ResultScanner scanner = t.getScanner(s)) {
208        Result[] next = scanner.next(10);
209        assertEquals(4, next.length);
210      }
211      s = new Scan();
212      s.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL, PRIVATE));
213      try (ResultScanner scanner = t.getScanner(s)) {
214        Result[] next = scanner.next(10);
215        assertEquals(4, next.length);
216      }
217    }
218  }
219
220  static Table createTableAndWriteDataWithLabels(TableName tableName, String... labelExps)
221    throws Exception {
222    List<Put> puts = new ArrayList<>(labelExps.length + 1);
223    for (int i = 0; i < labelExps.length; i++) {
224      Put put = new Put(Bytes.toBytes("row" + (i + 1)));
225      put.addColumn(TEST_FAMILY, TEST_QUALIFIER, HConstants.LATEST_TIMESTAMP, ZERO);
226      put.setCellVisibility(new CellVisibility(labelExps[i]));
227      puts.add(put);
228    }
229    Table table = TEST_UTIL.createTable(tableName, TEST_FAMILY);
230    table.put(puts);
231    return table;
232  }
233}