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