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)));
+    }
+  }
 }

Reply via email to