This is an automated email from the ASF dual-hosted git repository.
kturner pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/accumulo.git
The following commit(s) were added to refs/heads/main by this push:
new 0e52d3fc01 Updates for Accumulo Access API changes (#6053)
0e52d3fc01 is described below
commit 0e52d3fc0153e793115d7c610d5af655df26194b
Author: Keith Turner <[email protected]>
AuthorDate: Wed Jan 14 19:09:33 2026 -0500
Updates for Accumulo Access API changes (#6053)
Updates to work with changes in
https://github.com/apache/accumulo-access/pull/96.
The new core/clientImpl/access/BytesAccess class consildates all the
code to work with the new APIs and handle byte[]->String correctly.
---
.../summarizers/AuthorizationSummarizer.java | 4 +-
.../core/clientImpl/ConditionalWriterImpl.java | 6 +-
.../core/clientImpl/access/BytesAccess.java | 109 +++++++++++++++++++++
.../data/constraints/VisibilityConstraint.java | 7 +-
.../core/iterators/user/TransformingIterator.java | 9 +-
.../core/iterators/user/VisibilityFilter.java | 9 +-
.../iteratorsImpl/system/VisibilityFilter.java | 6 +-
.../accumulo/core/security/Authorizations.java | 17 ----
.../accumulo/core/security/ColumnVisibility.java | 12 ++-
.../core/security/VisibilityEvaluator.java | 10 +-
.../core/clientImpl/security/BytesAccessTest.java | 91 +++++++++++++++++
.../core/security/VisibilityEvaluatorTest.java | 38 +++++++
12 files changed, 268 insertions(+), 50 deletions(-)
diff --git
a/core/src/main/java/org/apache/accumulo/core/client/summary/summarizers/AuthorizationSummarizer.java
b/core/src/main/java/org/apache/accumulo/core/client/summary/summarizers/AuthorizationSummarizer.java
index ff60606a78..16d763da3f 100644
---
a/core/src/main/java/org/apache/accumulo/core/client/summary/summarizers/AuthorizationSummarizer.java
+++
b/core/src/main/java/org/apache/accumulo/core/client/summary/summarizers/AuthorizationSummarizer.java
@@ -24,9 +24,9 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
-import org.apache.accumulo.access.AccessExpression;
import org.apache.accumulo.core.client.admin.TableOperations;
import org.apache.accumulo.core.client.summary.CountingSummarizer;
+import org.apache.accumulo.core.clientImpl.access.BytesAccess;
import org.apache.accumulo.core.data.ArrayByteSequence;
import org.apache.accumulo.core.data.ByteSequence;
import org.apache.accumulo.core.data.Key;
@@ -82,7 +82,7 @@ public class AuthorizationSummarizer extends
CountingSummarizer<ByteSequence> {
Set<ByteSequence> auths = cache.get(vis);
if (auths == null) {
var newAuths = new HashSet<ByteSequence>();
- AccessExpression.findAuthorizations(vis.toArray(),
+ BytesAccess.findAuthorizations(vis.toArray(),
auth -> newAuths.add(new ArrayByteSequence(auth)));
cache.put(new ArrayByteSequence(vis), newAuths);
auths = newAuths;
diff --git
a/core/src/main/java/org/apache/accumulo/core/clientImpl/ConditionalWriterImpl.java
b/core/src/main/java/org/apache/accumulo/core/clientImpl/ConditionalWriterImpl.java
index 2103f90392..51739bf45e 100644
---
a/core/src/main/java/org/apache/accumulo/core/clientImpl/ConditionalWriterImpl.java
+++
b/core/src/main/java/org/apache/accumulo/core/clientImpl/ConditionalWriterImpl.java
@@ -43,7 +43,6 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import org.apache.accumulo.access.AccessEvaluator;
import org.apache.accumulo.access.InvalidAccessExpressionException;
import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
@@ -52,6 +51,7 @@ import
org.apache.accumulo.core.client.ConditionalWriterConfig;
import org.apache.accumulo.core.client.Durability;
import org.apache.accumulo.core.client.TimedOutException;
import
org.apache.accumulo.core.clientImpl.ClientTabletCache.TabletServerMutations;
+import org.apache.accumulo.core.clientImpl.access.BytesAccess;
import org.apache.accumulo.core.clientImpl.thrift.TInfo;
import org.apache.accumulo.core.clientImpl.thrift.ThriftSecurityException;
import org.apache.accumulo.core.data.ByteSequence;
@@ -97,7 +97,7 @@ public class ConditionalWriterImpl implements
ConditionalWriter {
private static final int MAX_SLEEP = 30000;
private final Authorizations auths;
- private final AccessEvaluator accessEvaluator;
+ private final BytesAccess.BytesEvaluator accessEvaluator;
private final Map<Text,Boolean> cache = Collections.synchronizedMap(new
LRUMap<>(1000));
private final ClientContext context;
private final ClientTabletCache locator;
@@ -375,7 +375,7 @@ public class ConditionalWriterImpl implements
ConditionalWriter {
this.config = config;
this.context = context;
this.auths = config.getAuthorizations();
- this.accessEvaluator =
AccessEvaluator.of(config.getAuthorizations().toAccessAuthorizations());
+ this.accessEvaluator =
BytesAccess.newEvaluator(config.getAuthorizations());
this.threadPool = context.threadPools().createScheduledExecutorService(
config.getMaxWriteThreads(), CONDITIONAL_WRITER_POOL.poolName);
this.locator = new SyncingClientTabletCache(context, tableId);
diff --git
a/core/src/main/java/org/apache/accumulo/core/clientImpl/access/BytesAccess.java
b/core/src/main/java/org/apache/accumulo/core/clientImpl/access/BytesAccess.java
new file mode 100644
index 0000000000..451c0eff3a
--- /dev/null
+++
b/core/src/main/java/org/apache/accumulo/core/clientImpl/access/BytesAccess.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.accumulo.core.clientImpl.access;
+
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+
+import org.apache.accumulo.access.Access;
+import org.apache.accumulo.access.AccessEvaluator;
+import org.apache.accumulo.core.data.ArrayByteSequence;
+import org.apache.accumulo.core.security.AuthorizationContainer;
+import org.apache.accumulo.core.security.Authorizations;
+
+/**
+ * All Accumulo Access APIs are String based. Accumulo's legacy APIs for
access control are all
+ * based on byte[]. This class maps those legacy byte[] based APIs to use the
Accumulo Access string
+ * based APIs.
+ *
+ * <p>
+ * This mapping is done by using ISO_8859_1 to convert byte[] to String and
vice versa. ISO_8859_1
+ * is used because it maps each unsigned integer in a byte array directly to
the same unsigned
+ * integer in the strings char array. This preserves the behavior of Accumulo
legacy APIs by making
+ * Accumulo Access operate on the exact same data as the legacy APIs did.
+ *
+ * <p>
+ * All transformations using ISO_8859_1 should remain completely within this
class and never escape.
+ * For example if a string escaped this class it could lead to an overall
situation where
+ * {@code new String(bytes,ISO_8859_1).getBytes(UTF_8)} accidentally happens.
+ *
+ * <p>
+ * In addition to using ISO_8859_1, an instance of Accumulo Access is created
that does no
+ * validation of authorizations. By default, Accumulo Access would reject some
chars which
+ * conceptually would reject some bytes. The legacy Accumulo APIs accepted any
bytes.
+ *
+ * <p>
+ * This class is meant to serve as a bridge between legacy behavior that
accepted any byte in an
+ * access expression and newer default behavior in Accumulo Access that only
accepts valid Unicode.
+ */
+public class BytesAccess {
+
+ private static final Access ACCESS =
+ Access.builder().authorizationValidator((auth, authChars) ->
true).build();
+
+ public static void validate(byte[] expression) {
+ ACCESS.validateExpression(new String(expression, ISO_8859_1));
+ }
+
+ public static byte[] quote(byte[] auth) {
+ return ACCESS.quote(new String(auth, ISO_8859_1)).getBytes(ISO_8859_1);
+ }
+
+ public static String quote(String auth) {
+ // TODO is this safe? does not go through a byte array
+ return ACCESS.quote(auth);
+ }
+
+ public static void findAuthorizations(byte[] expression, Consumer<byte[]>
consumer) {
+ ACCESS.findAuthorizations(new String(expression, ISO_8859_1),
+ authString -> consumer.accept(authString.getBytes(ISO_8859_1)));
+ }
+
+ public static class BytesEvaluator {
+
+ private final AccessEvaluator evaluator;
+
+ private BytesEvaluator(AccessEvaluator evaluator) {
+ this.evaluator = evaluator;
+ }
+
+ public boolean canAccess(byte[] expression) {
+ return evaluator.canAccess(new String(expression, ISO_8859_1));
+ }
+ }
+
+ public static BytesEvaluator newEvaluator(Authorizations auths) {
+ List<byte[]> bytesAuths = auths.getAuthorizations();
+ Set<String> stringAuths = new HashSet<>(bytesAuths.size());
+ for (var auth : bytesAuths) {
+ stringAuths.add(new String(auth, ISO_8859_1));
+ }
+ return new
BytesEvaluator(ACCESS.newEvaluator(ACCESS.newAuthorizations(stringAuths)));
+ }
+
+ public static BytesEvaluator newEvaluator(AuthorizationContainer
authContainer) {
+ AccessEvaluator.Authorizer authorizer = authString -> authContainer
+ .contains(new ArrayByteSequence(authString.getBytes(ISO_8859_1)));
+ return new BytesEvaluator(ACCESS.newEvaluator(authorizer));
+ }
+}
diff --git
a/core/src/main/java/org/apache/accumulo/core/data/constraints/VisibilityConstraint.java
b/core/src/main/java/org/apache/accumulo/core/data/constraints/VisibilityConstraint.java
index 2bd0fc1ec9..c9fcdc235b 100644
---
a/core/src/main/java/org/apache/accumulo/core/data/constraints/VisibilityConstraint.java
+++
b/core/src/main/java/org/apache/accumulo/core/data/constraints/VisibilityConstraint.java
@@ -24,9 +24,8 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
-import org.apache.accumulo.access.AccessEvaluator;
import org.apache.accumulo.access.InvalidAccessExpressionException;
-import org.apache.accumulo.core.data.ArrayByteSequence;
+import org.apache.accumulo.core.clientImpl.access.BytesAccess;
import org.apache.accumulo.core.data.ColumnUpdate;
import org.apache.accumulo.core.data.Mutation;
@@ -64,7 +63,7 @@ public class VisibilityConstraint implements Constraint {
ok = new HashSet<>();
}
- AccessEvaluator ve = null;
+ BytesAccess.BytesEvaluator ve = null;
for (ColumnUpdate update : updates) {
@@ -79,7 +78,7 @@ public class VisibilityConstraint implements Constraint {
if (ve == null) {
var authContainer = env.getAuthorizationsContainer();
- ve = AccessEvaluator.of(auth -> authContainer.contains(new
ArrayByteSequence(auth)));
+ ve = BytesAccess.newEvaluator(authContainer);
}
if (!ve.canAccess(cv)) {
diff --git
a/core/src/main/java/org/apache/accumulo/core/iterators/user/TransformingIterator.java
b/core/src/main/java/org/apache/accumulo/core/iterators/user/TransformingIterator.java
index c8915b1e52..c9aeb447a9 100644
---
a/core/src/main/java/org/apache/accumulo/core/iterators/user/TransformingIterator.java
+++
b/core/src/main/java/org/apache/accumulo/core/iterators/user/TransformingIterator.java
@@ -30,10 +30,9 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
-import org.apache.accumulo.access.AccessEvaluator;
-import org.apache.accumulo.access.AccessExpression;
import org.apache.accumulo.access.InvalidAccessExpressionException;
import org.apache.accumulo.core.client.IteratorSetting;
+import org.apache.accumulo.core.clientImpl.access.BytesAccess;
import org.apache.accumulo.core.conf.ConfigurationTypeHelper;
import org.apache.accumulo.core.data.ByteSequence;
import org.apache.accumulo.core.data.Key;
@@ -102,7 +101,7 @@ public abstract class TransformingIterator extends
WrappingIterator implements O
protected Collection<ByteSequence> seekColumnFamilies;
protected boolean seekColumnFamiliesInclusive;
- private AccessEvaluator ve = null;
+ private BytesAccess.BytesEvaluator ve = null;
private LRUMap<ByteSequence,Boolean> visibleCache = null;
private LRUMap<ByteSequence,Boolean> parsedVisibilitiesCache = null;
private long maxBufferSize;
@@ -118,7 +117,7 @@ public abstract class TransformingIterator extends
WrappingIterator implements O
if (scanning) {
String auths = options.get(AUTH_OPT);
if (auths != null && !auths.isEmpty()) {
- ve = AccessEvaluator.of(new
Authorizations(auths.getBytes(UTF_8)).toAccessAuthorizations());
+ ve = BytesAccess.newEvaluator(new
Authorizations(auths.getBytes(UTF_8)));
visibleCache = new LRUMap<>(100);
}
}
@@ -412,7 +411,7 @@ public abstract class TransformingIterator extends
WrappingIterator implements O
Boolean parsed = parsedVisibilitiesCache.get(visibility);
if (parsed == null) {
try {
- AccessExpression.validate(visibility.toArray());
+ BytesAccess.validate(visibility.toArray());
parsedVisibilitiesCache.put(visibility, Boolean.TRUE);
} catch (InvalidAccessExpressionException e) {
log.error("Parse error after transformation : {}", visibility);
diff --git
a/core/src/main/java/org/apache/accumulo/core/iterators/user/VisibilityFilter.java
b/core/src/main/java/org/apache/accumulo/core/iterators/user/VisibilityFilter.java
index 21a8bba586..92e5079ab6 100644
---
a/core/src/main/java/org/apache/accumulo/core/iterators/user/VisibilityFilter.java
+++
b/core/src/main/java/org/apache/accumulo/core/iterators/user/VisibilityFilter.java
@@ -23,10 +23,9 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.IOException;
import java.util.Map;
-import org.apache.accumulo.access.AccessEvaluator;
-import org.apache.accumulo.access.AccessExpression;
import org.apache.accumulo.access.InvalidAccessExpressionException;
import org.apache.accumulo.core.client.IteratorSetting;
+import org.apache.accumulo.core.clientImpl.access.BytesAccess;
import org.apache.accumulo.core.data.ArrayByteSequence;
import org.apache.accumulo.core.data.ByteSequence;
import org.apache.accumulo.core.data.Key;
@@ -45,7 +44,7 @@ import org.slf4j.LoggerFactory;
*/
public class VisibilityFilter extends Filter implements OptionDescriber {
- private AccessEvaluator accessEvaluator;
+ private BytesAccess.BytesEvaluator accessEvaluator;
protected Map<ByteSequence,Boolean> cache;
private final ArrayByteSequence testVis = new ArrayByteSequence(new byte[0]);
@@ -68,7 +67,7 @@ public class VisibilityFilter extends Filter implements
OptionDescriber {
Authorizations authObj = auths == null || auths.isEmpty() ? new
Authorizations()
: new Authorizations(auths.getBytes(UTF_8));
- this.accessEvaluator =
AccessEvaluator.of(authObj.toAccessAuthorizations());
+ this.accessEvaluator = BytesAccess.newEvaluator(authObj);
}
this.cache = new LRUMap<>(1000);
}
@@ -96,7 +95,7 @@ public class VisibilityFilter extends Filter implements
OptionDescriber {
}
final ArrayByteSequence copy = new ArrayByteSequence(testVis);
try {
- AccessExpression.validate(copy.toArray());
+ BytesAccess.validate(copy.toArray());
// cache a copy of testVis
cache.put(copy, true);
return true;
diff --git
a/core/src/main/java/org/apache/accumulo/core/iteratorsImpl/system/VisibilityFilter.java
b/core/src/main/java/org/apache/accumulo/core/iteratorsImpl/system/VisibilityFilter.java
index 2f7167cdd9..64f5725f23 100644
---
a/core/src/main/java/org/apache/accumulo/core/iteratorsImpl/system/VisibilityFilter.java
+++
b/core/src/main/java/org/apache/accumulo/core/iteratorsImpl/system/VisibilityFilter.java
@@ -18,8 +18,8 @@
*/
package org.apache.accumulo.core.iteratorsImpl.system;
-import org.apache.accumulo.access.AccessEvaluator;
import org.apache.accumulo.access.InvalidAccessExpressionException;
+import org.apache.accumulo.core.clientImpl.access.BytesAccess;
import org.apache.accumulo.core.data.ArrayByteSequence;
import org.apache.accumulo.core.data.ByteSequence;
import org.apache.accumulo.core.data.Key;
@@ -41,7 +41,7 @@ import org.slf4j.LoggerFactory;
* class.
*/
public class VisibilityFilter extends SynchronizedServerFilter {
- protected final AccessEvaluator ve;
+ protected final BytesAccess.BytesEvaluator ve;
protected final ArrayByteSequence defaultVisibility;
protected final LRUMap<ByteSequence,Boolean> cache;
protected final Authorizations authorizations;
@@ -53,7 +53,7 @@ public class VisibilityFilter extends
SynchronizedServerFilter {
private VisibilityFilter(SortedKeyValueIterator<Key,Value> iterator,
Authorizations authorizations, byte[] defaultVisibility) {
super(iterator);
- this.ve = AccessEvaluator.of(authorizations.toAccessAuthorizations());
+ this.ve = BytesAccess.newEvaluator(authorizations);
this.authorizations = authorizations;
this.defaultVisibility = new ArrayByteSequence(defaultVisibility);
this.cache = new LRUMap<>(1000);
diff --git
a/core/src/main/java/org/apache/accumulo/core/security/Authorizations.java
b/core/src/main/java/org/apache/accumulo/core/security/Authorizations.java
index c9231ace39..cb577c42ea 100644
--- a/core/src/main/java/org/apache/accumulo/core/security/Authorizations.java
+++ b/core/src/main/java/org/apache/accumulo/core/security/Authorizations.java
@@ -388,21 +388,4 @@ public class Authorizations implements Iterable<byte[]>,
Serializable, Authoriza
return sb.toString();
}
-
- /**
- * Converts to an Accumulo Access Authorizations object.
- *
- * @since 4.0.0
- */
- public org.apache.accumulo.access.Authorizations toAccessAuthorizations() {
- if (auths.isEmpty()) {
- return org.apache.accumulo.access.Authorizations.of();
- } else {
- Set<String> auths = new HashSet<>(authsList.size());
- for (var auth : authsList) {
- auths.add(new String(auth, UTF_8));
- }
- return org.apache.accumulo.access.Authorizations.of(auths);
- }
- }
}
diff --git
a/core/src/main/java/org/apache/accumulo/core/security/ColumnVisibility.java
b/core/src/main/java/org/apache/accumulo/core/security/ColumnVisibility.java
index 33a377008c..f63559938d 100644
--- a/core/src/main/java/org/apache/accumulo/core/security/ColumnVisibility.java
+++ b/core/src/main/java/org/apache/accumulo/core/security/ColumnVisibility.java
@@ -29,8 +29,10 @@ import java.util.List;
import java.util.TreeSet;
import java.util.function.Supplier;
+import org.apache.accumulo.access.Access;
import org.apache.accumulo.access.AccessExpression;
import org.apache.accumulo.access.InvalidAccessExpressionException;
+import org.apache.accumulo.core.clientImpl.access.BytesAccess;
import org.apache.accumulo.core.data.ArrayByteSequence;
import org.apache.accumulo.core.data.ByteSequence;
import org.apache.accumulo.core.util.BadArgumentException;
@@ -525,7 +527,7 @@ public class ColumnVisibility {
public ColumnVisibility(byte[] expression) {
this.expression = expression;
try {
- AccessExpression.validate(this.expression);
+ BytesAccess.validate(this.expression);
} catch (InvalidAccessExpressionException e) {
// This is thrown for compatability with the exception this class used
to throw when it parsed
// exceptions itself.
@@ -607,11 +609,11 @@ public class ColumnVisibility {
*
* @param term term to quote
* @return quoted term (unquoted if unnecessary)
- * @deprecated use {@link AccessExpression#quote(String)}
+ * @deprecated use {@link Access#quote(String)}
*/
@Deprecated(since = "4.0.0")
public static String quote(String term) {
- return AccessExpression.quote(term);
+ return BytesAccess.quote(term);
}
/**
@@ -621,10 +623,10 @@ public class ColumnVisibility {
* @param term term to quote, encoded as UTF-8 bytes
* @return quoted term (unquoted if unnecessary), encoded as UTF-8 bytes
* @see #quote(String)
- * @deprecated use {@link AccessExpression#quote(byte[])}
+ * @deprecated use {@link Access#quote(String)}
*/
@Deprecated(since = "4.0.0")
public static byte[] quote(byte[] term) {
- return AccessExpression.quote(term);
+ return BytesAccess.quote(term);
}
}
diff --git
a/core/src/main/java/org/apache/accumulo/core/security/VisibilityEvaluator.java
b/core/src/main/java/org/apache/accumulo/core/security/VisibilityEvaluator.java
index 62512d19aa..4b6fa7a2de 100644
---
a/core/src/main/java/org/apache/accumulo/core/security/VisibilityEvaluator.java
+++
b/core/src/main/java/org/apache/accumulo/core/security/VisibilityEvaluator.java
@@ -18,9 +18,8 @@
*/
package org.apache.accumulo.core.security;
-import org.apache.accumulo.access.AccessEvaluator;
import org.apache.accumulo.access.InvalidAccessExpressionException;
-import org.apache.accumulo.core.data.ArrayByteSequence;
+import org.apache.accumulo.core.clientImpl.access.BytesAccess;
/**
* A class which evaluates visibility expressions against a set of
authorizations.
@@ -29,7 +28,7 @@ import org.apache.accumulo.core.data.ArrayByteSequence;
*/
@Deprecated(since = "4.0.0")
public class VisibilityEvaluator {
- private final AccessEvaluator accessEvaluator;
+ private final BytesAccess.BytesEvaluator accessEvaluator;
/**
* Properly escapes an authorization string. The string can be quoted if
desired.
@@ -74,8 +73,7 @@ public class VisibilityEvaluator {
*/
public VisibilityEvaluator(AuthorizationContainer authsContainer) {
// TODO need to look into efficiency and correctness of this
- this.accessEvaluator =
- AccessEvaluator.of(auth -> authsContainer.contains(new
ArrayByteSequence(auth)));
+ this.accessEvaluator = BytesAccess.newEvaluator(authsContainer);
}
/**
@@ -85,7 +83,7 @@ public class VisibilityEvaluator {
* @param authorizations authorizations object
*/
public VisibilityEvaluator(Authorizations authorizations) {
- this.accessEvaluator =
AccessEvaluator.of(authorizations.toAccessAuthorizations());
+ this.accessEvaluator = BytesAccess.newEvaluator(authorizations);
}
/**
diff --git
a/core/src/test/java/org/apache/accumulo/core/clientImpl/security/BytesAccessTest.java
b/core/src/test/java/org/apache/accumulo/core/clientImpl/security/BytesAccessTest.java
new file mode 100644
index 0000000000..a753a4c4e0
--- /dev/null
+++
b/core/src/test/java/org/apache/accumulo/core/clientImpl/security/BytesAccessTest.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.accumulo.core.clientImpl.security;
+
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.accumulo.core.clientImpl.access.BytesAccess;
+import org.junit.jupiter.api.Test;
+
+public class BytesAccessTest {
+ @Test
+ public void testIso8859() {
+ // BytesAccess heavily depends on a 1:1 conversion of data from byte[] to
string when using
+ // ISO_8859_1. This test validates those assumptions.
+
+ byte[] data = new byte[256];
+ for (int i = 0; i < 256; i++) {
+ data[i] = (byte) i;
+ }
+
+ var str = new String(data, ISO_8859_1);
+
+ assertEquals(256, str.length());
+ var chars = str.toCharArray();
+ assertEquals(256, chars.length);
+ for (int i = 0; i < 256; i++) {
+ assertEquals(i, str.charAt(i));
+ assertEquals(i, chars[i]);
+ }
+
+ byte[] data2 = str.getBytes(ISO_8859_1);
+ assertArrayEquals(data, data2);
+ }
+
+ @Test
+ public void testQuote() {
+ byte[] data = new byte[256];
+ for (int i = 0; i < 256; i++) {
+ data[i] = (byte) i;
+ }
+
+ var quoted = BytesAccess.quote(data);
+ assertEquals(260, quoted.length);
+ assertEquals('"', quoted[0]);
+ assertEquals('"', quoted[259]);
+ int expected = 0;
+ for (int i = 1; i < 259; i++) {
+ if (quoted[i] == '\\') {
+ i++;
+ assertTrue('"' == quoted[i] || '\\' == quoted[i]);
+ }
+ assertEquals(expected, 0xff & quoted[i]);
+ expected++;
+ }
+ }
+
+ @Test
+ public void testFindAuths() {
+ byte[] exp = new byte[] {'"', 0, 1, '"', '&', 'A', 'B', '&', '"', 3, 4, 5,
'"'};
+
+ List<byte[]> seenAuths = new ArrayList<>();
+ BytesAccess.findAuthorizations(exp, seenAuths::add);
+
+ assertEquals(3, seenAuths.size());
+ assertArrayEquals(new byte[] {0, 1}, seenAuths.get(0));
+ assertArrayEquals(new byte[] {'A', 'B'}, seenAuths.get(1));
+ assertArrayEquals(new byte[] {3, 4, 5}, seenAuths.get(2));
+ }
+}
diff --git
a/core/src/test/java/org/apache/accumulo/core/security/VisibilityEvaluatorTest.java
b/core/src/test/java/org/apache/accumulo/core/security/VisibilityEvaluatorTest.java
index 31ae8ea3ed..aa9cb1a702 100644
---
a/core/src/test/java/org/apache/accumulo/core/security/VisibilityEvaluatorTest.java
+++
b/core/src/test/java/org/apache/accumulo/core/security/VisibilityEvaluatorTest.java
@@ -23,6 +23,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.List;
+
+import org.apache.accumulo.core.data.ArrayByteSequence;
import org.apache.accumulo.core.util.ByteArraySet;
import org.junit.jupiter.api.Test;
@@ -110,4 +113,39 @@ public class VisibilityEvaluatorTest {
ct.evaluate(new ColumnVisibility(quote("五") + "&(" + quote("四") + "|"
+ quote("三") + ")")));
assertFalse(ct.evaluate(new ColumnVisibility("\"五\"&(\"四\"|\"三\")")));
}
+
+ @Test
+ public void testBinary() throws VisibilityParseException {
+ for (int i = 0; i < 256; i++) {
+ if (i == '\\' || i == '"') {
+ // these have to be escaped
+ continue;
+ }
+
+ byte b = (byte) i;
+
+ byte[] exp1 = new byte[] {'"', b, 2, '"', '&', '"', (byte) 250, 6, '"'};
+ byte[] exp2 = new byte[] {'"', b, 2, '"', '|', '"', (byte) 250, 6, '"'};
+
+ var auths1 = new Authorizations(List.of(new byte[] {b, 2}, new byte[]
{(byte) 250, 6}));
+ VisibilityEvaluator ve1 = new VisibilityEvaluator(auths1);
+ assertTrue(ve1.evaluate(new ColumnVisibility(exp1)));
+ assertTrue(ve1.evaluate(new ColumnVisibility(exp2)));
+
+ var auths2 = new Authorizations(List.of(new byte[] {b, 2}));
+ VisibilityEvaluator ve2 = new VisibilityEvaluator(auths2);
+ assertFalse(ve2.evaluate(new ColumnVisibility(exp1)));
+ assertTrue(ve2.evaluate(new ColumnVisibility(exp2)));
+
+ var auths3 = new Authorizations(List.of(new byte[] {b, 2, 0}));
+ VisibilityEvaluator ve3 = new VisibilityEvaluator(auths3);
+ assertFalse(ve3.evaluate(new ColumnVisibility(exp1)));
+ assertFalse(ve3.evaluate(new ColumnVisibility(exp2)));
+
+ var bs = new ArrayByteSequence(new byte[] {b, 2});
+ VisibilityEvaluator ve4 = new VisibilityEvaluator(auth ->
auth.equals(bs));
+ assertFalse(ve4.evaluate(new ColumnVisibility(exp1)));
+ assertTrue(ve4.evaluate(new ColumnVisibility(exp2)));
+ }
+ }
}