1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.hadoop.hbase.quotas;
18
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNull;
21 import static org.junit.Assert.fail;
22
23 import java.io.IOException;
24 import java.security.PrivilegedExceptionAction;
25 import java.util.Map;
26 import java.util.concurrent.Callable;
27 import java.util.concurrent.atomic.AtomicLong;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.conf.Configuration;
32 import org.apache.hadoop.hbase.DoNotRetryIOException;
33 import org.apache.hadoop.hbase.HBaseTestingUtility;
34 import org.apache.hadoop.hbase.TableName;
35 import org.apache.hadoop.hbase.Waiter;
36 import org.apache.hadoop.hbase.Waiter.Predicate;
37 import org.apache.hadoop.hbase.client.Admin;
38 import org.apache.hadoop.hbase.client.Connection;
39 import org.apache.hadoop.hbase.client.ConnectionFactory;
40 import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
41 import org.apache.hadoop.hbase.regionserver.HRegionServer;
42 import org.apache.hadoop.hbase.security.access.AccessControlClient;
43 import org.apache.hadoop.hbase.security.access.AccessController;
44 import org.apache.hadoop.hbase.security.access.Permission.Action;
45 import org.apache.hadoop.hbase.testclassification.MediumTests;
46 import org.apache.hadoop.security.UserGroupInformation;
47 import org.junit.AfterClass;
48 import org.junit.Before;
49 import org.junit.BeforeClass;
50 import org.junit.Rule;
51 import org.junit.Test;
52 import org.junit.experimental.categories.Category;
53 import org.junit.rules.TestName;
54
55
56
57
58 @Category(MediumTests.class)
59 public class TestSuperUserQuotaPermissions {
60 private static final Log LOG = LogFactory.getLog(TestSuperUserQuotaPermissions.class);
61 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
62
63 private static final String SUPERUSER_NAME = System.getProperty("user.name");
64 private static final UserGroupInformation SUPERUSER_UGI =
65 UserGroupInformation.createUserForTesting(SUPERUSER_NAME, new String[0]);
66 private static final String REGULARUSER_NAME = "quota_regularuser";
67 private static final UserGroupInformation REGULARUSER_UGI =
68 UserGroupInformation.createUserForTesting(REGULARUSER_NAME, new String[0]);
69 private static final AtomicLong COUNTER = new AtomicLong(0);
70
71 @Rule
72 public TestName testName = new TestName();
73 private SpaceQuotaHelperForTests helper;
74
75 @BeforeClass
76 public static void setupMiniCluster() throws Exception {
77 Configuration conf = TEST_UTIL.getConfiguration();
78
79 conf.setInt(FileSystemUtilizationChore.FS_UTILIZATION_CHORE_DELAY_KEY, 1000);
80 conf.setInt(FileSystemUtilizationChore.FS_UTILIZATION_CHORE_PERIOD_KEY, 1000);
81 conf.setInt(QuotaObserverChore.QUOTA_OBSERVER_CHORE_DELAY_KEY, 1000);
82 conf.setInt(QuotaObserverChore.QUOTA_OBSERVER_CHORE_PERIOD_KEY, 1000);
83 conf.setInt(SpaceQuotaRefresherChore.POLICY_REFRESHER_CHORE_DELAY_KEY, 1000);
84 conf.setInt(SpaceQuotaRefresherChore.POLICY_REFRESHER_CHORE_PERIOD_KEY, 1000);
85 conf.setBoolean(QuotaUtil.QUOTA_CONF_KEY, true);
86
87 conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, AccessController.class.getName());
88 conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, AccessController.class.getName());
89 conf.set(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY, AccessController.class.getName());
90 conf.setBoolean("hbase.security.exec.permission.checks", true);
91 conf.setBoolean("hbase.security.authorization", true);
92 conf.set("hbase.superuser", SUPERUSER_NAME);
93
94 TEST_UTIL.startMiniCluster(1);
95 }
96
97 @AfterClass
98 public static void tearDown() throws Exception {
99 TEST_UTIL.shutdownMiniCluster();
100 }
101
102 @Before
103 public void removeAllQuotas() throws Exception {
104 final Connection conn = TEST_UTIL.getConnection();
105 if (helper == null) {
106 helper = new SpaceQuotaHelperForTests(TEST_UTIL, testName, COUNTER);
107 }
108
109 if (!conn.getAdmin().tableExists(QuotaUtil.QUOTA_TABLE_NAME)) {
110 helper.waitForQuotaTable(conn);
111 } else {
112
113 helper.removeAllQuotas(conn);
114 assertEquals(0, helper.listNumDefinedQuotas(conn));
115 }
116 }
117
118 @Test
119 public void testSuperUserCanStillCompact() throws Exception {
120
121 final TableName tn = doAsSuperUser(new Callable<TableName>() {
122 @Override
123 public TableName call() throws Exception {
124 try (Connection conn = getConnection()) {
125 Admin admin = conn.getAdmin();
126 final TableName tn = helper.createTableWithRegions(admin, 5);
127 final long sizeLimit = 2L * SpaceQuotaHelperForTests.ONE_MEGABYTE;
128 QuotaSettings settings = QuotaSettingsFactory.limitTableSpace(
129 tn, sizeLimit, SpaceViolationPolicy.NO_WRITES_COMPACTIONS);
130 admin.setQuota(settings);
131
132 try {
133 AccessControlClient.grant(
134 conn, tn, REGULARUSER_NAME, null, null, Action.READ, Action.WRITE);
135 } catch (Throwable t) {
136 if (t instanceof Exception) {
137 throw (Exception) t;
138 }
139 throw new Exception(t);
140 }
141 return tn;
142 }
143 }
144 });
145
146
147 doAsRegularUser(new Callable<Void>() {
148 @Override
149 public Void call() throws Exception {
150 try (Connection conn = getConnection()) {
151 helper.writeData(tn, 3L * SpaceQuotaHelperForTests.ONE_MEGABYTE);
152 return null;
153 }
154 }
155 });
156
157 waitForTableToEnterQuotaViolation(tn);
158
159
160 try {
161 doAsRegularUser(new Callable<Void>() {
162 @Override
163 public Void call() throws Exception {
164 try (Connection conn = getConnection()) {
165 conn.getAdmin().majorCompact(tn);
166 return null;
167 }
168 }
169 });
170 fail("Expected an exception trying to compact a table with a quota violation");
171 } catch (DoNotRetryIOException e) {
172
173 }
174
175
176 doAsSuperUser(new Callable<Void>() {
177 @Override
178 public Void call() throws Exception {
179 try (Connection conn = getConnection()) {
180 conn.getAdmin().majorCompact(tn);
181 return null;
182 }
183 }
184 });
185 }
186
187 @Test
188 public void testSuperuserCanRemoveQuota() throws Exception {
189
190 final TableName tn = doAsSuperUser(new Callable<TableName>() {
191 @Override
192 public TableName call() throws Exception {
193 try (Connection conn = getConnection()) {
194 final Admin admin = conn.getAdmin();
195 final TableName tn = helper.createTableWithRegions(admin, 5);
196 final long sizeLimit = 2L * SpaceQuotaHelperForTests.ONE_MEGABYTE;
197 QuotaSettings settings = QuotaSettingsFactory.limitTableSpace(
198 tn, sizeLimit, SpaceViolationPolicy.NO_WRITES_COMPACTIONS);
199 admin.setQuota(settings);
200
201 try {
202 AccessControlClient.grant(
203 conn, tn, REGULARUSER_NAME, null, null, Action.READ, Action.WRITE);
204 } catch (Throwable t) {
205 if (t instanceof Exception) {
206 throw (Exception) t;
207 }
208 throw new Exception(t);
209 }
210 return tn;
211 }
212 }
213 });
214
215
216 doAsRegularUser(new Callable<Void>() {
217 @Override
218 public Void call() throws Exception {
219 try (Connection conn = getConnection()) {
220 helper.writeData(tn, 3L * SpaceQuotaHelperForTests.ONE_MEGABYTE);
221 return null;
222 }
223 }
224 });
225
226
227 waitForTableToEnterQuotaViolation(tn);
228
229
230 doAsRegularUser(new Callable<Void>() {
231 @Override
232 public Void call() throws Exception {
233 try (Connection conn = getConnection()) {
234 final Admin admin = conn.getAdmin();
235 QuotaSettings qs = QuotaSettingsFactory.removeTableSpaceLimit(tn);
236 try {
237 admin.setQuota(qs);
238 fail("Expected that an unprivileged user should not be allowed to remove a quota");
239 } catch (Exception e) {
240
241 }
242 return null;
243 }
244 }
245 });
246
247
248 doAsSuperUser(new Callable<Void>() {
249 @Override
250 public Void call() throws Exception {
251 try (Connection conn = getConnection()) {
252 final Admin admin = conn.getAdmin();
253 QuotaSettings qs = QuotaSettingsFactory.removeTableSpaceLimit(tn);
254 admin.setQuota(qs);
255 assertNull(helper.getTableSpaceQuota(conn, tn));
256 return null;
257 }
258 }
259 });
260 }
261
262 private Connection getConnection() throws IOException {
263 return ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
264 }
265
266 private <T> T doAsSuperUser(Callable<T> task) throws Exception {
267 return doAsUser(SUPERUSER_UGI, task);
268 }
269
270 private <T> T doAsRegularUser(Callable<T> task) throws Exception {
271 return doAsUser(REGULARUSER_UGI, task);
272 }
273
274 private <T> T doAsUser(UserGroupInformation ugi, final Callable<T> task) throws Exception {
275 return ugi.doAs(new PrivilegedExceptionAction<T>() {
276 public T run() throws Exception {
277 return task.call();
278 }
279 });
280 }
281
282 private void waitForTableToEnterQuotaViolation(final TableName tn) throws Exception {
283
284 final HRegionServer rs = TEST_UTIL.getHBaseCluster().getRegionServer(0);
285 Waiter.waitFor(TEST_UTIL.getConfiguration(), 30 * 1000, 1000, new Predicate<Exception>() {
286 @Override
287 public boolean evaluate() throws Exception {
288 Map<TableName,SpaceQuotaSnapshot> snapshots =
289 rs.getRegionServerSpaceQuotaManager().copyQuotaSnapshots();
290 SpaceQuotaSnapshot snapshot = snapshots.get(tn);
291 if (snapshot == null) {
292 LOG.info("Found no snapshot for " + tn);
293 return false;
294 }
295 LOG.info("Found snapshot " + snapshot);
296 return snapshot.getQuotaStatus().isInViolation();
297 }
298 });
299 }
300 }