1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.test;
19
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertTrue;
22
23 import java.io.IOException;
24 import java.security.PrivilegedExceptionAction;
25 import java.util.List;
26
27 import org.apache.commons.cli.CommandLine;
28 import org.apache.hadoop.conf.Configuration;
29 import org.apache.hadoop.fs.Path;
30 import org.apache.hadoop.hbase.HBaseConfiguration;
31 import org.apache.hadoop.hbase.HColumnDescriptor;
32 import org.apache.hadoop.hbase.HConstants;
33 import org.apache.hadoop.hbase.HTableDescriptor;
34 import org.apache.hadoop.hbase.IntegrationTestingUtility;
35 import org.apache.hadoop.hbase.IntegrationTests;
36 import org.apache.hadoop.hbase.TableName;
37 import org.apache.hadoop.hbase.client.HBaseAdmin;
38 import org.apache.hadoop.hbase.client.Put;
39 import org.apache.hadoop.hbase.client.Result;
40 import org.apache.hadoop.hbase.client.Scan;
41 import org.apache.hadoop.hbase.client.ScannerCallable;
42 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
43 import org.apache.hadoop.hbase.io.hfile.HFile;
44 import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
45 import org.apache.hadoop.hbase.mapreduce.TableMapper;
46 import org.apache.hadoop.hbase.mapreduce.TableRecordReaderImpl;
47 import org.apache.hadoop.hbase.security.User;
48 import org.apache.hadoop.hbase.security.visibility.Authorizations;
49 import org.apache.hadoop.hbase.security.visibility.CellVisibility;
50 import org.apache.hadoop.hbase.security.visibility.VisibilityClient;
51 import org.apache.hadoop.hbase.security.visibility.VisibilityController;
52 import org.apache.hadoop.hbase.util.AbstractHBaseTool;
53 import org.apache.hadoop.hbase.util.Bytes;
54 import org.apache.hadoop.io.BytesWritable;
55 import org.apache.hadoop.io.NullWritable;
56 import org.apache.hadoop.mapreduce.Counter;
57 import org.apache.hadoop.mapreduce.Job;
58 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
59 import org.apache.hadoop.util.ToolRunner;
60 import org.junit.experimental.categories.Category;
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 @Category(IntegrationTests.class)
77 public class IntegrationTestWithCellVisibilityLoadAndVerify extends IntegrationTestLoadAndVerify {
78 private static final char NOT = '!';
79 private static final char OR = '|';
80 private static final char AND = '&';
81 private static final String TEST_NAME = "IntegrationTestCellVisibilityLoadAndVerify";
82 private static final String CONFIDENTIAL = "confidential";
83 private static final String TOPSECRET = "topsecret";
84 private static final String SECRET = "secret";
85 private static final String PUBLIC = "public";
86 private static final String PRIVATE = "private";
87 private static final String[] LABELS = { CONFIDENTIAL, TOPSECRET, SECRET, PRIVATE, PUBLIC };
88 private static final String[] VISIBILITY_EXPS = { CONFIDENTIAL + AND + TOPSECRET + AND + PRIVATE,
89 CONFIDENTIAL + OR + TOPSECRET, PUBLIC,
90 '(' + SECRET + OR + PRIVATE + ')' + AND + NOT + CONFIDENTIAL };
91 private static final int VISIBILITY_EXPS_COUNT = VISIBILITY_EXPS.length;
92 private static final byte[] TEST_FAMILY = Bytes.toBytes("f1");
93 private static final byte[] TEST_QUALIFIER = Bytes.toBytes("q1");
94 private static final String NUM_TO_WRITE_KEY = "loadmapper.num_to_write";
95 private static final long NUM_TO_WRITE_DEFAULT = 100 * 1000;
96 private static final int SCANNER_CACHING = 500;
97
98 private long numRowsLoadedWithExp1, numRowsLoadedWithExp2, numRowsLoadWithExp3,
99 numRowsLoadWithExp4;
100 private long numRowsReadWithExp1, numRowsReadWithExp2, numRowsReadWithExp3, numRowsReadWithExp4;
101
102 private static User ADMIN;
103 private static User NORMAL_USER;
104
105 private enum Counters {
106 ROWS_VIS_EXP_1, ROWS_VIS_EXP_2, ROWS_VIS_EXP_3, ROWS_VIS_EXP_4;
107 }
108
109 @Override
110 public void setUpCluster() throws Exception {
111 util = getTestingUtil(null);
112 Configuration conf = util.getConfiguration();
113 conf.setInt(HFile.FORMAT_VERSION_KEY, 3);
114 conf.set("hbase.coprocessor.master.classes", VisibilityController.class.getName());
115 conf.set("hbase.coprocessor.region.classes", VisibilityController.class.getName());
116 String adminName = User.getCurrent().getName();
117 conf.set("hbase.superuser", adminName);
118 super.setUpCluster();
119 ADMIN = User.createUserForTesting(conf, adminName, new String[] { "supergroup" });
120 NORMAL_USER = User.createUserForTesting(conf, "user1", new String[] {});
121 addLabelsAndAuths();
122 }
123
124 private void addLabelsAndAuths() throws Exception {
125 try {
126 VisibilityClient.addLabels(util.getConfiguration(), LABELS);
127 VisibilityClient.setAuths(util.getConfiguration(), LABELS, User.getCurrent().getName());
128 VisibilityClient.setAuths(util.getConfiguration(), new String[] { CONFIDENTIAL, TOPSECRET,
129 SECRET, PRIVATE }, ADMIN.getName());
130 VisibilityClient.setAuths(util.getConfiguration(), new String[] { PUBLIC },
131 NORMAL_USER.getName());
132 } catch (Throwable t) {
133 throw new IOException(t);
134 }
135 }
136
137 public static class LoadWithCellVisibilityMapper extends LoadMapper {
138 private Counter rowsExp1, rowsExp2, rowsExp3, rowsexp4;
139
140 @Override
141 public void setup(Context context) throws IOException {
142 super.setup(context);
143 rowsExp1 = context.getCounter(Counters.ROWS_VIS_EXP_1);
144 rowsExp2 = context.getCounter(Counters.ROWS_VIS_EXP_2);
145 rowsExp3 = context.getCounter(Counters.ROWS_VIS_EXP_3);
146 rowsexp4 = context.getCounter(Counters.ROWS_VIS_EXP_4);
147 }
148
149 @Override
150 protected void map(NullWritable key, NullWritable value, Context context) throws IOException,
151 InterruptedException {
152 String suffix = "/" + shortTaskId;
153 int BLOCK_SIZE = (int) (recordsToWrite / 100);
154 for (long i = 0; i < recordsToWrite;) {
155 for (long idx = 0; idx < BLOCK_SIZE && i < recordsToWrite; idx++, i++) {
156 int expIdx = rand.nextInt(BLOCK_SIZE) % VISIBILITY_EXPS_COUNT;
157 String exp = VISIBILITY_EXPS[expIdx];
158 byte[] row = Bytes.add(Bytes.toBytes(i), Bytes.toBytes(suffix), Bytes.toBytes(exp));
159 Put p = new Put(row);
160 p.add(TEST_FAMILY, TEST_QUALIFIER, HConstants.EMPTY_BYTE_ARRAY);
161 p.setCellVisibility(new CellVisibility(exp));
162 getCounter(expIdx).increment(1);
163 table.put(p);
164
165 if (i % 100 == 0) {
166 context.setStatus("Written " + i + "/" + recordsToWrite + " records");
167 context.progress();
168 }
169 }
170
171
172 table.flushCommits();
173 }
174 }
175
176 private Counter getCounter(int idx) {
177 switch (idx) {
178 case 0:
179 return rowsExp1;
180 case 1:
181 return rowsExp2;
182 case 2:
183 return rowsExp3;
184 case 3:
185 return rowsexp4;
186 default:
187 return null;
188 }
189 }
190 }
191
192 public static class VerifyMapper extends TableMapper<BytesWritable, BytesWritable> {
193 private Counter rowsExp1, rowsExp2, rowsExp3, rowsExp4;
194
195 @Override
196 public void setup(Context context) throws IOException {
197 rowsExp1 = context.getCounter(Counters.ROWS_VIS_EXP_1);
198 rowsExp2 = context.getCounter(Counters.ROWS_VIS_EXP_2);
199 rowsExp3 = context.getCounter(Counters.ROWS_VIS_EXP_3);
200 rowsExp4 = context.getCounter(Counters.ROWS_VIS_EXP_4);
201 }
202
203 @Override
204 protected void map(ImmutableBytesWritable key, Result value, Context context)
205 throws IOException, InterruptedException {
206 byte[] row = value.getRow();
207 Counter c = getCounter(row);
208 c.increment(1);
209 }
210
211 private Counter getCounter(byte[] row) {
212 Counter c = null;
213 if (Bytes.indexOf(row, Bytes.toBytes(VISIBILITY_EXPS[0])) != -1) {
214 c = rowsExp1;
215 } else if (Bytes.indexOf(row, Bytes.toBytes(VISIBILITY_EXPS[1])) != -1) {
216 c = rowsExp2;
217 } else if (Bytes.indexOf(row, Bytes.toBytes(VISIBILITY_EXPS[2])) != -1) {
218 c = rowsExp3;
219 } else if (Bytes.indexOf(row, Bytes.toBytes(VISIBILITY_EXPS[3])) != -1) {
220 c = rowsExp4;
221 }
222 return c;
223 }
224 }
225
226 @Override
227 protected Job doLoad(Configuration conf, HTableDescriptor htd) throws Exception {
228 Job job = super.doLoad(conf, htd);
229 this.numRowsLoadedWithExp1 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_1).getValue();
230 this.numRowsLoadedWithExp2 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_2).getValue();
231 this.numRowsLoadWithExp3 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_3).getValue();
232 this.numRowsLoadWithExp4 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_4).getValue();
233 System.out.println("Rows loaded with cell visibility " + VISIBILITY_EXPS[0] + " : "
234 + this.numRowsLoadedWithExp1);
235 System.out.println("Rows loaded with cell visibility " + VISIBILITY_EXPS[1] + " : "
236 + this.numRowsLoadedWithExp2);
237 System.out.println("Rows loaded with cell visibility " + VISIBILITY_EXPS[2] + " : "
238 + this.numRowsLoadWithExp3);
239 System.out.println("Rows loaded with cell visibility " + VISIBILITY_EXPS[3] + " : "
240 + this.numRowsLoadWithExp4);
241 return job;
242 }
243
244 protected void setMapperClass(Job job) {
245 job.setMapperClass(LoadWithCellVisibilityMapper.class);
246 }
247
248 protected void doVerify(final Configuration conf, final HTableDescriptor htd) throws Exception {
249 System.out.println(String.format("Verifying for auths %s, %s, %s, %s", CONFIDENTIAL, TOPSECRET,
250 SECRET, PRIVATE));
251 PrivilegedExceptionAction<Job> scanAction = new PrivilegedExceptionAction<Job>() {
252 @Override
253 public Job run() throws Exception {
254 return doVerify(conf, htd, CONFIDENTIAL, TOPSECRET, SECRET, PRIVATE);
255 }
256 };
257 Job job = ADMIN.runAs(scanAction);
258 this.numRowsReadWithExp1 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_1).getValue();
259 this.numRowsReadWithExp2 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_2).getValue();
260 this.numRowsReadWithExp3 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_3).getValue();
261 this.numRowsReadWithExp4 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_4).getValue();
262 assertEquals(this.numRowsLoadedWithExp1, this.numRowsReadWithExp1);
263 assertEquals(this.numRowsLoadedWithExp2, this.numRowsReadWithExp2);
264 assertEquals(0, this.numRowsReadWithExp3);
265 assertEquals(0, this.numRowsReadWithExp4);
266
267
268 System.out.println(String.format("Verifying for auths %s, %s", PRIVATE, PUBLIC));
269 scanAction = new PrivilegedExceptionAction<Job>() {
270 @Override
271 public Job run() throws Exception {
272 return doVerify(conf, htd, PRIVATE, PUBLIC);
273 }
274 };
275 job = ADMIN.runAs(scanAction);
276 this.numRowsReadWithExp1 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_1).getValue();
277 this.numRowsReadWithExp2 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_2).getValue();
278 this.numRowsReadWithExp3 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_3).getValue();
279 this.numRowsReadWithExp4 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_4).getValue();
280 assertEquals(0, this.numRowsReadWithExp1);
281 assertEquals(0, this.numRowsReadWithExp2);
282 assertEquals(0, this.numRowsReadWithExp3);
283 assertEquals(this.numRowsLoadWithExp4, this.numRowsReadWithExp4);
284
285
286 System.out.println(String.format("Verifying for auths %s, %s", SECRET, PUBLIC));
287 scanAction = new PrivilegedExceptionAction<Job>() {
288 @Override
289 public Job run() throws Exception {
290 return doVerify(conf, htd, PRIVATE, PUBLIC);
291 }
292 };
293 job = NORMAL_USER.runAs(scanAction);
294 this.numRowsReadWithExp1 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_1).getValue();
295 this.numRowsReadWithExp2 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_2).getValue();
296 this.numRowsReadWithExp3 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_3).getValue();
297 this.numRowsReadWithExp4 = job.getCounters().findCounter(Counters.ROWS_VIS_EXP_4).getValue();
298 assertEquals(0, this.numRowsReadWithExp1);
299 assertEquals(0, this.numRowsReadWithExp2);
300 assertEquals(this.numRowsLoadWithExp3, this.numRowsReadWithExp3);
301 assertEquals(0, this.numRowsReadWithExp4);
302 }
303
304 private Job doVerify(Configuration conf, HTableDescriptor htd, String... auths)
305 throws IOException, InterruptedException, ClassNotFoundException {
306 Path outputDir = getTestDir(TEST_NAME, "verify-output");
307 Job job = new Job(conf);
308 job.setJarByClass(this.getClass());
309 job.setJobName(TEST_NAME + " Verification for " + htd.getTableName());
310 setJobScannerConf(job);
311 Scan scan = new Scan();
312 scan.setAuthorizations(new Authorizations(auths));
313 TableMapReduceUtil.initTableMapperJob(htd.getTableName().getNameAsString(), scan,
314 VerifyMapper.class, NullWritable.class, NullWritable.class, job);
315 TableMapReduceUtil.addDependencyJars(job.getConfiguration(), AbstractHBaseTool.class);
316 int scannerCaching = conf.getInt("verify.scannercaching", SCANNER_CACHING);
317 TableMapReduceUtil.setScannerCaching(job, scannerCaching);
318 job.setNumReduceTasks(0);
319 FileOutputFormat.setOutputPath(job, outputDir);
320 assertTrue(job.waitForCompletion(true));
321 return job;
322 }
323
324 private static void setJobScannerConf(Job job) {
325 job.getConfiguration().setBoolean(ScannerCallable.LOG_SCANNER_ACTIVITY, true);
326 long lpr = job.getConfiguration().getLong(NUM_TO_WRITE_KEY, NUM_TO_WRITE_DEFAULT) / 100;
327 job.getConfiguration().setInt(TableRecordReaderImpl.LOG_PER_ROW_COUNT, (int) lpr);
328 }
329
330 public void usage() {
331 System.err.println(this.getClass().getSimpleName() + " [-Doptions]");
332 System.err.println(" Loads a table with cell visibilities and verifies with Authorizations");
333 System.err.println("Options");
334 System.err
335 .println(" -Dloadmapper.table=<name> Table to write/verify (default autogen)");
336 System.err.println(" -Dloadmapper.num_to_write=<n> "
337 + "Number of rows per mapper (default 100,000 per mapper)");
338 System.err.println(" -Dloadmapper.numPresplits=<n> "
339 + "Number of presplit regions to start with (default 40)");
340 System.err
341 .println(" -Dloadmapper.map.tasks=<n> Number of map tasks for load (default 200)");
342 System.err.println(" -Dverify.scannercaching=<n> "
343 + "Number hbase scanner caching rows to read (default 50)");
344 }
345
346 public int runTestFromCommandLine() throws Exception {
347 IntegrationTestingUtility.setUseDistributedCluster(getConf());
348 int numPresplits = getConf().getInt("loadmapper.numPresplits", 5);
349
350 String table = getTablename();
351 HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(table));
352 htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
353
354 HBaseAdmin admin = new HBaseAdmin(getConf());
355 try {
356 admin.createTable(htd, Bytes.toBytes(0L), Bytes.toBytes(-1L), numPresplits);
357 } finally {
358 admin.close();
359 }
360 doLoad(getConf(), htd);
361 doVerify(getConf(), htd);
362 getTestingUtil(getConf()).deleteTable(htd.getName());
363 return 0;
364 }
365
366 @Override
367 protected void processOptions(CommandLine cmd) {
368 List args = cmd.getArgList();
369 if (args.size() > 0) {
370 usage();
371 throw new RuntimeException("No args expected.");
372 }
373
374 args.add("loadAndVerify");
375 super.processOptions(cmd);
376 }
377
378 public static void main(String argv[]) throws Exception {
379 Configuration conf = HBaseConfiguration.create();
380 IntegrationTestingUtility.setUseDistributedCluster(conf);
381 int ret = ToolRunner.run(conf, new IntegrationTestWithCellVisibilityLoadAndVerify(), argv);
382 System.exit(ret);
383 }
384 }