1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.security.visibility;
19
20 import static org.apache.hadoop.hbase.TagType.VISIBILITY_TAG_TYPE;
21 import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_FAMILY;
22 import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
23 import static org.apache.hadoop.hbase.security.visibility.VisibilityUtils.SYSTEM_LABEL;
24
25 import java.io.ByteArrayOutputStream;
26 import java.io.DataOutputStream;
27 import java.io.IOException;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.HashSet;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Set;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.hadoop.conf.Configuration;
38 import org.apache.hadoop.hbase.AuthUtil;
39 import org.apache.hadoop.hbase.Cell;
40 import org.apache.hadoop.hbase.CellUtil;
41 import org.apache.hadoop.hbase.HConstants.OperationStatusCode;
42 import org.apache.hadoop.hbase.Tag;
43 import org.apache.hadoop.hbase.TagType;
44 import org.apache.hadoop.hbase.classification.InterfaceAudience;
45 import org.apache.hadoop.hbase.client.Delete;
46 import org.apache.hadoop.hbase.client.Get;
47 import org.apache.hadoop.hbase.client.HTable;
48 import org.apache.hadoop.hbase.client.Put;
49 import org.apache.hadoop.hbase.client.Result;
50 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
51 import org.apache.hadoop.hbase.regionserver.HRegion;
52 import org.apache.hadoop.hbase.regionserver.OperationStatus;
53 import org.apache.hadoop.hbase.security.Superusers;
54 import org.apache.hadoop.hbase.security.User;
55 import org.apache.hadoop.hbase.security.visibility.expression.ExpressionNode;
56 import org.apache.hadoop.hbase.security.visibility.expression.LeafExpressionNode;
57 import org.apache.hadoop.hbase.security.visibility.expression.NonLeafExpressionNode;
58 import org.apache.hadoop.hbase.security.visibility.expression.Operator;
59 import org.apache.hadoop.hbase.util.Bytes;
60
61
62
63
64
65
66
67 @InterfaceAudience.Private
68 public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelService {
69
70 private static final Log LOG = LogFactory.getLog(ExpAsStringVisibilityLabelServiceImpl.class);
71
72 private static final byte[] DUMMY_VALUE = new byte[0];
73 private static final byte STRING_SERIALIZATION_FORMAT = 2;
74 private static final Tag STRING_SERIALIZATION_FORMAT_TAG = new Tag(
75 TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE,
76 new byte[] { STRING_SERIALIZATION_FORMAT });
77 private final ExpressionParser expressionParser = new ExpressionParser();
78 private final ExpressionExpander expressionExpander = new ExpressionExpander();
79 private Configuration conf;
80 private HRegion labelsRegion;
81 private List<ScanLabelGenerator> scanLabelGenerators;
82
83 @Override
84 public OperationStatus[] addLabels(List<byte[]> labels) throws IOException {
85
86
87
88 OperationStatus[] status = new OperationStatus[labels.size()];
89 for (int i = 0; i < labels.size(); i++) {
90 status[i] = new OperationStatus(OperationStatusCode.SUCCESS);
91 }
92 return status;
93 }
94
95 @Override
96 public OperationStatus[] setAuths(byte[] user, List<byte[]> authLabels) throws IOException {
97 assert labelsRegion != null;
98 OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
99 Put p = new Put(user);
100 for (byte[] auth : authLabels) {
101 p.addImmutable(LABELS_TABLE_FAMILY, auth, DUMMY_VALUE);
102 }
103 this.labelsRegion.put(p);
104
105 for (int i = 0; i < authLabels.size(); i++) {
106 finalOpStatus[i] = new OperationStatus(OperationStatusCode.SUCCESS);
107 }
108 return finalOpStatus;
109 }
110
111 @Override
112 public OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException {
113 assert labelsRegion != null;
114 OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
115 List<String> currentAuths;
116 if (AuthUtil.isGroupPrincipal(Bytes.toString(user))) {
117 String group = AuthUtil.getGroupName(Bytes.toString(user));
118 currentAuths = this.getGroupAuths(new String[]{group}, true);
119 }
120 else {
121 currentAuths = this.getUserAuths(user, true);
122 }
123 Delete d = new Delete(user);
124 int i = 0;
125 for (byte[] authLabel : authLabels) {
126 String authLabelStr = Bytes.toString(authLabel);
127 if (currentAuths.contains(authLabelStr)) {
128 d.deleteColumns(LABELS_TABLE_FAMILY, authLabel);
129 } else {
130
131 finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE,
132 new InvalidLabelException("Label '" + authLabelStr + "' is not set for the user "
133 + Bytes.toString(user)));
134 }
135 i++;
136 }
137 this.labelsRegion.delete(d);
138
139 for (i = 0; i < authLabels.size(); i++) {
140 if (finalOpStatus[i] == null) {
141 finalOpStatus[i] = new OperationStatus(OperationStatusCode.SUCCESS);
142 }
143 }
144 return finalOpStatus;
145 }
146
147 @Override
148 @Deprecated
149 public List<String> getAuths(byte[] user, boolean systemCall) throws IOException {
150 return getUserAuths(user, systemCall);
151 }
152
153 @Override
154 public List<String> getUserAuths(byte[] user, boolean systemCall) throws IOException {
155 assert (labelsRegion != null || systemCall);
156 List<String> auths = new ArrayList<String>();
157 Get get = new Get(user);
158 List<Cell> cells = null;
159 if (labelsRegion == null) {
160 HTable table = null;
161 try {
162 table = new HTable(conf, VisibilityConstants.LABELS_TABLE_NAME);
163 Result result = table.get(get);
164 cells = result.listCells();
165 } finally {
166 if (table != null) {
167 table.close();
168 }
169 }
170 } else {
171 cells = this.labelsRegion.get(get, false);
172 }
173 if (cells != null) {
174 for (Cell cell : cells) {
175 String auth = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(),
176 cell.getQualifierLength());
177 auths.add(auth);
178 }
179 }
180 return auths;
181 }
182
183 @Override
184 public List<String> getGroupAuths(String[] groups, boolean systemCall) throws IOException {
185 assert (labelsRegion != null || systemCall);
186 List<String> auths = new ArrayList<String>();
187 if (groups != null && groups.length > 0) {
188 for (String group : groups) {
189 Get get = new Get(Bytes.toBytes(AuthUtil.toGroupEntry(group)));
190 List<Cell> cells = null;
191 if (labelsRegion == null) {
192 HTable table = null;
193 try {
194 table = new HTable(conf, VisibilityConstants.LABELS_TABLE_NAME);
195 Result result = table.get(get);
196 cells = result.listCells();
197 } finally {
198 if (table != null) {
199 table.close();
200 }
201 }
202 } else {
203 cells = this.labelsRegion.get(get, false);
204 }
205 if (cells != null) {
206 for (Cell cell : cells) {
207 String auth = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(),
208 cell.getQualifierLength());
209 auths.add(auth);
210 }
211 }
212 }
213 }
214 return auths;
215 }
216
217 @Override
218 public List<String> listLabels(String regex) throws IOException {
219
220 return new ArrayList<String>();
221 }
222
223 @Override
224 public List<Tag> createVisibilityExpTags(String visExpression, boolean withSerializationFormat,
225 boolean checkAuths) throws IOException {
226 ExpressionNode node = null;
227 try {
228 node = this.expressionParser.parse(visExpression);
229 } catch (ParseException e) {
230 throw new IOException(e);
231 }
232 node = this.expressionExpander.expand(node);
233 List<Tag> tags = new ArrayList<Tag>();
234 if (withSerializationFormat) {
235 tags.add(STRING_SERIALIZATION_FORMAT_TAG);
236 }
237 if (node instanceof NonLeafExpressionNode
238 && ((NonLeafExpressionNode) node).getOperator() == Operator.OR) {
239 for (ExpressionNode child : ((NonLeafExpressionNode) node).getChildExps()) {
240 tags.add(createTag(child));
241 }
242 } else {
243 tags.add(createTag(node));
244 }
245 return tags;
246 }
247
248 @Override
249 public VisibilityExpEvaluator getVisibilityExpEvaluator(Authorizations authorizations)
250 throws IOException {
251
252
253 if (isReadFromSystemAuthUser()) {
254 return new VisibilityExpEvaluator() {
255 @Override
256 public boolean evaluate(Cell cell) throws IOException {
257 return true;
258 }
259 };
260 }
261 List<String> authLabels = null;
262 for (ScanLabelGenerator scanLabelGenerator : scanLabelGenerators) {
263 try {
264
265 authLabels = scanLabelGenerator.getLabels(VisibilityUtils.getActiveUser(), authorizations);
266 authLabels = (authLabels == null) ? new ArrayList<String>() : authLabels;
267 authorizations = new Authorizations(authLabels);
268 } catch (Throwable t) {
269 LOG.error(t);
270 throw new IOException(t);
271 }
272 }
273 final List<String> authLabelsFinal = authLabels;
274 return new VisibilityExpEvaluator() {
275 @Override
276 public boolean evaluate(Cell cell) throws IOException {
277 boolean visibilityTagPresent = false;
278
279 if (cell.getTagsLengthUnsigned() > 0) {
280 Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
281 cell.getTagsLengthUnsigned());
282 while (tagsItr.hasNext()) {
283 boolean includeKV = true;
284 Tag tag = tagsItr.next();
285 if (tag.getType() == VISIBILITY_TAG_TYPE) {
286 visibilityTagPresent = true;
287 int offset = tag.getTagOffset();
288 int endOffset = offset + tag.getTagLength();
289 while (offset < endOffset) {
290 short len = Bytes.toShort(tag.getBuffer(), offset);
291 offset += 2;
292 if (len < 0) {
293
294 len = (short) (-1 * len);
295 String label = Bytes.toString(tag.getBuffer(), offset, len);
296 if (authLabelsFinal.contains(label)) {
297 includeKV = false;
298 break;
299 }
300 } else {
301 String label = Bytes.toString(tag.getBuffer(), offset, len);
302 if (!authLabelsFinal.contains(label)) {
303 includeKV = false;
304 break;
305 }
306 }
307 offset += len;
308 }
309 if (includeKV) {
310
311
312
313 return true;
314 }
315 }
316 }
317 }
318 return !(visibilityTagPresent);
319 }
320 };
321 }
322
323 protected boolean isReadFromSystemAuthUser() throws IOException {
324 User user = VisibilityUtils.getActiveUser();
325 return havingSystemAuth(user);
326 }
327
328 private Tag createTag(ExpressionNode node) throws IOException {
329 ByteArrayOutputStream baos = new ByteArrayOutputStream();
330 DataOutputStream dos = new DataOutputStream(baos);
331 List<String> labels = new ArrayList<String>();
332 List<String> notLabels = new ArrayList<String>();
333 extractLabels(node, labels, notLabels);
334 Collections.sort(labels);
335 Collections.sort(notLabels);
336
337
338
339
340 for (String label : notLabels) {
341 byte[] bLabel = Bytes.toBytes(label);
342 short length = (short) bLabel.length;
343 length = (short) (-1 * length);
344 dos.writeShort(length);
345 dos.write(bLabel);
346 }
347 for (String label : labels) {
348 byte[] bLabel = Bytes.toBytes(label);
349 dos.writeShort(bLabel.length);
350 dos.write(bLabel);
351 }
352 return new Tag(VISIBILITY_TAG_TYPE, baos.toByteArray());
353 }
354
355 private void extractLabels(ExpressionNode node, List<String> labels, List<String> notLabels) {
356 if (node.isSingleNode()) {
357 if (node instanceof NonLeafExpressionNode) {
358
359 LeafExpressionNode lNode = (LeafExpressionNode) ((NonLeafExpressionNode) node)
360 .getChildExps().get(0);
361 notLabels.add(lNode.getIdentifier());
362 } else {
363 labels.add(((LeafExpressionNode) node).getIdentifier());
364 }
365 } else {
366
367 NonLeafExpressionNode nlNode = (NonLeafExpressionNode) node;
368 assert nlNode.getOperator() == Operator.AND;
369 List<ExpressionNode> childExps = nlNode.getChildExps();
370 for (ExpressionNode child : childExps) {
371 extractLabels(child, labels, notLabels);
372 }
373 }
374 }
375
376 @Override
377 public Configuration getConf() {
378 return this.conf;
379 }
380
381 @Override
382 public void setConf(Configuration conf) {
383 this.conf = conf;
384 }
385
386 @Override
387 public void init(RegionCoprocessorEnvironment e) throws IOException {
388 this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf);
389 if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
390 this.labelsRegion = e.getRegion();
391 }
392 }
393
394 @Override
395 @Deprecated
396 public boolean havingSystemAuth(byte[] user) throws IOException {
397
398 if (Superusers.isSuperUser(Bytes.toString(user))) {
399 return true;
400 }
401 List<String> auths = this.getUserAuths(user, true);
402 if (LOG.isTraceEnabled()) {
403 LOG.trace("The auths for user " + Bytes.toString(user) + " are " + auths);
404 }
405 return auths.contains(SYSTEM_LABEL);
406 }
407
408 @Override
409 public boolean havingSystemAuth(User user) throws IOException {
410 if (Superusers.isSuperUser(user)) {
411 return true;
412 }
413 Set<String> auths = new HashSet<String>();
414 auths.addAll(this.getUserAuths(Bytes.toBytes(user.getShortName()), true));
415 auths.addAll(this.getGroupAuths(user.getGroupNames(), true));
416 return auths.contains(SYSTEM_LABEL);
417 }
418
419 @Override
420 public boolean matchVisibility(List<Tag> putTags, Byte putTagsFormat, List<Tag> deleteTags,
421 Byte deleteTagsFormat) throws IOException {
422 assert putTagsFormat == STRING_SERIALIZATION_FORMAT;
423 assert deleteTagsFormat == STRING_SERIALIZATION_FORMAT;
424 return checkForMatchingVisibilityTagsWithSortedOrder(putTags, deleteTags);
425 }
426
427 private static boolean checkForMatchingVisibilityTagsWithSortedOrder(List<Tag> putVisTags,
428 List<Tag> deleteVisTags) {
429 boolean matchFound = false;
430
431
432 if ((deleteVisTags.size()) == putVisTags.size()) {
433 for (Tag tag : deleteVisTags) {
434 matchFound = false;
435 for (Tag givenTag : putVisTags) {
436 if (Bytes.equals(tag.getBuffer(), tag.getTagOffset(), tag.getTagLength(),
437 givenTag.getBuffer(), givenTag.getTagOffset(), givenTag.getTagLength())) {
438 matchFound = true;
439 break;
440 }
441 }
442 if (!matchFound)
443 break;
444 }
445 }
446 return matchFound;
447 }
448
449 @Override
450 public byte[] encodeVisibilityForReplication(final List<Tag> tags, final Byte serializationFormat)
451 throws IOException {
452 if (tags.size() > 0 && (serializationFormat == null
453 || serializationFormat == STRING_SERIALIZATION_FORMAT)) {
454 return createModifiedVisExpression(tags);
455 }
456 return null;
457 }
458
459
460
461
462
463 private byte[] createModifiedVisExpression(final List<Tag> tags)
464 throws IOException {
465 StringBuilder visibilityString = new StringBuilder();
466 for (Tag tag : tags) {
467 if (tag.getType() == TagType.VISIBILITY_TAG_TYPE) {
468 if (visibilityString.length() != 0) {
469 visibilityString.append(VisibilityConstants.CLOSED_PARAN
470 + VisibilityConstants.OR_OPERATOR);
471 }
472 int offset = tag.getTagOffset();
473 int endOffset = offset + tag.getTagLength();
474 boolean expressionStart = true;
475 while (offset < endOffset) {
476 short len = Bytes.toShort(tag.getBuffer(), offset);
477 offset += 2;
478 if (len < 0) {
479 len = (short) (-1 * len);
480 String label = Bytes.toString(tag.getBuffer(), offset, len);
481 if (expressionStart) {
482 visibilityString.append(VisibilityConstants.OPEN_PARAN
483 + VisibilityConstants.NOT_OPERATOR + CellVisibility.quote(label));
484 } else {
485 visibilityString.append(VisibilityConstants.AND_OPERATOR
486 + VisibilityConstants.NOT_OPERATOR + CellVisibility.quote(label));
487 }
488 } else {
489 String label = Bytes.toString(tag.getBuffer(), offset, len);
490 if (expressionStart) {
491 visibilityString.append(VisibilityConstants.OPEN_PARAN + CellVisibility.quote(label));
492 } else {
493 visibilityString.append(VisibilityConstants.AND_OPERATOR
494 + CellVisibility.quote(label));
495 }
496 }
497 expressionStart = false;
498 offset += len;
499 }
500 }
501 }
502 if (visibilityString.length() != 0) {
503 visibilityString.append(VisibilityConstants.CLOSED_PARAN);
504
505 return Bytes.toBytes(visibilityString.toString());
506 }
507 return null;
508 }
509 }