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