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;
023
024import java.io.IOException;
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.client.Connection;
031import org.apache.hadoop.hbase.client.ConnectionFactory;
032import org.apache.hadoop.hbase.security.User;
033import org.apache.hadoop.hbase.testclassification.MediumTests;
034import org.apache.hadoop.hbase.testclassification.SecurityTests;
035import org.apache.hadoop.hbase.util.Bytes;
036import org.junit.jupiter.api.AfterAll;
037import org.junit.jupiter.api.BeforeAll;
038import org.junit.jupiter.api.Tag;
039import org.junit.jupiter.api.Test;
040
041import org.apache.hbase.thirdparty.com.google.protobuf.ByteString;
042
043import org.apache.hadoop.hbase.shaded.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse;
044import org.apache.hadoop.hbase.shaded.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse;
045
046@Tag(SecurityTests.TAG)
047@Tag(MediumTests.TAG)
048public class TestVisibilityLabelsOpWithDifferentUsersNoACL {
049
050  private static final String PRIVATE = "private";
051  private static final String CONFIDENTIAL = "confidential";
052  private static final String SECRET = "secret";
053  private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
054  private static Configuration conf;
055
056  private static User SUPERUSER;
057  private static User NORMAL_USER;
058  private static User NORMAL_USER1;
059
060  @BeforeAll
061  public static void setupBeforeClass() throws Exception {
062    // setup configuration
063    conf = TEST_UTIL.getConfiguration();
064    VisibilityTestUtil.enableVisiblityLabels(conf);
065    String currentUser = User.getCurrent().getName();
066    conf.set("hbase.superuser", "admin," + currentUser);
067    TEST_UTIL.startMiniCluster(2);
068
069    // Wait for the labels table to become available
070    TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
071    SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
072    NORMAL_USER = User.createUserForTesting(conf, "user1", new String[] {});
073    NORMAL_USER1 = User.createUserForTesting(conf, "user2", new String[] {});
074    addLabels();
075  }
076
077  @AfterAll
078  public static void tearDownAfterClass() throws Exception {
079    TEST_UTIL.shutdownMiniCluster();
080  }
081
082  @Test
083  public void testLabelsTableOpsWithDifferentUsers() throws Throwable {
084    PrivilegedExceptionAction<VisibilityLabelsResponse> action =
085      new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
086        @Override
087        public VisibilityLabelsResponse run() throws Exception {
088          try (Connection conn = ConnectionFactory.createConnection(conf)) {
089            return VisibilityClient.setAuths(conn, new String[] { CONFIDENTIAL, PRIVATE }, "user1");
090          } catch (Throwable e) {
091          }
092          return null;
093        }
094      };
095    VisibilityLabelsResponse response = SUPERUSER.runAs(action);
096    assertTrue(response.getResult(0).getException().getValue().isEmpty());
097    assertTrue(response.getResult(1).getException().getValue().isEmpty());
098
099    // Ideally this should not be allowed. this operation should fail or do nothing.
100    action = new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
101      @Override
102      public VisibilityLabelsResponse run() throws Exception {
103        try (Connection conn = ConnectionFactory.createConnection(conf)) {
104          return VisibilityClient.setAuths(conn, new String[] { CONFIDENTIAL, PRIVATE }, "user3");
105        } catch (Throwable e) {
106        }
107        return null;
108      }
109    };
110    response = NORMAL_USER1.runAs(action);
111    assertEquals("org.apache.hadoop.hbase.security.AccessDeniedException",
112      response.getResult(0).getException().getName());
113    assertEquals("org.apache.hadoop.hbase.security.AccessDeniedException",
114      response.getResult(1).getException().getName());
115
116    PrivilegedExceptionAction<GetAuthsResponse> action1 =
117      new PrivilegedExceptionAction<GetAuthsResponse>() {
118        @Override
119        public GetAuthsResponse run() throws Exception {
120          try (Connection conn = ConnectionFactory.createConnection(conf)) {
121            return VisibilityClient.getAuths(conn, "user1");
122          } catch (Throwable e) {
123          }
124          return null;
125        }
126      };
127    GetAuthsResponse authsResponse = NORMAL_USER.runAs(action1);
128    assertTrue(authsResponse.getAuthList().isEmpty());
129    authsResponse = NORMAL_USER1.runAs(action1);
130    assertTrue(authsResponse.getAuthList().isEmpty());
131    authsResponse = SUPERUSER.runAs(action1);
132    List<String> authsList = new ArrayList<>(authsResponse.getAuthList().size());
133    for (ByteString authBS : authsResponse.getAuthList()) {
134      authsList.add(Bytes.toString(authBS.toByteArray()));
135    }
136    assertEquals(2, authsList.size());
137    assertTrue(authsList.contains(CONFIDENTIAL));
138    assertTrue(authsList.contains(PRIVATE));
139
140    PrivilegedExceptionAction<VisibilityLabelsResponse> action2 =
141      new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
142        @Override
143        public VisibilityLabelsResponse run() throws Exception {
144          try (Connection conn = ConnectionFactory.createConnection(conf)) {
145            return VisibilityClient.clearAuths(conn, new String[] { CONFIDENTIAL, PRIVATE },
146              "user1");
147          } catch (Throwable e) {
148          }
149          return null;
150        }
151      };
152    response = NORMAL_USER1.runAs(action2);
153    assertEquals("org.apache.hadoop.hbase.security.AccessDeniedException",
154      response.getResult(0).getException().getName());
155    assertEquals("org.apache.hadoop.hbase.security.AccessDeniedException",
156      response.getResult(1).getException().getName());
157    response = SUPERUSER.runAs(action2);
158    assertTrue(response.getResult(0).getException().getValue().isEmpty());
159    assertTrue(response.getResult(1).getException().getValue().isEmpty());
160    authsResponse = SUPERUSER.runAs(action1);
161    assertTrue(authsResponse.getAuthList().isEmpty());
162  }
163
164  private static void addLabels() throws Exception {
165    PrivilegedExceptionAction<VisibilityLabelsResponse> action =
166      new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
167        @Override
168        public VisibilityLabelsResponse run() throws Exception {
169          String[] labels = { SECRET, CONFIDENTIAL, PRIVATE };
170          try (Connection conn = ConnectionFactory.createConnection(conf)) {
171            VisibilityClient.addLabels(conn, labels);
172          } catch (Throwable t) {
173            throw new IOException(t);
174          }
175          return null;
176        }
177      };
178    SUPERUSER.runAs(action);
179  }
180}