From 95583e3fce5cbcba3f139970965862f3d12aa277 Mon Sep 17 00:00:00 2001
From: "Alexandre DUVAL - @kannarfr" <kannarfr@gmail.com>
Date: Tue, 1 Feb 2022 16:07:31 +0100
Subject: [PATCH 1/8] format

---
 .../clevercloud/biscuitpulsar/AuthorizationProviderBiscuit.java  | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/main/java/com/clevercloud/biscuitpulsar/AuthorizationProviderBiscuit.java b/src/main/java/com/clevercloud/biscuitpulsar/AuthorizationProviderBiscuit.java
index cafb6ab..8c026f4 100644
--- a/src/main/java/com/clevercloud/biscuitpulsar/AuthorizationProviderBiscuit.java
+++ b/src/main/java/com/clevercloud/biscuitpulsar/AuthorizationProviderBiscuit.java
@@ -289,7 +289,6 @@ public CompletableFuture<Boolean> allowNamespacePolicyOperationAsync(NamespaceNa
 
         Verifier verifier = res.get();
         verifier.set_time();
-
         verifier.add_fact(namespaceFact(namespaceName)).get();
         verifier.add_fact(namespacePolicyOperationFact(namespaceName, policy, operation)).get();
         verifier.add_rule("right(#authority, $tenant, $namespace, $operation) <- right(#authority, #admin), namespace_operation(#ambient, $tenant, $namespace, $operation), " + policiesOperations + ".contains($operation)").get();

From c1384349c96c1adcceaacc54b1adefb3c78c33cf Mon Sep 17 00:00:00 2001
From: "Alexandre DUVAL - @kannarfr" <kannarfr@gmail.com>
Date: Tue, 1 Mar 2022 13:08:22 +0100
Subject: [PATCH 2/8] biscuit-java 2.0.0

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 224b81c..7618d99 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
     <nexus-staging-maven.version>1.6.7</nexus-staging-maven.version>
 
     <!-- dependencies -->
-    <biscuit-java.version>1.1.4</biscuit-java.version>
+    <biscuit-java.version>2.0.0-SNAPSHOT</biscuit-java.version>
     <logback-classic.version>1.2.10</logback-classic.version>
     <protobuf.version>3.16.1</protobuf.version>
     <pulsar.version>2.9.1</pulsar.version>

From 8bfc9795486cd8c9052cda896b45c240a1572275 Mon Sep 17 00:00:00 2001
From: "Alexandre DUVAL - @kannarfr" <kannarfr@gmail.com>
Date: Tue, 1 Mar 2022 13:08:43 +0100
Subject: [PATCH 3/8] bump to biscuit 2.0

---
 pom.xml                                       |   6 -
 .../AuthenticationProviderBiscuit.java        |  46 +-
 .../AuthorizationProviderBiscuit.java         | 362 +++------
 .../formatter/BiscuitFormatter.java           |  79 +-
 src/main/java/resources/log4j2.xml            |  13 -
 src/main/resources/logback.xml                |  20 +
 .../AuthenticationProviderBiscuitTest.java    |   9 +-
 .../AuthorizationProviderBiscuitTest.java     | 685 ++++--------------
 .../formatter/BiscuitFormatterTest.java       |  15 +-
 src/test/java/resources/log4j2.xml            |  13 -
 src/test/resources/logback-test.xml           |  14 +
 11 files changed, 341 insertions(+), 921 deletions(-)
 delete mode 100644 src/main/java/resources/log4j2.xml
 create mode 100644 src/main/resources/logback.xml
 delete mode 100644 src/test/java/resources/log4j2.xml
 create mode 100644 src/test/resources/logback-test.xml

diff --git a/pom.xml b/pom.xml
index 7618d99..4545a61 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,6 @@
     <logback-classic.version>1.2.10</logback-classic.version>
     <protobuf.version>3.16.1</protobuf.version>
     <pulsar.version>2.9.1</pulsar.version>
-    <slf4j-api.version>1.7.32</slf4j-api.version>
     <vavr.version>0.10.2</vavr.version>
 
     <!-- test dependencies -->
@@ -196,11 +195,6 @@
       </exclusions>
     </dependency>
 
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-api</artifactId>
-      <version>${slf4j-api.version}</version>
-    </dependency>
     <dependency>
       <groupId>ch.qos.logback</groupId>
       <artifactId>logback-classic</artifactId>
diff --git a/src/main/java/com/clevercloud/biscuitpulsar/AuthenticationProviderBiscuit.java b/src/main/java/com/clevercloud/biscuitpulsar/AuthenticationProviderBiscuit.java
index 8ef98e3..fcae746 100644
--- a/src/main/java/com/clevercloud/biscuitpulsar/AuthenticationProviderBiscuit.java
+++ b/src/main/java/com/clevercloud/biscuitpulsar/AuthenticationProviderBiscuit.java
@@ -1,9 +1,9 @@
 package com.clevercloud.biscuitpulsar;
 
+import biscuit.format.schema.Schema;
 import com.clevercloud.biscuit.crypto.PublicKey;
 import com.clevercloud.biscuit.error.Error;
 import com.clevercloud.biscuit.token.Biscuit;
-import io.vavr.control.Either;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.pulsar.broker.ServiceConfiguration;
 import org.apache.pulsar.broker.authentication.AuthenticationDataSource;
@@ -14,6 +14,9 @@
 
 import javax.naming.AuthenticationException;
 import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SignatureException;
 import java.util.Base64;
 
 public class AuthenticationProviderBiscuit implements AuthenticationProvider {
@@ -24,12 +27,10 @@ public class AuthenticationProviderBiscuit implements AuthenticationProvider {
 
     final static String BISCUIT = "token";
 
-    final static String CONF_BISCUIT_SEALING_KEY = "biscuitSealingKey";
     final static String CONF_BISCUIT_PUBLIC_ROOT_KEY = "biscuitPublicRootKey";
     final static String CONF_BISCUIT_SUPPORT_JWT = "biscuitSupportJWT";
 
-    private PublicKey rootKey;
-    static String SEALING_KEY;
+    static PublicKey rootKey;
 
     private AuthenticationProviderToken jwtAuthenticator;
     private Boolean isJWTSupported;
@@ -39,8 +40,7 @@ public void close() throws IOException {
     }
 
     public void initialize(ServiceConfiguration serviceConfiguration) throws IOException {
-        log.info("Initialize Pulsar Biscuit Authentication plugin...");
-
+        log.info("Initializing Pulsar Biscuit Authentication plugin...");
         log.info("With JWT authentication support?");
         isJWTSupported = Boolean.parseBoolean((String) serviceConfiguration.getProperty(CONF_BISCUIT_SUPPORT_JWT));
         if (isJWTSupported) {
@@ -55,13 +55,11 @@ public void initialize(ServiceConfiguration serviceConfiguration) throws IOExcep
         log.info("Biscuit authentication configuration...");
         String key = (String) serviceConfiguration.getProperty(CONF_BISCUIT_PUBLIC_ROOT_KEY);
         log.debug("Got biscuit root public key: {}", key);
-        SEALING_KEY = (String) serviceConfiguration.getProperty(CONF_BISCUIT_SEALING_KEY);
-        log.debug("Got biscuit sealing key: {}", SEALING_KEY);
         try {
-            rootKey = new PublicKey(hexStringToByteArray(key));
+            rootKey = new PublicKey(Schema.PublicKey.Algorithm.Ed25519, hexStringToByteArray(key));
             log.info("Biscuit authentication initialized.");
-        } catch (Exception e) {
-            log.error("Could not decode Biscuit root public key: {}", e);
+        } catch (Exception ex) {
+            log.error("Could not decode Biscuit root public key", ex);
             throw new IOException();
         }
     }
@@ -113,27 +111,13 @@ private static String validateBearer(final String bearer) throws AuthenticationE
         }
     }
 
-    private String parseBiscuit(final String biscuit) throws AuthenticationException {
-        log.debug("Biscuit to parse: {}", biscuit);
+    private String parseBiscuit(final String biscuitB64Url) throws AuthenticationException {
+        log.debug("Biscuit to parse: {}", biscuitB64Url);
         try {
-            Either<Error, Biscuit> deser = Biscuit.from_b64(biscuit);
-
-            if (deser.isLeft()) {
-                throw new AuthenticationException("Could not deserialize biscuit");
-            } else {
-                Biscuit realBiscuit = deser.get();
-                log.debug("Deserialized biscuit");
-
-                if (realBiscuit.check_root_key(rootKey).isLeft()) {
-                    throw new AuthenticationException("This biscuit was not generated with the expected root key");
-                }
-                log.debug("Root key is valid");
-
-                byte[] sealed = realBiscuit.seal(SEALING_KEY.getBytes()).get();
-                log.debug("Biscuit deserialized and sealed");
-                return "biscuit:" + Base64.getUrlEncoder().encodeToString(sealed);
-            }
-        } catch (IllegalArgumentException e) {
+            Biscuit.from_b64url(biscuitB64Url, rootKey);
+            log.debug("Deserialized biscuit");
+            return "biscuit:" + biscuitB64Url;
+        } catch (IllegalArgumentException | NoSuchAlgorithmException | SignatureException | InvalidKeyException | Error e) {
             throw new AuthenticationException(e.getMessage());
         }
     }
diff --git a/src/main/java/com/clevercloud/biscuitpulsar/AuthorizationProviderBiscuit.java b/src/main/java/com/clevercloud/biscuitpulsar/AuthorizationProviderBiscuit.java
index 8c026f4..849ab78 100644
--- a/src/main/java/com/clevercloud/biscuitpulsar/AuthorizationProviderBiscuit.java
+++ b/src/main/java/com/clevercloud/biscuitpulsar/AuthorizationProviderBiscuit.java
@@ -2,8 +2,8 @@
 
 import com.clevercloud.biscuit.datalog.RunLimits;
 import com.clevercloud.biscuit.error.Error;
+import com.clevercloud.biscuit.token.Authorizer;
 import com.clevercloud.biscuit.token.Biscuit;
-import com.clevercloud.biscuit.token.Verifier;
 import io.vavr.control.Either;
 import org.apache.pulsar.broker.ServiceConfiguration;
 import org.apache.pulsar.broker.authentication.AuthenticationDataSource;
@@ -17,13 +17,16 @@
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SignatureException;
 import java.time.Duration;
-import java.util.Base64;
+import java.util.HashSet;
 import java.util.Set;
+import java.util.concurrent.Callable;
 import java.util.concurrent.CompletableFuture;
 
 import static com.clevercloud.biscuitpulsar.formatter.BiscuitFormatter.*;
-import static io.vavr.API.Left;
 
 public class AuthorizationProviderBiscuit implements AuthorizationProvider {
     private static final Logger log = LoggerFactory.getLogger(AuthorizationProviderBiscuit.class);
@@ -38,7 +41,8 @@ public class AuthorizationProviderBiscuit implements AuthorizationProvider {
     private RunLimits runLimits;
 
     public AuthorizationProviderBiscuit() {
-        runLimits = new RunLimits();
+        // TODO: we increase the timeout duration due to JVM warmup, need to find a fix
+        runLimits = new RunLimits(1000, 100, Duration.ofMillis(20));
     }
 
     public AuthorizationProviderBiscuit(ServiceConfiguration conf, PulsarResources pulsarResources)
@@ -51,21 +55,6 @@ public AuthorizationProviderBiscuit(ServiceConfiguration conf, PulsarResources p
         );
     }
 
-    public Either<Error, Verifier> verifierFromBiscuit(String role) {
-        Either<Error, Biscuit> deser = Biscuit.from_sealed(
-                Base64.getUrlDecoder().decode(role.substring("biscuit:".length())),
-                AuthenticationProviderBiscuit.SEALING_KEY.getBytes()
-        );
-        if (deser.isLeft()) {
-            Error e = deser.getLeft();
-            log.error("Failed to deserialize Biscuit from sealed [{}] due to [{}].", role, e.toString());
-            return Left(e);
-        }
-
-        Biscuit token = deser.get();
-        return token.verify_sealed();
-    }
-
     @Override
     public void initialize(ServiceConfiguration conf, PulsarResources pulsarResources) throws IOException {
         this.conf = conf;
@@ -73,129 +62,77 @@ public void initialize(ServiceConfiguration conf, PulsarResources pulsarResource
         defaultProvider = new PulsarAuthorizationProvider(conf, pulsarResources);
     }
 
-    @Override
-    public CompletableFuture<Boolean> canProduceAsync(TopicName topicName, String role, AuthenticationDataSource authenticationData) {
-        if (!role.startsWith("biscuit:")) {
-            return defaultProvider.canProduceAsync(topicName, role, authenticationData);
+    private Either<Exception, Authorizer> authorizerFromBiscuitB64Url(String role) {
+        try {
+            Biscuit sealedBiscuit = Biscuit.from_b64url(role.substring("biscuit:".length()), AuthenticationProviderBiscuit.rootKey);
+            Authorizer authorizer = sealedBiscuit.authorizer();
+            return Either.right(authorizer);
+        } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | Error ex) {
+            log.error("Failed to deserialize Biscuit from sealed [{}] due to [{}].", role, ex);
+            return Either.left(ex);
         }
+    }
 
-        Either<Error, Verifier> res = verifierFromBiscuit(role);
-        if (res.isLeft()) {
-            return CompletableFuture.failedFuture(new Exception(res.getLeft().toString()));
-        }
-
-        Verifier verifier = res.get();
-        verifier.set_time();
-        verifier.add_fact(topicFact(topicName)).get();
-        verifier.add_fact(topicOperationFact(topicName, TopicOperation.PRODUCE)).get();
-        verifier.add_rule("right(#authority, $tenant, $namespace, $topic, #produce) <- right(#authority, #admin), topic_operation(#ambient, $tenant, $namespace, $topic, #produce)").get();
-        verifier.add_check("check if right(#authority," + topicFactFragment(topicName) + "," + topicOperationSymbol(TopicOperation.PRODUCE)).get();
-        verifier.allow();
-
-        Either verifierResult = verifier.verify(runLimits);
-        log.debug(verifier.print_world());
+    private CompletableFuture<Boolean> authorize(Callable<CompletableFuture<Boolean>> notBiscuitFallback, Boolean withSuperUserFallback,
+                                                 String action, String role, AuthenticationDataSource authenticationData,
+                                                 Set<String> facts, Set<String> rules, Set<String> checks) {
+        if (!role.startsWith("biscuit:")) {
+            try {
+                return notBiscuitFallback.call();
+            } catch (Exception ex) {
+                log.error("Error during notBiscuitFallback call", ex);
+                return CompletableFuture.failedFuture(ex);
+            }
+        }
+
+        return authorizerFromBiscuitB64Url(role)
+                .fold(CompletableFuture::failedFuture,
+                        authorizer -> {
+                            try {
+                                authorizer.set_time();
+                                authorizer.add_check(adminCheck);
+                                for (String fact : facts) authorizer.add_fact(fact);
+                                for (String rule : rules) authorizer.add_rule(rule);
+                                for (String check : checks) authorizer.add_check(check);
+                                authorizer.allow();
+                                log.debug(authorizer.print_world());
+                                authorizer.authorize(runLimits);
+                            } catch (Error ex) {
+                                log.debug("BiscuitAuthorization [{}] unauthorized role -> [{}]", action, role, ex);
+                                if (withSuperUserFallback) {
+                                    log.debug("Will fallback with isSuperUser check");
+                                    return isSuperUser(role, authenticationData, this.conf);
+                                } else {
+                                    log.debug("No isSuperUSer fallback, returning unauthorized.");
+                                    return CompletableFuture.completedFuture(false);
+                                }
+                            }
+                            log.debug("BiscuitAuthorization [{}] authorized.", action);
+                            return CompletableFuture.completedFuture(true);
+                        });
+    }
 
-        if (verifierResult.isLeft()) {
-            log.debug("Biscuit canProduceAsync on [{}] NOT authorized for role [{}]: {}", topicName, role, verifierResult.getLeft());
-            return isSuperUser(role, authenticationData, this.conf);
-        } else {
-            log.debug("Biscuit canProductAsync on [{}] authorized.", topicName);
-            return CompletableFuture.completedFuture(true);
-        }
+    @Override
+    public CompletableFuture<Boolean> canProduceAsync(TopicName topicName, String role, AuthenticationDataSource authenticationData) {
+        return allowTopicOperationAsync(topicName, role, TopicOperation.PRODUCE, authenticationData);
     }
 
     @Override
     public CompletableFuture<Boolean> canConsumeAsync(TopicName topicName, String role, AuthenticationDataSource authenticationData, String subscription) {
-        if (!role.startsWith("biscuit:")) {
-            return defaultProvider.canConsumeAsync(topicName, role, authenticationData, subscription);
-        }
-
-        Either<Error, Verifier> res = verifierFromBiscuit(role);
-        if (res.isLeft()) {
-            return CompletableFuture.failedFuture(new Exception(res.getLeft().toString()));
-        }
-
-        Verifier verifier = res.get();
-        verifier.set_time();
-        verifier.add_fact(topicFact(topicName)).get();
-        verifier.add_fact(topicOperationFact(topicName, TopicOperation.CONSUME)).get();
-        verifier.add_rule("right(#authority, $tenant, $namespace, $topic, #consume) <- right(#authority, #admin), topic_operation(#ambient, $tenant, $namespace, $topic, #consume)").get();
-        verifier.add_check("check if right(#authority," + topicFactFragment(topicName) + "," + topicOperationSymbol(TopicOperation.CONSUME)).get();
-        verifier.allow();
-
-        Either verifierResult = verifier.verify(runLimits);
-        log.debug(verifier.print_world());
-
-        if (verifierResult.isLeft()) {
-            log.debug("Biscuit canConsumeAsync on [{}] NOT authorized for role [{}]: {}", topicName, role, verifierResult.getLeft());
-            return isSuperUser(role, authenticationData, this.conf);
-        } else {
-            log.debug("Biscuit canConsumeAsync on [{}] authorized.", topicName);
-            return CompletableFuture.completedFuture(true);
-        }
+        return allowTopicOperationAsync(topicName, role, TopicOperation.CONSUME, authenticationData);
     }
 
     @Override
     public CompletableFuture<Boolean> canLookupAsync(TopicName topicName, String role, AuthenticationDataSource authenticationData) {
-        if (!role.startsWith("biscuit:")) {
-            return defaultProvider.canLookupAsync(topicName, role, authenticationData);
-        }
-
-        Either<Error, Verifier> res = verifierFromBiscuit(role);
-        if (res.isLeft()) {
-            return CompletableFuture.failedFuture(new Exception(res.getLeft().toString()));
-        }
-
-        Verifier verifier = res.get();
-        verifier.set_time();
-        verifier.add_fact(topicFact(topicName)).get();
-        verifier.add_fact(topicOperationFact(topicName, TopicOperation.LOOKUP)).get();
-        verifier.add_rule("right(#authority, $tenant, $namespace, $topic, #lookup) <- right(#authority, #admin), topic_operation(#ambient, $tenant, $namespace, $topic, #lookup)").get();
-        verifier.add_check("check if right(#authority," + topicFactFragment(topicName) + "," + topicOperationSymbol(TopicOperation.LOOKUP)).get();
-        verifier.allow();
-
-        Either verifierResult = verifier.verify(runLimits);
-        log.debug(verifier.print_world());
-
-        if (verifierResult.isLeft()) {
-            log.debug("Biscuit canLookupAsync on [{}] NOT authorized for role [{}]: {}", topicName, role, verifierResult.getLeft());
-            return isSuperUser(role, authenticationData, this.conf);
-        } else {
-            log.debug("Biscuit canLookupAsync on [{}] authorized.", topicName);
-            return CompletableFuture.completedFuture(true);
-        }
+        return allowTopicOperationAsync(topicName, role, TopicOperation.LOOKUP, authenticationData);
     }
 
     @Override
-    //FIXME: should be reworked
     public CompletableFuture<Boolean> allowFunctionOpsAsync(NamespaceName namespaceName, String role, AuthenticationDataSource authenticationData) {
         if (!role.startsWith("biscuit:")) {
             return defaultProvider.allowFunctionOpsAsync(namespaceName, role, authenticationData);
         }
-
-        Either<Error, Verifier> res = verifierFromBiscuit(role);
-        if (res.isLeft()) {
-            return CompletableFuture.failedFuture(new Exception(res.getLeft().toString()));
-        }
-
-        Verifier verifier = res.get();
-        verifier.set_time();
-        verifier.add_fact(namespaceFact(namespaceName)).get();
-        verifier.add_operation("functions");
-        // should we have #namespace here? why not have #topic for topic rights to be coherent?
-        verifier.add_check("check if right(#authority, #namespace," + namespaceFactFragment(namespaceName) + ",#functions)").get();
-        verifier.allow();
-
-        Either verifierResult = verifier.verify(runLimits);
-        log.debug(verifier.print_world());
-
-        if (verifierResult.isLeft()) {
-            log.debug("Biscuit allowFunctionOpsAsync on [{}] NOT authorized for role [{}]: {}", namespaceName, role, verifierResult.getLeft());
-            return isSuperUser(role, authenticationData, this.conf);
-        } else {
-            log.debug("Biscuit allowFunctionOpsAsync on [{}] authorized.", namespaceName);
-            return CompletableFuture.completedFuture(true);
-        }
+        return isSuperUser(role, authenticationData, this.conf);
     }
 
     @Override
@@ -210,172 +147,61 @@ public CompletableFuture<Boolean> allowSinkOpsAsync(NamespaceName namespaceName,
 
     @Override
     public CompletableFuture<Boolean> isSuperUser(String role, AuthenticationDataSource authenticationData, ServiceConfiguration serviceConfiguration) {
-        if (!role.startsWith("biscuit:")) {
-            return defaultProvider.isSuperUser(role, authenticationData, serviceConfiguration);
-        }
-
-        Either<Error, Verifier> res = verifierFromBiscuit(role);
-        if (res.isLeft()) {
-            return CompletableFuture.failedFuture(new Exception(res.getLeft().toString()));
-        }
-
-        Verifier verifier = res.get();
-        verifier.set_time();
-        verifier.add_check("check if right(#authority, #admin)").get();
-        verifier.allow();
-
-        Either verifierResult = verifier.verify(runLimits);
-        log.debug(verifier.print_world());
-
-        if (verifierResult.isLeft()) {
-            log.debug("Biscuit isSuperUser NOT authorized for role [{}]: {}", role, verifierResult.getLeft());
-            return CompletableFuture.completedFuture(false);
-        } else {
-            log.debug("Biscuit isSuperUser authorized.");
-            return CompletableFuture.completedFuture(true);
-        }
+        return authorize(() -> defaultProvider.isSuperUser(role, authenticationData, serviceConfiguration), false, "isSuperUser", role, authenticationData, Set.of(), Set.of(), Set.of());
     }
 
     @Override
     public CompletableFuture<Boolean> allowTenantOperationAsync(String tenantName, String role, TenantOperation operation, AuthenticationDataSource authData) {
-        if (!role.startsWith("biscuit:")) {
-            return defaultProvider.allowTenantOperationAsync(tenantName, role, operation, authData);
-        }
-
         return isSuperUser(role, authData, this.conf);
     }
 
     @Override
     public CompletableFuture<Boolean> allowNamespaceOperationAsync(NamespaceName namespaceName, String role, NamespaceOperation operation, AuthenticationDataSource authData) {
-        if (!role.startsWith("biscuit:")) {
-            return defaultProvider.allowNamespaceOperationAsync(namespaceName, role, operation, authData);
-        }
-
-        Either<Error, Verifier> res = verifierFromBiscuit(role);
-        if (res.isLeft()) {
-            return CompletableFuture.failedFuture(new Exception(res.getLeft().toString()));
-        }
-
-        Verifier verifier = res.get();
-        verifier.set_time();
-        verifier.add_fact(namespaceFact(namespaceName)).get();
-        verifier.add_fact(namespaceOperationFact(namespaceName, operation)).get();
-        verifier.add_rule("right(#authority, $tenant, $namespace, $operation) <- right(#authority, #admin), namespace_operation(#ambient, $tenant, $namespace, $operation), " + namespaceOperations + ".contains($operation)").get();
-        verifier.add_check("check if right(#authority," + namespaceFactFragment(namespaceName) + "," + namespaceOperationSymbol(operation) + ")").get();
-        verifier.allow();
-
-        Either verifierResult = verifier.verify(runLimits);
-        log.debug(verifier.print_world());
-
-        if (verifierResult.isLeft()) {
-            log.debug("Biscuit allowNamespaceOperationAsync [{}] on [{}] NOT authorized for role [{}]: {}", operation, namespaceName, role, verifierResult.getLeft());
-            return isSuperUser(role, authData, this.conf);
-        } else {
-            log.debug("Biscuit allowNamespaceOperationAsync [{}] on [{}] authorized.", operation, namespaceName);
-            return CompletableFuture.completedFuture(true);
-        }
+        Set<String> facts = Set.of(
+                namespaceFact(namespaceName),
+                namespaceOperationFact(operation)
+        );
+        Set<String> rules = Set.of("right($tenant, $namespace, $operation) <- namespace($tenant, $namespace), namespace_operation($operation), " + namespaceOperations + ".contains($operation)");
+        Set<String> checks = Set.of("check if right(" + namespaceFragment(namespaceName) + "," + namespaceOperationFragment(operation) + ")");
+        return authorize(() -> defaultProvider.allowNamespaceOperationAsync(namespaceName, role, operation, authData), true, "allowNamespaceOperationAsync(" + operation + " -> " + namespaceName + ")", role, authData, facts, rules, checks);
     }
 
     @Override
     public CompletableFuture<Boolean> allowNamespacePolicyOperationAsync(NamespaceName namespaceName, PolicyName policy, PolicyOperation operation, String role, AuthenticationDataSource authData) {
-        if (!role.startsWith("biscuit:")) {
-            return defaultProvider.allowNamespacePolicyOperationAsync(namespaceName, policy, operation, role, authData);
-        }
-
-        Either<Error, Verifier> res = verifierFromBiscuit(role);
-        if (res.isLeft()) {
-            return CompletableFuture.failedFuture(new Exception(res.getLeft().toString()));
-        }
-
-        Verifier verifier = res.get();
-        verifier.set_time();
-        verifier.add_fact(namespaceFact(namespaceName)).get();
-        verifier.add_fact(namespacePolicyOperationFact(namespaceName, policy, operation)).get();
-        verifier.add_rule("right(#authority, $tenant, $namespace, $operation) <- right(#authority, #admin), namespace_operation(#ambient, $tenant, $namespace, $operation), " + policiesOperations + ".contains($operation)").get();
-        verifier.add_check("check if right(#authority," + namespaceFactFragment(namespaceName) + "," + policyOperationFact(policy, operation) + ")").get();
-        verifier.allow();
-
-        Either verifierResult = verifier.verify(runLimits);
-        log.debug(verifier.print_world());
-
-        if (verifierResult.isLeft()) {
-            log.debug("Biscuit allowNamespacePolicyOperationAsync [{}]:[{}] on [{}] NOT authorized for role [{}]: {}", policy, operation, namespaceName, role, verifierResult.getLeft());
-            return isSuperUser(role, authData, this.conf);
-        } else {
-            log.debug("Biscuit allowNamespacePolicyOperationAsync [{}]:[{}] on [{}] authorized.", policy, operation, namespaceName);
-            return CompletableFuture.completedFuture(true);
-        }
+        Set<String> facts = Set.of(
+                namespaceFact(namespaceName),
+                namespacePolicyOperationFact(policy, operation)
+        );
+        Set<String> rules = Set.of("right($tenant, $namespace, $operation) <- namespace($tenant, $namespace), namespace_operation($operation), " + policiesOperations + ".contains($operation)");
+        Set<String> checks = Set.of("check if right(" + namespaceFragment(namespaceName) + "," + namespacePolicyOperationFragment(policy, operation) + ")");
+        return authorize(() -> defaultProvider.allowNamespacePolicyOperationAsync(namespaceName, policy, operation, role, authData), true, "allowNamespacePolicyOperationAsync(" + policy + "." + operation + " -> " + namespaceName + ")", role, authData, facts, rules, checks);
     }
 
     @Override
-    public CompletableFuture<Boolean> allowTopicOperationAsync(TopicName topicName, String role,
-                                                               TopicOperation operation,
-                                                               AuthenticationDataSource authData) {
-        if (!role.startsWith("biscuit:")) {
-            return defaultProvider.allowTopicOperationAsync(topicName, role, operation, authData);
-        }
-
-        Either<Error, Verifier> res = verifierFromBiscuit(role);
-        if (res.isLeft()) {
-            return CompletableFuture.failedFuture(new Exception(res.getLeft().toString()));
-        }
-
-        Verifier verifier = res.get();
-        verifier.set_time();
-        verifier.add_fact(topicFact(topicName)).get();
-        verifier.add_fact(topicOperationFact(topicName, operation)).get();
-
-        // if produce|consume right is authorized then we authorize lookup
+    public CompletableFuture<Boolean> allowTopicOperationAsync(TopicName topicName, String role, TopicOperation operation, AuthenticationDataSource authData) {
+        Set<String> facts = new HashSet<>(Set.of(
+                topicFact(topicName),
+                topicOperationFact(operation)
+        ));
         if (operation.equals(TopicOperation.LOOKUP)) {
-            verifier.add_fact(topicOperationFact(topicName, TopicOperation.PRODUCE)).get();
-            verifier.add_fact(topicOperationFact(topicName, TopicOperation.CONSUME)).get();
-        }
-
-        verifier.add_rule("right(#authority, $tenant, $namespace, $topic, $operation) <- right(#authority, #admin), topic_operation(#ambient, $tenant, $namespace, $topic, $operation)," + topicOperations + ".contains($operation)").get();
-        verifier.add_check("check if right( #authority," + topicFactFragment(topicName) + "," + topicOperationSymbol(operation) + ")").get();
-        verifier.allow();
-
-        Either verifierResult = verifier.verify(runLimits);
-        log.debug(verifier.print_world());
-
-        if (verifierResult.isLeft()) {
-            log.debug("Biscuit allowTopicOperationAsync [{}] on [{}] NOT authorized for role [{}]: {}", operation, topicName, role, verifierResult.getLeft());
-            return isSuperUser(role, authData, this.conf);
-        } else {
-            log.debug("Biscuit allowTopicOperationAsync [{}] on [{}] authorized.", operation, topicName);
-            return CompletableFuture.completedFuture(true);
+            // if produce|consume right is authorized then we authorize lookup
+            facts.add(topicOperationFact(TopicOperation.PRODUCE));
+            facts.add(topicOperationFact(TopicOperation.CONSUME));
         }
+        Set<String> rules = Set.of("right($tenant, $namespace, $topic, $operation) <- topic($tenant, $namespace, $topic), topic_operation($operation)," + topicOperations + ".contains($operation)");
+        Set<String> checks = Set.of("check if right(" + topicFragment(topicName) + "," + topicOperationFragment(operation) + ")");
+        return authorize(() -> defaultProvider.allowTopicOperationAsync(topicName, role, operation, authData), true, "allowTopicOperationAsync(" + operation + " -> " + topicName + ")", role, authData, facts, rules, checks);
     }
 
     @Override
     public CompletableFuture<Boolean> allowTopicPolicyOperationAsync(TopicName topicName, String role, PolicyName policy, PolicyOperation operation, AuthenticationDataSource authData) {
-        if (!role.startsWith("biscuit:")) {
-            return defaultProvider.allowTopicPolicyOperationAsync(topicName, role, policy, operation, authData);
-        }
-
-        Either<Error, Verifier> res = verifierFromBiscuit(role);
-        if (res.isLeft()) {
-            return CompletableFuture.failedFuture(new Exception(res.getLeft().toString()));
-        }
-
-        Verifier verifier = res.get();
-        verifier.set_time();
-        verifier.add_fact(topicFact(topicName)).get();
-        verifier.add_fact(topicPolicyOperationFact(topicName, policy, operation)).get();
-        verifier.add_rule("right(#authority, $tenant, $namespace, $topic, $operation) <- right(#authority, #admin), topic_operation(#ambient, $tenant, $namespace, $topic, $operation), " + policiesOperations + ".contains($operation)").get();
-        verifier.add_check("check if right(#authority," + topicFactFragment(topicName) + "," + policyOperationFact(policy, operation) + ")").get();
-        verifier.allow();
-
-        Either verifierResult = verifier.verify(runLimits);
-        log.debug(verifier.print_world());
-
-        if (verifierResult.isLeft()) {
-            log.debug("Biscuit allowTopicPolicyOperationAsync [{}]:[{}] on [{}] NOT authorized for role [{}]: {}", policy, operation, topicName, role, verifierResult.getLeft());
-            return isSuperUser(role, authData, this.conf);
-        } else {
-            log.debug("Biscuit allowTopicPolicyOperationAsync [{}]:[{}] on [{}] authorized.", policy, operation, topicName);
-            return CompletableFuture.completedFuture(true);
-        }
+        Set<String> facts = Set.of(
+                topicFact(topicName),
+                topicPolicyOperationFact(policy, operation)
+        );
+        Set<String> rules = Set.of("right($tenant, $namespace, $topic, $operation) <- topic($tenant, $namespace, $topic), topic_operation($operation), " + policiesOperations + ".contains($operation)");
+        Set<String> checks = Set.of("check if right(" + topicFragment(topicName) + "," + topicPolicyOperationFragment(policy, operation) + ")");
+        return authorize(() -> defaultProvider.allowTopicPolicyOperationAsync(topicName, role, policy, operation, authData), true, "allowTopicPolicyOperationAsync(" + policy + "." + operation + " -> " + topicName + ")", role, authData, facts, rules, checks);
     }
 
     // those management functions will be performed outside of the authorization provider
diff --git a/src/main/java/com/clevercloud/biscuitpulsar/formatter/BiscuitFormatter.java b/src/main/java/com/clevercloud/biscuitpulsar/formatter/BiscuitFormatter.java
index 0e99b94..66096b4 100644
--- a/src/main/java/com/clevercloud/biscuitpulsar/formatter/BiscuitFormatter.java
+++ b/src/main/java/com/clevercloud/biscuitpulsar/formatter/BiscuitFormatter.java
@@ -14,7 +14,7 @@
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-public class BiscuitFormatter {
+public final class BiscuitFormatter {
     /**
      * generates a string of biscuit symbols using the namespace operations
      * <p>
@@ -26,7 +26,7 @@ public class BiscuitFormatter {
     public static final String namespaceOperations =
             Arrays.stream(BiscuitNamespaceOperation.values())
                     .map(namespaceOperation -> namespaceOperation.toString().toLowerCase())
-                    .collect(Collectors.joining(",#", "[#", "]"));
+                    .collect(Collectors.joining("\",\"", "[\"", "\"]"));
     /**
      * generates a string of biscuit symbols using the policies operations
      * <p>
@@ -38,7 +38,7 @@ public class BiscuitFormatter {
     public static final String policiesOperations =
             Arrays.stream(BiscuitPolicyOperation.values())
                     .map(policyOperation -> policyOperation.toString().toLowerCase())
-                    .collect(Collectors.joining(",#", "[#", "]"));
+                    .collect(Collectors.joining("\",\"", "[\"", "\"]"));
 
     /**
      * generates a string of biscuit symbols using the topics operations
@@ -51,52 +51,79 @@ public class BiscuitFormatter {
     public static final String topicOperations =
             Arrays.stream(BiscuitTopicOperation.values())
                     .map(topicOperation -> topicOperation.toString().toLowerCase())
-                    .collect(Collectors.joining(",#", "[#", "]"));
+                    .collect(Collectors.joining("\",\"", "[\"", "\"]"));
 
-    public static String topicFactFragment(TopicName topic) {
+    public static String namespacePolicyOperationFact(PolicyName policy, PolicyOperation operation) {
+        return "namespace_operation(\"" + policy.toString().toLowerCase() + "_" + operation.toString().toLowerCase() + "\")";
+    }
+
+    public static String namespaceOperationFact(NamespaceOperation operation) {
+        return "namespace_operation(\"" + operation.toString().toLowerCase() + "\")";
+    }
+
+    public static String topicOperationFragment(TopicOperation operation) {
+        return "\"" + operation.name().toLowerCase() + "\"";
+    }
+
+    public static String topicOperationFact(TopicOperation operation) {
+        return "topic_operation(" + topicOperationFragment(operation) + ")";
+    }
+
+    public static String topicPolicyOperationFact(PolicyName policy, PolicyOperation operation) {
+        return "topic_operation(\"" + policy.toString().toLowerCase() + "_" + operation.toString().toLowerCase() + "\")";
+    }
+
+    public static String topicPolicyOperationFragment(PolicyName policy, PolicyOperation operation) {
+        return "\"" + policy.name().toLowerCase() + "_" + operation.name().toLowerCase() + "\"";
+    }
+
+    public static String topicFragment(TopicName topic) {
         return Stream.of(topic.getTenant(), topic.getNamespacePortion(), topic.getLocalName())
                 .collect(Collectors.joining("\",\"", "\"", "\""));
     }
 
-    public static String namespaceFactFragment(NamespaceName namespace) {
+    public static String topicVariableFragment(NamespaceName namespace) {
+        return "\"" + namespace.getTenant() + "\",\"" + namespace.getLocalName() + "\", $topic";
+    }
+
+    public static String namespaceFragment(NamespaceName namespace) {
         return Stream.of(namespace.getTenant(), namespace.getLocalName())
                 .collect(Collectors.joining("\",\"", "\"", "\""));
     }
 
-    public static String policyOperationFact(PolicyName policy, PolicyOperation operation) {
-        return "#" + policy.toString().toLowerCase() + "_" + operation.toString().toLowerCase();
+    public static String namespaceOperationFragment(NamespaceOperation operation) {
+        return "\"" + operation.name().toLowerCase() + "\"";
     }
 
-    public static String topicOperationSymbol(TopicOperation operation) {
-        return "#" + operation.toString().toLowerCase();
+    public static String namespacePolicyOperationFragment(PolicyName policy, PolicyOperation operation) {
+        return "\"" + policy.name().toLowerCase() + "_" + operation.name().toLowerCase() + "\"";
     }
 
-    public
-    static String namespaceOperationSymbol(NamespaceOperation operation) {
-        return "#" + operation.toString().toLowerCase();
+    public static String topicFact(TopicName topic) {
+        return "topic(" + topicFragment(topic) + ")";
     }
 
-    public static String namespacePolicyOperationFact(NamespaceName namespace, PolicyName policy, PolicyOperation operation) {
-        return "namespace_operation(#ambient," + namespaceFactFragment(namespace) + "," + policyOperationFact(policy, operation) + ")";
+    public static String topicVariableFact(NamespaceName namespace) {
+        return "topic(" + topicVariableFragment(namespace) + ")";
     }
 
-    public static String namespaceOperationFact(NamespaceName namespace, NamespaceOperation operation) {
-        return "namespace_operation(#ambient," + namespaceFactFragment(namespace) + "," + namespaceOperationSymbol(operation) + ")";
+    public static String topicVariableFact(String tenant, String namespace) {
+        return "topic(" + topicVariableFragment(NamespaceName.get(tenant + "/" + namespace)) + ")";
     }
 
-    public static String topicOperationFact(TopicName topic, TopicOperation operation) {
-        return "topic_operation(#ambient," + topicFactFragment(topic) + "," + topicOperationSymbol(operation) + ")";
+    public static String namespaceFact(NamespaceName namespace) {
+        return "namespace(\"" + namespace.getTenant() + "\",\"" + namespace.getLocalName() + "\")";
     }
 
-    public static String topicPolicyOperationFact(TopicName topic, PolicyName policy, PolicyOperation operation) {
-        return "topic_operation(#ambient," + topicFactFragment(topic) + "," + policyOperationFact(policy, operation) + ")";
+    public static String namespaceFact(String tenant, String namespace) {
+        return namespaceFact(NamespaceName.get(tenant, namespace));
     }
 
-    public static String topicFact(TopicName topic) {
-        return "topic(#ambient," + topicFactFragment(topic) + ")";
+    public static String topicOperationCheck(TopicName topic, TopicOperation operation) {
+        return "check if " + topicFact(topic) + "," + topicOperationFact(operation);
     }
 
-    public static String namespaceFact(NamespaceName namespace) {
-        return "namespace(#ambient," + namespaceFactFragment(namespace) + ")";
-    }
+    public static String adminFact = "right(\"admin\")";
+
+    public static String adminCheck = "check if " + adminFact;
 }
diff --git a/src/main/java/resources/log4j2.xml b/src/main/java/resources/log4j2.xml
deleted file mode 100644
index fc7e008..0000000
--- a/src/main/java/resources/log4j2.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Configuration status="debug">
-  <Appenders>
-    <Console name="Console" target="SYSTEM_OUT">
-      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
-    </Console>
-  </Appenders>
-  <Loggers>
-    <Root level="debug">
-      <AppenderRef ref="Console"/>
-    </Root>
-  </Loggers>
-</Configuration>
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
new file mode 100644
index 0000000..6dc986b
--- /dev/null
+++ b/src/main/resources/logback.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <layout class="ch.qos.logback.classic.PatternLayout">
+      <pattern>
+        %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
+      </pattern>
+    </layout>
+  </appender>
+
+  <logger name="com.clevercloud" level="info" additivity="false">
+    <appender-ref ref="STDOUT"/>
+  </logger>
+
+  <root level="error">
+    <appender-ref ref="STDOUT"/>
+  </root>
+
+</configuration>
\ No newline at end of file
diff --git a/src/test/java/com/clevercloud/biscuitpulsar/AuthenticationProviderBiscuitTest.java b/src/test/java/com/clevercloud/biscuitpulsar/AuthenticationProviderBiscuitTest.java
index 50ec687..4cc64b5 100644
--- a/src/test/java/com/clevercloud/biscuitpulsar/AuthenticationProviderBiscuitTest.java
+++ b/src/test/java/com/clevercloud/biscuitpulsar/AuthenticationProviderBiscuitTest.java
@@ -31,7 +31,7 @@ public void testAuthSecretKeyPair() throws Exception {
         LOGGER.info(root.toHex());
 
         LOGGER.info("ROOT PUBLICKEY");
-        LOGGER.info(hex(root.public_key().key.compress().toByteArray()));
+        LOGGER.info(hex(root.public_key().key.getAbyte()));
 
         SymbolTable symbols = Biscuit.default_symbol_table();
 
@@ -40,19 +40,18 @@ public void testAuthSecretKeyPair() throws Exception {
 
         byte[] seed = {0, 0, 0, 0};
         SecureRandom rng = new SecureRandom(seed);
-        Biscuit b = Biscuit.make(rng, root, Biscuit.default_symbol_table(), authority_builder.build()).get();
+        Biscuit b = Biscuit.make(rng, root, Biscuit.default_symbol_table(), authority_builder.build());
 
         AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
 
         Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
+        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.getAbyte()));
 
         ServiceConfiguration conf = new ServiceConfiguration();
         conf.setProperties(properties);
         provider.initialize(conf);
 
-        String biscuit = b.serialize_b64().get();
+        String biscuit = b.serialize_b64url();
 
         String subject = provider.authenticate(new AuthenticationDataSource() {
             @Override
diff --git a/src/test/java/com/clevercloud/biscuitpulsar/AuthorizationProviderBiscuitTest.java b/src/test/java/com/clevercloud/biscuitpulsar/AuthorizationProviderBiscuitTest.java
index 6b9b468..c67c782 100644
--- a/src/test/java/com/clevercloud/biscuitpulsar/AuthorizationProviderBiscuitTest.java
+++ b/src/test/java/com/clevercloud/biscuitpulsar/AuthorizationProviderBiscuitTest.java
@@ -2,9 +2,9 @@
 
 import com.clevercloud.biscuit.crypto.KeyPair;
 import com.clevercloud.biscuit.datalog.SymbolTable;
+import com.clevercloud.biscuit.error.Error;
 import com.clevercloud.biscuit.token.Biscuit;
 import com.clevercloud.biscuit.token.builder.Block;
-import com.clevercloud.biscuit.token.builder.Check;
 import org.apache.pulsar.broker.ServiceConfiguration;
 import org.apache.pulsar.broker.authentication.AuthenticationDataSource;
 import org.apache.pulsar.common.naming.NamespaceName;
@@ -19,46 +19,30 @@
 
 import javax.naming.AuthenticationException;
 import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
-import java.time.Instant;
-import java.util.Arrays;
-import java.util.Date;
+import java.security.SignatureException;
 import java.util.Properties;
-import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 
 import static com.clevercloud.biscuit.crypto.TokenSignature.hex;
-import static com.clevercloud.biscuit.token.builder.Utils.*;
-import static org.junit.Assert.*;
+import static com.clevercloud.biscuitpulsar.formatter.BiscuitFormatter.*;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 public class AuthorizationProviderBiscuitTest {
-    private static final Logger log = LoggerFactory.getLogger(AuthorizationProviderBiscuitTest.class);
-
-    @Test
-    public void testProduceAndNotConsumeOnTopic() throws Exception {
-        SecureRandom rng = new SecureRandom();
-        KeyPair root = new KeyPair(rng);
-        SymbolTable symbols = Biscuit.default_symbol_table();
-
-        String tenant = "tenantTest";
-        String namespace = "namespaceTest";
-        String topic = "topicTest";
-
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_rule(rule("right",
-                Arrays.asList(s("authority"), string(tenant), string(namespace), string(topic), s("produce")),
-                Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), string(topic), s("produce"))))));
-        Biscuit biscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
+    static final Logger log = LoggerFactory.getLogger(AuthorizationProviderBiscuitTest.class);
 
+    private String authedBiscuit(KeyPair root, Biscuit biscuit) throws IOException, AuthenticationException {
         AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
         Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
+        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.getAbyte()));
         ServiceConfiguration conf = new ServiceConfiguration();
         conf.setProperties(properties);
         provider.initialize(conf);
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
+
+        return provider.authenticate(new AuthenticationDataSource() {
             @Override
             public boolean hasDataFromCommand() {
                 return true;
@@ -66,10 +50,32 @@ public boolean hasDataFromCommand() {
 
             @Override
             public String getCommandData() {
-                return biscuit.serialize_b64().get();
+                try {
+                    return biscuit.serialize_b64url();
+                } catch (Error.FormatError.SerializationError e) {
+                    log.error("Can't deserialize biscuit due to: {}", e.getMessage());
+                    return "";
+                }
             }
         });
+    }
+
+    @Test
+    public void testProduceAndNotConsumeOnTopic() throws Exception {
+        SecureRandom rng = new SecureRandom();
+        KeyPair root = new KeyPair(rng);
+        SymbolTable symbols = Biscuit.default_symbol_table();
 
+        String tenant = "tenantTest";
+        String namespace = "namespaceTest";
+        String topic = "topicTest";
+
+        Block block0 = new Block(0, symbols);
+        block0.add_fact(adminFact);
+        block0.add_check(topicOperationCheck(TopicName.get(tenant + "/" + namespace + "/" + topic), TopicOperation.PRODUCE));
+        Biscuit biscuit = Biscuit.make(rng, root, symbols, block0.build());
+
+        String authedBiscuit = authedBiscuit(root, biscuit);
         AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
         log.debug(biscuit.print());
         assertTrue(authorizationProvider.allowTopicOperation(TopicName.get(tenant + "/" + namespace + "/" + topic), authedBiscuit, TopicOperation.PRODUCE, null));
@@ -86,39 +92,15 @@ public void testProduceAndNotConsumeAttenuatedOnTopic() throws Exception {
         String namespace = "namespaceTest";
         String topic = "topicTest";
 
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_fact(fact("revocation_id", Arrays.asList(date(Date.from(Instant.now())))));
-        authority_builder.add_fact(fact("right", Arrays.asList(s("authority"), s("admin"))));
-        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
+        Block block0 = new Block(0, symbols);
+        block0.add_fact(adminFact);
+        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, block0.build());
 
-        Block block = rootBiscuit.create_block();
-        block.add_check(new Check(Arrays.asList(
-                rule("limited_right",
-                        Arrays.asList(string(tenant), string(namespace), string(topic), s("produce")),
-                        Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), string(topic), s("produce")))))
-        )));
-
-        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block.build()).get();
-
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
-        Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
-        ServiceConfiguration conf = new ServiceConfiguration();
-        conf.setProperties(properties);
-        provider.initialize(conf);
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
-
-            @Override
-            public String getCommandData() {
-                return biscuit.serialize_b64().get();
-            }
-        });
+        Block block1 = rootBiscuit.create_block();
+        block1.add_check(topicOperationCheck(TopicName.get(tenant + "/" + namespace + "/" + topic), TopicOperation.PRODUCE));
+        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block1.build());
 
+        String authedBiscuit = authedBiscuit(root, biscuit);
         AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
         log.debug(biscuit.print());
         assertTrue(authorizationProvider.allowTopicOperation(TopicName.get(tenant + "/" + namespace + "/" + topic), authedBiscuit, TopicOperation.PRODUCE, null));
@@ -135,31 +117,12 @@ public void testConsumeAndNotProduceOnTopic() throws Exception {
         String namespace = "namespaceTest";
         String topic = "topicTest";
 
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_rule(rule("right",
-                Arrays.asList(s("authority"), string(tenant), string(namespace), string(topic), s("consume")),
-                Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), string(topic), s("consume"))))));
-        Biscuit biscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
-
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
-        Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
-        ServiceConfiguration conf = new ServiceConfiguration();
-        conf.setProperties(properties);
-        provider.initialize(conf);
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
-
-            @Override
-            public String getCommandData() {
-                return biscuit.serialize_b64().get();
-            }
-        });
+        Block block0 = new Block(0, symbols);
+        block0.add_fact(adminFact);
+        block0.add_check(topicOperationCheck(TopicName.get(tenant + "/" + namespace + "/" + topic), TopicOperation.CONSUME));
+        Biscuit biscuit = Biscuit.make(rng, root, symbols, block0.build());
 
+        String authedBiscuit = authedBiscuit(root, biscuit);
         AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
         log.debug(biscuit.print());
         assertFalse(authorizationProvider.allowTopicOperation(TopicName.get(tenant + "/" + namespace + "/" + topic), authedBiscuit, TopicOperation.PRODUCE, null));
@@ -176,41 +139,16 @@ public void testConsumerAndNotProduceAttenuatedOnTopic() throws Exception {
         String namespace = "namespaceTest";
         String topic = "topicTest";
 
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_fact(fact("revocation_id", Arrays.asList(date(Date.from(Instant.now())))));
-        authority_builder.add_fact(fact("right", Arrays.asList(s("authority"), s("admin"))));
-        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
+        Block block0 = new Block(0, symbols);
+        block0.add_fact(adminFact);
+        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, block0.build());
 
-        Block block = rootBiscuit.create_block();
-        block.add_check(new Check(Arrays.asList(
-                rule("limited_right",
-                        Arrays.asList(string(tenant), string(namespace), string(topic), s("consume")),
-                        Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), string(topic), s("consume")))))
-        )));
-
-        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block.build()).get();
-
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
-        Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
-        ServiceConfiguration conf = new ServiceConfiguration();
-        conf.setProperties(properties);
-        provider.initialize(conf);
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
-
-            @Override
-            public String getCommandData() {
-                return biscuit.serialize_b64().get();
-            }
-        });
+        Block block1 = rootBiscuit.create_block();
+        block1.add_check(topicOperationCheck(TopicName.get(tenant + "/" + namespace + "/" + topic), TopicOperation.CONSUME));
+        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block1.build());
 
+        String authedBiscuit = authedBiscuit(root, biscuit);
         AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
-        log.debug(biscuit.print());
         assertFalse(authorizationProvider.allowTopicOperation(TopicName.get(tenant + "/" + namespace + "/" + topic), authedBiscuit, TopicOperation.PRODUCE, null));
         assertTrue(authorizationProvider.allowTopicOperation(TopicName.get(tenant + "/" + namespace + "/" + topic), authedBiscuit, TopicOperation.CONSUME, null));
     }
@@ -224,31 +162,12 @@ public void testTopicCreation() throws Exception {
         String tenant = "tenantTest";
         String namespace = "namespaceTest";
 
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_rule(rule("right",
-                Arrays.asList(s("authority"), string(tenant), string(namespace), s("create_topic")),
-                Arrays.asList(pred("namespace_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), s("create_topic"))))));
-        Biscuit biscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
-
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
-        Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
-        ServiceConfiguration conf = new ServiceConfiguration();
-        conf.setProperties(properties);
-        provider.initialize(conf);
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
-
-            @Override
-            public String getCommandData() {
-                return biscuit.serialize_b64().get();
-            }
-        });
+        Block block0 = new Block(0, symbols);
+        block0.add_fact(adminFact);
+        block0.add_rule("right($tenant, $namespace, \"create_topic\") <- namespace($tenant, $namespace), namespace_operation(\"create_topic\")");
+        Biscuit biscuit = Biscuit.make(rng, root, symbols, block0.build());
 
+        String authedBiscuit = authedBiscuit(root, biscuit);
         AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
         log.debug(biscuit.print());
         assertTrue(authorizationProvider.allowNamespaceOperation(NamespaceName.get(tenant + "/" + namespace), authedBiscuit, NamespaceOperation.CREATE_TOPIC, null));
@@ -263,40 +182,14 @@ public void testReadWriteTopicInNamespace() throws Exception {
         String tenant = "tenantTest";
         String namespace = "namespaceTest";
 
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_rule(rule("right",
-                Arrays.asList(s("authority"), string(tenant), string(namespace), s("create_topic")),
-                Arrays.asList(pred("namespace_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), s("create_topic"))))));
-        authority_builder.add_rule(rule("right",
-                Arrays.asList(s("authority"), string(tenant), string(namespace), var("2"), s("produce")),
-                Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), var("2"), s("produce"))))));
-        authority_builder.add_rule(rule("right",
-                Arrays.asList(s("authority"), string(tenant), string(namespace), var("2"), s("consume")),
-                Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), var("2"), s("consume"))))));
-        Biscuit biscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
-
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
-        Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
-        ServiceConfiguration conf = new ServiceConfiguration();
-        conf.setProperties(properties);
-        provider.initialize(conf);
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
-
-            @Override
-            public String getCommandData() {
-                return biscuit.serialize_b64().get();
-            }
-        });
+        Block block0 = new Block(0, symbols);
+        block0.add_fact(adminFact);
+        block0.add_check("check if " + topicVariableFact(NamespaceName.get(tenant + "/" + namespace)) + ", topic_operation($operation), [\"produce\",\"consume\"].contains($operation)");
+        Biscuit biscuit = Biscuit.make(rng, root, symbols, block0.build());
 
+        String authedBiscuit = authedBiscuit(root, biscuit);
         log.debug(biscuit.print());
         AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
-        assertTrue(authorizationProvider.allowNamespaceOperation(NamespaceName.get(tenant + "/" + namespace), authedBiscuit, NamespaceOperation.CREATE_TOPIC, null));
         assertTrue(authorizationProvider.allowTopicOperation(TopicName.get(tenant + "/" + namespace + "/" + "test"), authedBiscuit, TopicOperation.CONSUME, null));
         assertTrue(authorizationProvider.allowTopicOperation(TopicName.get(tenant + "/" + namespace + "/" + "test123"), authedBiscuit, TopicOperation.CONSUME, null));
         assertTrue(authorizationProvider.allowTopicOperation(TopicName.get(tenant + "/" + namespace + "/" + "test"), authedBiscuit, TopicOperation.PRODUCE, null));
@@ -304,7 +197,7 @@ public String getCommandData() {
     }
 
     @Test
-    public void testTopicOperation() throws IOException, AuthenticationException, ExecutionException, InterruptedException {
+    public void testTopicOperation() throws IOException, AuthenticationException, ExecutionException, InterruptedException, Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
         SecureRandom rng = new SecureRandom();
         KeyPair root = new KeyPair(rng);
         SymbolTable symbols = Biscuit.default_symbol_table();
@@ -312,42 +205,15 @@ public void testTopicOperation() throws IOException, AuthenticationException, Ex
         String tenant = "tenantTest";
         String namespace = "namespaceTest";
 
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_fact(fact("revocation_id", Arrays.asList(date(Date.from(Instant.now())))));
-        authority_builder.add_fact(fact("right", Arrays.asList(s("authority"), s("admin"))));
-        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
-
-        Block block = rootBiscuit.create_block();
-        block.add_check(new Check(Arrays.asList(
-                rule("limited_right",
-                        Arrays.asList(string(tenant), string(namespace), var("2"), var("3")),
-                        Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), var("2"), var("3"))))),
-                rule("limited_right",
-                        Arrays.asList(string(tenant), string(namespace), var("2")),
-                        Arrays.asList(pred("namespace_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), var("2")))))
-        )));
+        Block block0 = new Block(0, symbols);
+        block0.add_fact(adminFact);
+        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, block0.build());
 
-        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block.build()).get();
-
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
-        Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
-        ServiceConfiguration conf = new ServiceConfiguration();
-        conf.setProperties(properties);
-        provider.initialize(conf);
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
-
-            @Override
-            public String getCommandData() {
-                return biscuit.serialize_b64().get();
-            }
-        });
+        Block block1 = rootBiscuit.create_block();
+        block1.add_check("check if " + topicVariableFact(tenant, namespace) + ", topic_operation($4)");
+        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block1.build());
 
+        String authedBiscuit = authedBiscuit(root, biscuit);
         log.debug(biscuit.print());
         AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
         assertTrue(authorizationProvider.allowTopicOperationAsync(TopicName.get(tenant + "/" + namespace + "/" + "test"), authedBiscuit, TopicOperation.PRODUCE, null).get());
@@ -355,7 +221,7 @@ public String getCommandData() {
     }
 
     @Test
-    public void testNsLimitations() throws Exception {
+    public void testLimitations() throws Exception {
         SecureRandom rng = new SecureRandom();
         KeyPair root = new KeyPair(rng);
         SymbolTable symbols = Biscuit.default_symbol_table();
@@ -363,41 +229,15 @@ public void testNsLimitations() throws Exception {
         String tenant = "tenantTest";
         String namespace = "namespaceTest";
 
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_fact(fact("revocation_id", Arrays.asList(date(Date.from(Instant.now())))));
-        authority_builder.add_fact(fact("right", Arrays.asList(s("authority"), s("admin"))));
-        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
-
-        Block block = rootBiscuit.create_block();
-        block.add_check(new Check(Arrays.asList(
-                rule("limited_right",
-                        Arrays.asList(string(tenant), string(namespace), var("2"), var("3")),
-                        Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), var("2"), var("3"))))),
-                rule("limited_right",
-                        Arrays.asList(string(tenant), string(namespace), var("2")),
-                        Arrays.asList(pred("namespace_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), var("2")))))
-        )));
-
-        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block.build()).get();
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
-        Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
-        ServiceConfiguration conf = new ServiceConfiguration();
-        conf.setProperties(properties);
-        provider.initialize(conf);
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
+        Block block0 = new Block(0, symbols);
+        block0.add_fact(adminFact);
+        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, block0.build());
 
-            @Override
-            public String getCommandData() {
-                return biscuit.serialize_b64().get();
-            }
-        });
+        Block block1 = rootBiscuit.create_block();
+        block1.add_check("check if " + namespaceFact(tenant, namespace) + " or " + topicVariableFact(tenant, namespace));
+        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block1.build());
 
+        String authedBiscuit = authedBiscuit(root, biscuit);
         AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
 
         log.debug(biscuit.print());
@@ -433,110 +273,40 @@ public String getSubscription() {
         assertTrue(authorizationProvider.allowTopicOperation(TopicName.get(tenant + "/" + namespace + "/" + "test"), authedBiscuit, TopicOperation.PRODUCE, null));
         assertTrue(authorizationProvider.allowTopicOperation(TopicName.get(tenant + "/" + namespace + "/" + "test123"), authedBiscuit, TopicOperation.CONSUME, authData));
         assertTrue(authorizationProvider.allowTopicOperation(TopicName.get(tenant + "/" + namespace + "/" + "test123"), authedBiscuit, TopicOperation.PRODUCE, null));
-        assertFalse(authorizationProvider.isSuperUser(authedBiscuit, null, conf).get());
-    }
-
-    @Test
-    public void testBuilders() {
-        SecureRandom rng = new SecureRandom();
-        KeyPair root = new KeyPair(rng);
-        SymbolTable symbols = Biscuit.default_symbol_table();
-
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_fact(fact("revocation_id", Arrays.asList(date(Date.from(Instant.now())))));
-        authority_builder.add_fact(fact("right", Arrays.asList(s("authority"), s("admin"))));
-        authority_builder.add_rule("right(#authority, $tenant, $namespace, $operation) <- " +
-                "right(#authority, #admin), namespace_operation(#ambient, $tenant, $namespace, $operation), " +
-                "[ #create_topic, #get_topic, #get_topics ].contains($operation)").get();
-        authority_builder.add_rule("right(#authority, \"topic\", $tenant, $namespace, $topic, $operation) <- " +
-                "right(#authority, #admin), topic_operation(#authority, $tenant, $namespace, $topic, $operation), " +
-                "[ #lookup ].contains($operation)").get();
+        assertTrue(authorizationProvider.allowTopicPolicyOperation(TopicName.get(tenant + "/" + namespace + "/" + "test"), authedBiscuit, PolicyName.ALL, PolicyOperation.READ, null));
+        assertFalse(authorizationProvider.allowTopicPolicyOperation(TopicName.get(tenant + "/" + namespace + "/" + "test"), authedBiscuit, PolicyName.ALL, PolicyOperation.WRITE, null));
 
-
-        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
-        assertNotNull(rootBiscuit);
-    }
-
-    @Test
-    public void testSuperUser() throws IOException, AuthenticationException, ExecutionException, InterruptedException {
-        SecureRandom rng = new SecureRandom();
-        KeyPair root = new KeyPair(rng);
-        SymbolTable symbols = Biscuit.default_symbol_table();
-
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_fact(fact("revocation_id", Arrays.asList(date(Date.from(Instant.now())))));
-        authority_builder.add_fact(fact("right", Arrays.asList(s("authority"), s("admin"))));
-        Biscuit biscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
-
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
         Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
+        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.getAbyte()));
         ServiceConfiguration conf = new ServiceConfiguration();
         conf.setProperties(properties);
-        provider.initialize(conf);
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
-
-            @Override
-            public String getCommandData() {
-                return biscuit.serialize_b64().get();
-            }
-        });
-
-        log.debug(biscuit.print());
-
-        AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
-        assertTrue(authorizationProvider.isSuperUser(authedBiscuit, null, conf).get());
-        assertTrue(authorizationProvider.allowNamespacePolicyOperation(NamespaceName.get("randomTenant/randomNamespace"), PolicyName.REPLICATION, PolicyOperation.WRITE, authedBiscuit, null));
+        assertFalse(authorizationProvider.isSuperUser(authedBiscuit, null, conf).get());
     }
 
     @Test
-    public void testSimpleSuperUser() throws IOException, AuthenticationException, ExecutionException, InterruptedException {
+    public void testSuperUser() throws IOException, AuthenticationException, ExecutionException, InterruptedException, Error.SymbolTableOverlap, Error.FormatError, Error.Language, Error.Parser {
         SecureRandom rng = new SecureRandom();
         KeyPair root = new KeyPair(rng);
         SymbolTable symbols = Biscuit.default_symbol_table();
 
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_rule(
-                rule("right",
-                        Arrays.asList(s("authority"), s("admin")),
-                        Arrays.asList(pred("right", Arrays.asList(s("authority"), string("admin"))))
-                )
-        );
-        authority_builder.add_fact(fact("right", Arrays.asList(s("authority"), s("admin"))));
-        Biscuit biscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
+        Block block0 = new Block(0, symbols);
+        block0.add_fact(adminFact);
+        Biscuit biscuit = Biscuit.make(rng, root, symbols, block0.build());
 
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
+        String authedBiscuit = authedBiscuit(root, biscuit);
+        log.debug(biscuit.print());
+
+        AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
         Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
+        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.getAbyte()));
         ServiceConfiguration conf = new ServiceConfiguration();
         conf.setProperties(properties);
-        provider.initialize(conf);
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
-
-            @Override
-            public String getCommandData() {
-                return biscuit.serialize_b64().get();
-            }
-        });
-
-        log.debug(biscuit.print());
-        AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
-        CompletableFuture<Boolean> authorizedFuture = authorizationProvider.isSuperUser(authedBiscuit, null, conf);
-        assertTrue(authorizedFuture.get());
+        assertTrue(authorizationProvider.isSuperUser(authedBiscuit, null, conf).get());
+        assertTrue(authorizationProvider.allowNamespacePolicyOperation(NamespaceName.get("randomTenant/randomNamespace"), PolicyName.REPLICATION, PolicyOperation.WRITE, authedBiscuit, null));
     }
 
     @Test
-    public void testNsLimitationsThenPrefixLimitation() throws IOException, AuthenticationException, ExecutionException, InterruptedException {
+    public void testNsLimitationsThenPrefixLimitation() throws IOException, AuthenticationException, ExecutionException, InterruptedException, Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
         SecureRandom rng = new SecureRandom();
         KeyPair root = new KeyPair(rng);
         SymbolTable symbols = Biscuit.default_symbol_table();
@@ -544,58 +314,22 @@ public void testNsLimitationsThenPrefixLimitation() throws IOException, Authenti
         String tenant = "tenantTest";
         String namespace = "namespaceTest";
 
-        // root token
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_fact(fact("revocation_id", Arrays.asList(date(Date.from(Instant.now())))));
-        authority_builder.add_fact(fact("right", Arrays.asList(s("authority"), s("admin"))));
-        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
+        Block block0 = new Block(0, symbols);
+        block0.add_fact(adminFact);
+        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, block0.build());
 
         // limit on ns tenant/namespace
-        Block block = rootBiscuit.create_block();
-        block.add_check(new Check(Arrays.asList(
-                rule("limited_right",
-                        Arrays.asList(string(tenant), string(namespace), var("2"), var("3")),
-                        Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), var("2"), var("3"))))),
-                rule("limited_right",
-                        Arrays.asList(string(tenant), string(namespace), var("2")),
-                        Arrays.asList(pred("namespace_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), var("2")))))
-        )));
-        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block.build()).get();
+        Block block1 = rootBiscuit.create_block();
+        block1.add_check("check if " + namespaceFact(tenant, namespace) + " or " + topicVariableFact(tenant, namespace));
+        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block1.build());
 
         // limit on tenant/namespace/PREFIX*
         String PREFIX = "INSTANCE_PREFIX_TO_DEFINE";
-        Block attenuated = biscuit.create_block();
-        /*
-        attenuated.add_check(Check(constrained_rule("limited_topic",
-                Arrays.asList(string(tenant), string(namespace), var("2")),
-                Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), var("2"), var("3")))),
-                Arrays.asList(new StrConstraint.Prefix(2, PREFIX))
-        )));
-
-         */
-        attenuated.add_check("check if topic_operation(#ambient, \"" + tenant + "\", \"" + namespace + "\", $topic, $operation), " +
-                "$topic.starts_with(\"" + PREFIX + "\")").get();
-        biscuit = biscuit.attenuate(rng, root, attenuated.build()).get();
-
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
-        Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
-        ServiceConfiguration conf = new ServiceConfiguration();
-        conf.setProperties(properties);
-        provider.initialize(conf);
-        Biscuit finalBiscuit = biscuit;
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
+        Block block2 = biscuit.create_block();
+        block2.add_check("check if " + topicVariableFact(tenant, namespace) + ", $topic.starts_with(\"" + PREFIX + "\")");
+        biscuit = biscuit.attenuate(rng, root, block2.build());
 
-            @Override
-            public String getCommandData() {
-                return finalBiscuit.serialize_b64().get();
-            }
-        });
+        String authedBiscuit = authedBiscuit(root, biscuit);
 
         AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
 
@@ -606,7 +340,7 @@ public String getCommandData() {
     }
 
     @Test
-    public void testLimitProduceOnTopicStartsWith() throws IOException, AuthenticationException, ExecutionException, InterruptedException {
+    public void testLimitProduceOnTopicStartsWith() throws IOException, AuthenticationException, ExecutionException, InterruptedException, Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
         SecureRandom rng = new SecureRandom();
         KeyPair root = new KeyPair(rng);
         SymbolTable symbols = Biscuit.default_symbol_table();
@@ -615,44 +349,17 @@ public void testLimitProduceOnTopicStartsWith() throws IOException, Authenticati
         String namespace = "namespaceTest";
 
         // root token
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_fact(fact("revocation_id", Arrays.asList(string(UUID.randomUUID().toString()))));
-        authority_builder.add_fact(fact("right", Arrays.asList(s("authority"), s("admin"))));
-        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
+        Block block0 = new Block(0, symbols);
+        block0.add_fact(adminFact);
+        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, block0.build());
 
         // limit on tenant/namespace/PREFIX*
         String PREFIX = "PREFIX";
-        Block attenuated = rootBiscuit.create_block();
-        /*
-        attenuated.add_check(Check(constrained_rule("limited_topic",
-                Arrays.asList(string(tenant), string(namespace), var("2")),
-                Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), var("2"), s("produce")))),
-                Arrays.asList(new StrConstraint.Prefix(2, PREFIX))
-        )));
-        */
-        attenuated.add_check("check if topic_operation(#ambient, \"" + tenant + "\", \"" + namespace + "\", $topic, #produce), " +
-                "$topic.starts_with(\"" + PREFIX + "\")").get();
-        Biscuit biscuit = rootBiscuit.attenuate(rng, root, attenuated.build()).get();
+        Block block2 = rootBiscuit.create_block();
+        block2.add_check("check if " + topicVariableFact(tenant, namespace) + "," + topicOperationFact(TopicOperation.PRODUCE) + ", $topic.starts_with(\"" + PREFIX + "\")");
+        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block2.build());
 
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
-        Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
-        ServiceConfiguration conf = new ServiceConfiguration();
-        conf.setProperties(properties);
-        provider.initialize(conf);
-        Biscuit finalBiscuit = biscuit;
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
-
-            @Override
-            public String getCommandData() {
-                return finalBiscuit.serialize_b64().get();
-            }
-        });
+        String authedBiscuit = authedBiscuit(root, biscuit);
 
         AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
 
@@ -666,7 +373,7 @@ public String getCommandData() {
     }
 
     @Test
-    public void testConsumeOverrideLookup() throws IOException, AuthenticationException, ExecutionException, InterruptedException {
+    public void testConsumeOverrideLookup() throws IOException, AuthenticationException, ExecutionException, InterruptedException, Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
         SecureRandom rng = new SecureRandom();
         KeyPair root = new KeyPair(rng);
         SymbolTable symbols = Biscuit.default_symbol_table();
@@ -674,38 +381,15 @@ public void testConsumeOverrideLookup() throws IOException, AuthenticationExcept
         String tenant = "tenantTest";
         String namespace = "namespaceTest";
 
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_fact(fact("revocation_id", Arrays.asList(date(Date.from(Instant.now())))));
-        authority_builder.add_fact(fact("right", Arrays.asList(s("authority"), s("admin"))));
-        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
+        Block block0 = new Block(0, symbols);
+        block0.add_fact(adminFact);
+        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, block0.build());
 
-        Block block = rootBiscuit.create_block();
-        block.add_check(new Check(Arrays.asList(
-                rule("limited_right",
-                        Arrays.asList(string(tenant), string(namespace), var("2"), var("3")),
-                        Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), var("2"), s("consume")))))
-        )));
+        Block block1 = rootBiscuit.create_block();
+        block1.add_check("check if " + topicVariableFact(tenant, namespace) + "," + topicOperationFact(TopicOperation.CONSUME));
+        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block1.build());
 
-        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block.build()).get();
-
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
-        Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
-        ServiceConfiguration conf = new ServiceConfiguration();
-        conf.setProperties(properties);
-        provider.initialize(conf);
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
-
-            @Override
-            public String getCommandData() {
-                return biscuit.serialize_b64().get();
-            }
-        });
+        String authedBiscuit = authedBiscuit(root, biscuit);
 
         log.debug(biscuit.print());
         AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
@@ -714,7 +398,7 @@ public String getCommandData() {
     }
 
     @Test
-    public void testProduceOverrideLookup() throws IOException, AuthenticationException, ExecutionException, InterruptedException {
+    public void testProduceOverrideLookup() throws IOException, AuthenticationException, ExecutionException, InterruptedException, Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
         SecureRandom rng = new SecureRandom();
         KeyPair root = new KeyPair(rng);
         SymbolTable symbols = Biscuit.default_symbol_table();
@@ -722,38 +406,15 @@ public void testProduceOverrideLookup() throws IOException, AuthenticationExcept
         String tenant = "tenantTest";
         String namespace = "namespaceTest";
 
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_fact(fact("revocation_id", Arrays.asList(date(Date.from(Instant.now())))));
-        authority_builder.add_fact(fact("right", Arrays.asList(s("authority"), s("admin"))));
-        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
-
-        Block block = rootBiscuit.create_block();
-        block.add_check(new Check(Arrays.asList(
-                rule("limited_right",
-                        Arrays.asList(string(tenant), string(namespace), var("2"), var("3")),
-                        Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), var("2"), s("produce")))))
-        )));
+        Block block0 = new Block(0, symbols);
+        block0.add_fact(adminFact);
+        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, block0.build());
 
-        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block.build()).get();
-
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
-        Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
-        ServiceConfiguration conf = new ServiceConfiguration();
-        conf.setProperties(properties);
-        provider.initialize(conf);
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
+        Block block1 = rootBiscuit.create_block();
+        block1.add_check("check if " + topicVariableFact(tenant, namespace) + "," + topicOperationFact(TopicOperation.PRODUCE));
+        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block1.build());
 
-            @Override
-            public String getCommandData() {
-                return biscuit.serialize_b64().get();
-            }
-        });
+        String authedBiscuit = authedBiscuit(root, biscuit);
 
         log.debug(biscuit.print());
         AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
@@ -762,7 +423,7 @@ public String getCommandData() {
     }
 
     @Test
-    public void testLookupIsNotOverrodeByProduce() throws IOException, AuthenticationException, ExecutionException, InterruptedException {
+    public void testLookupIsNotOverrodeByProduceOrConsume() throws IOException, AuthenticationException, ExecutionException, InterruptedException, Error, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
         SecureRandom rng = new SecureRandom();
         KeyPair root = new KeyPair(rng);
         SymbolTable symbols = Biscuit.default_symbol_table();
@@ -770,90 +431,20 @@ public void testLookupIsNotOverrodeByProduce() throws IOException, Authenticatio
         String tenant = "tenantTest";
         String namespace = "namespaceTest";
 
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_fact(fact("revocation_id", Arrays.asList(date(Date.from(Instant.now())))));
-        authority_builder.add_fact(fact("right", Arrays.asList(s("authority"), s("admin"))));
-        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
-
-        Block block = rootBiscuit.create_block();
-        block.add_check(new Check(Arrays.asList(
-                rule("limited_right",
-                        Arrays.asList(string(tenant), string(namespace), var("2"), var("3")),
-                        Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), var("2"), s("lookup")))))
-        )));
+        Block block0 = new Block(0, symbols);
+        block0.add_fact(adminFact);
+        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, block0.build());
 
-        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block.build()).get();
-
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
-        Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
-        ServiceConfiguration conf = new ServiceConfiguration();
-        conf.setProperties(properties);
-        provider.initialize(conf);
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
+        Block block1 = rootBiscuit.create_block();
+        block1.add_check("check if " + topicVariableFact(tenant, namespace) + "," + topicOperationFact(TopicOperation.LOOKUP));
+        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block1.build());
 
-            @Override
-            public String getCommandData() {
-                return biscuit.serialize_b64().get();
-            }
-        });
+        String authedBiscuit = authedBiscuit(root, biscuit);
 
         log.debug(biscuit.print());
         AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
         assertTrue(authorizationProvider.allowTopicOperationAsync(TopicName.get(tenant + "/" + namespace + "/" + "test"), authedBiscuit, TopicOperation.LOOKUP, null).get());
         assertFalse(authorizationProvider.allowTopicOperationAsync(TopicName.get(tenant + "/" + namespace + "/" + "test"), authedBiscuit, TopicOperation.PRODUCE, null).get());
-    }
-
-    @Test
-    public void testLookupIsNotOverrodeByConsume() throws IOException, AuthenticationException, ExecutionException, InterruptedException {
-        SecureRandom rng = new SecureRandom();
-        KeyPair root = new KeyPair(rng);
-        SymbolTable symbols = Biscuit.default_symbol_table();
-
-        String tenant = "tenantTest";
-        String namespace = "namespaceTest";
-
-        Block authority_builder = new Block(0, symbols);
-        authority_builder.add_fact(fact("revocation_id", Arrays.asList(date(Date.from(Instant.now())))));
-        authority_builder.add_fact(fact("right", Arrays.asList(s("authority"), s("admin"))));
-        Biscuit rootBiscuit = Biscuit.make(rng, root, symbols, authority_builder.build()).get();
-
-        Block block = rootBiscuit.create_block();
-        block.add_check(new Check(Arrays.asList(
-                rule("limited_right",
-                        Arrays.asList(string(tenant), string(namespace), var("2"), var("3")),
-                        Arrays.asList(pred("topic_operation", Arrays.asList(s("ambient"), string(tenant), string(namespace), var("2"), s("lookup")))))
-        )));
-
-        Biscuit biscuit = rootBiscuit.attenuate(rng, root, block.build()).get();
-
-        AuthenticationProviderBiscuit provider = new AuthenticationProviderBiscuit();
-        Properties properties = new Properties();
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_PUBLIC_ROOT_KEY, hex(root.public_key().key.compress().toByteArray()));
-        properties.setProperty(AuthenticationProviderBiscuit.CONF_BISCUIT_SEALING_KEY, "test");
-        ServiceConfiguration conf = new ServiceConfiguration();
-        conf.setProperties(properties);
-        provider.initialize(conf);
-        String authedBiscuit = provider.authenticate(new AuthenticationDataSource() {
-            @Override
-            public boolean hasDataFromCommand() {
-                return true;
-            }
-
-            @Override
-            public String getCommandData() {
-                return biscuit.serialize_b64().get();
-            }
-        });
-
-        log.debug(biscuit.print());
-        AuthorizationProviderBiscuit authorizationProvider = new AuthorizationProviderBiscuit();
-        assertTrue(authorizationProvider.allowTopicOperationAsync(TopicName.get(tenant + "/" + namespace + "/" + "test"), authedBiscuit, TopicOperation.LOOKUP, null).get());
         assertFalse(authorizationProvider.allowTopicOperationAsync(TopicName.get(tenant + "/" + namespace + "/" + "test"), authedBiscuit, TopicOperation.CONSUME, null).get());
     }
 }
\ No newline at end of file
diff --git a/src/test/java/com/clevercloud/biscuitpulsar/formatter/BiscuitFormatterTest.java b/src/test/java/com/clevercloud/biscuitpulsar/formatter/BiscuitFormatterTest.java
index 41d1abe..e350aa3 100644
--- a/src/test/java/com/clevercloud/biscuitpulsar/formatter/BiscuitFormatterTest.java
+++ b/src/test/java/com/clevercloud/biscuitpulsar/formatter/BiscuitFormatterTest.java
@@ -1,8 +1,5 @@
 package com.clevercloud.biscuitpulsar.formatter;
 
-import org.apache.pulsar.common.naming.NamespaceName;
-import org.apache.pulsar.common.policies.data.PolicyName;
-import org.apache.pulsar.common.policies.data.PolicyOperation;
 import org.junit.Test;
 
 import static com.clevercloud.biscuitpulsar.formatter.BiscuitFormatter.*;
@@ -11,22 +8,16 @@
 public class BiscuitFormatterTest {
     @Test
     public void testNamespaceOperations() {
-        assertEquals("[#create_topic,#get_topic,#get_topics,#delete_topic,#clear_backlog,#unsubscribe]", namespaceOperations);
+        assertEquals("[\"create_topic\",\"get_topic\",\"get_topics\",\"delete_topic\",\"clear_backlog\",\"unsubscribe\"]", namespaceOperations);
     }
 
     @Test
     public void testPoliciesOperations() {
-        assertEquals("[#all_read,#anti_affinity_read,#auto_subscription_creation_read,#auto_subscription_creation_write,#auto_topic_creation_read,#auto_topic_creation_write,#backlog_read,#backlog_write,#compaction_read,#compaction_write,#deduplication_read,#deduplication_snapshot_read,#deduplication_snapshot_write,#deduplication_write,#delayed_delivery_read,#delayed_delivery_write,#encryption_read,#encryption_write,#inactive_topic_read,#inactive_topic_write,#max_consumers_read,#max_consumers_write,#max_producers_read,#max_producers_write,#max_subscriptions_read,#max_subscriptions_write,#max_topics_read,#max_topics_write,#max_unacked_read,#max_unacked_write,#offload_read,#partition_read,#partition_write,#persistence_read,#persistence_write,#rate_read,#rate_write,#replication_rate_read,#replication_read,#resourcegroup_read,#resourcegroup_write,#retention_read,#retention_write,#schema_compatibility_strategy_read,#schema_compatibility_strategy_write,#subscription_auth_mode_read,#subscription_auth_mode_write,#subscription_expiration_time_read,#subscription_expiration_time_write,#ttl_read,#ttl_write]", policiesOperations);
+        assertEquals("[\"all_read\",\"anti_affinity_read\",\"auto_subscription_creation_read\",\"auto_subscription_creation_write\",\"auto_topic_creation_read\",\"auto_topic_creation_write\",\"backlog_read\",\"backlog_write\",\"compaction_read\",\"compaction_write\",\"deduplication_read\",\"deduplication_snapshot_read\",\"deduplication_snapshot_write\",\"deduplication_write\",\"delayed_delivery_read\",\"delayed_delivery_write\",\"encryption_read\",\"encryption_write\",\"inactive_topic_read\",\"inactive_topic_write\",\"max_consumers_read\",\"max_consumers_write\",\"max_producers_read\",\"max_producers_write\",\"max_subscriptions_read\",\"max_subscriptions_write\",\"max_topics_read\",\"max_topics_write\",\"max_unacked_read\",\"max_unacked_write\",\"offload_read\",\"partition_read\",\"partition_write\",\"persistence_read\",\"persistence_write\",\"rate_read\",\"rate_write\",\"replication_rate_read\",\"replication_read\",\"resourcegroup_read\",\"resourcegroup_write\",\"retention_read\",\"retention_write\",\"schema_compatibility_strategy_read\",\"schema_compatibility_strategy_write\",\"subscription_auth_mode_read\",\"subscription_auth_mode_write\",\"subscription_expiration_time_read\",\"subscription_expiration_time_write\",\"ttl_read\",\"ttl_write\"]", policiesOperations);
     }
 
     @Test
     public void testTopicOperations() {
-        assertEquals("[#lookup,#produce,#consume,#compact,#expire_messages,#offload,#peek_messages,#reset_cursor,#skip,#terminate,#subscribe,#get_subscriptions,#unsubscribe,#get_stats,#get_metadata,#get_backlog_size,#set_replicated_subscription_status]", topicOperations);
-    }
-
-    @Test
-    public void testNamespaceOperationFact() {
-        String nsFact = namespacePolicyOperationFact(NamespaceName.get("tenant/namespace"), PolicyName.COMPACTION, PolicyOperation.WRITE);
-        assertEquals("namespace_operation(#ambient,\"tenant\",\"namespace\",#compaction_write)", nsFact);
+        assertEquals("[\"lookup\",\"produce\",\"consume\",\"compact\",\"expire_messages\",\"offload\",\"peek_messages\",\"reset_cursor\",\"skip\",\"terminate\",\"subscribe\",\"get_subscriptions\",\"unsubscribe\",\"get_stats\",\"get_metadata\",\"get_backlog_size\",\"set_replicated_subscription_status\"]", topicOperations);
     }
 }
\ No newline at end of file
diff --git a/src/test/java/resources/log4j2.xml b/src/test/java/resources/log4j2.xml
deleted file mode 100644
index fc7e008..0000000
--- a/src/test/java/resources/log4j2.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Configuration status="debug">
-  <Appenders>
-    <Console name="Console" target="SYSTEM_OUT">
-      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
-    </Console>
-  </Appenders>
-  <Loggers>
-    <Root level="debug">
-      <AppenderRef ref="Console"/>
-    </Root>
-  </Loggers>
-</Configuration>
diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..70e29b8
--- /dev/null
+++ b/src/test/resources/logback-test.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="debug">
+    <appender-ref ref="STDOUT"/>
+  </root>
+
+</configuration>
\ No newline at end of file

From f1a93fad6da057f28977d9fccb9788fccded5f32 Mon Sep 17 00:00:00 2001
From: "Alexandre DUVAL - @kannarfr" <kannarfr@gmail.com>
Date: Thu, 3 Mar 2022 15:42:01 +0100
Subject: [PATCH 4/8] drop useless package

---
 pom.xml | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/pom.xml b/pom.xml
index 4545a61..b1c54d8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -173,11 +173,6 @@
       <artifactId>vavr</artifactId>
       <version>${vavr.version}</version>
     </dependency>
-    <dependency>
-      <groupId>org.apache.pulsar</groupId>
-      <artifactId>pulsar-client</artifactId>
-      <version>${pulsar.version}</version>
-    </dependency>
     <dependency>
       <groupId>org.apache.pulsar</groupId>
       <artifactId>pulsar-broker</artifactId>

From 56abf7f368d8ee7ab595741c6b864d22c559f3d5 Mon Sep 17 00:00:00 2001
From: "Alexandre DUVAL - @kannarfr" <kannarfr@gmail.com>
Date: Thu, 3 Mar 2022 15:42:09 +0100
Subject: [PATCH 5/8] exclude functions as it's not used and embed log4j conf
 which overrides our logback conf

---
 pom.xml | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/pom.xml b/pom.xml
index b1c54d8..3e0c0a2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -177,15 +177,10 @@
       <groupId>org.apache.pulsar</groupId>
       <artifactId>pulsar-broker</artifactId>
       <version>${pulsar.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.pulsar</groupId>
-      <artifactId>pulsar-common</artifactId>
-      <version>${pulsar.version}</version>
       <exclusions>
         <exclusion>
-          <groupId>io.grpc</groupId>
-          <artifactId>grpc-all</artifactId>
+          <groupId>org.apache.pulsar</groupId>
+          <artifactId>pulsar-functions-runtime</artifactId>
         </exclusion>
       </exclusions>
     </dependency>

From adef978a5f7ac6ee266bfd1e86a655a55f6c71c3 Mon Sep 17 00:00:00 2001
From: "Alexandre DUVAL - @kannarfr" <kannarfr@gmail.com>
Date: Fri, 4 Mar 2022 15:44:05 +0100
Subject: [PATCH 6/8] clean unused

---
 .../clevercloud/biscuitpulsar/AuthenticationBiscuit.java   | 7 +++----
 .../biscuitpulsar/AuthenticationProviderBiscuit.java       | 1 -
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/src/main/java/com/clevercloud/biscuitpulsar/AuthenticationBiscuit.java b/src/main/java/com/clevercloud/biscuitpulsar/AuthenticationBiscuit.java
index 37fb756..ae13a3a 100644
--- a/src/main/java/com/clevercloud/biscuitpulsar/AuthenticationBiscuit.java
+++ b/src/main/java/com/clevercloud/biscuitpulsar/AuthenticationBiscuit.java
@@ -4,7 +4,6 @@
 import org.apache.pulsar.client.api.Authentication;
 import org.apache.pulsar.client.api.AuthenticationDataProvider;
 import org.apache.pulsar.client.api.EncodedAuthenticationParameterSupport;
-import org.apache.pulsar.client.api.PulsarClientException;
 
 import java.io.IOException;
 import java.net.URI;
@@ -31,7 +30,7 @@ public AuthenticationBiscuit(Supplier<String> biscuitSupplier) {
     }
 
     @Override
-    public void close() throws IOException {
+    public void close() {
         // noop
     }
 
@@ -41,7 +40,7 @@ public String getAuthMethodName() {
     }
 
     @Override
-    public AuthenticationDataProvider getAuthData() throws PulsarClientException {
+    public AuthenticationDataProvider getAuthData() {
         return new AuthenticationDataBiscuit(biscuitSupplier);
     }
 
@@ -72,7 +71,7 @@ public void configure(Map<String, String> authParams) {
     }
 
     @Override
-    public void start() throws PulsarClientException {
+    public void start() {
         // noop
     }
 }
diff --git a/src/main/java/com/clevercloud/biscuitpulsar/AuthenticationProviderBiscuit.java b/src/main/java/com/clevercloud/biscuitpulsar/AuthenticationProviderBiscuit.java
index fcae746..2c0a4f7 100644
--- a/src/main/java/com/clevercloud/biscuitpulsar/AuthenticationProviderBiscuit.java
+++ b/src/main/java/com/clevercloud/biscuitpulsar/AuthenticationProviderBiscuit.java
@@ -17,7 +17,6 @@
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 import java.security.SignatureException;
-import java.util.Base64;
 
 public class AuthenticationProviderBiscuit implements AuthenticationProvider {
     private static final Logger log = LoggerFactory.getLogger(AuthenticationProviderBiscuit.class);

From 56ce2f0b570d1bff3cce3b57c8897e07c2cbdbdc Mon Sep 17 00:00:00 2001
From: "Alexandre DUVAL - @kannarfr" <kannarfr@gmail.com>
Date: Fri, 4 Mar 2022 15:44:16 +0100
Subject: [PATCH 7/8] biscuit-java 2.0

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 3e0c0a2..0886675 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
     <nexus-staging-maven.version>1.6.7</nexus-staging-maven.version>
 
     <!-- dependencies -->
-    <biscuit-java.version>2.0.0-SNAPSHOT</biscuit-java.version>
+    <biscuit-java.version>2.0.0</biscuit-java.version>
     <logback-classic.version>1.2.10</logback-classic.version>
     <protobuf.version>3.16.1</protobuf.version>
     <pulsar.version>2.9.1</pulsar.version>

From a98522d4c2f30b5501543763509573c81ced2413 Mon Sep 17 00:00:00 2001
From: "Alexandre DUVAL - @kannarfr" <kannarfr@gmail.com>
Date: Fri, 4 Mar 2022 15:49:30 +0100
Subject: [PATCH 8/8] bump readme

---
 README.md | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/README.md b/README.md
index 3ef2ed7..4513f83 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,6 @@ This only supports Apache Pulsar v2.9+.
 
 The listed dependencies can be necessary to add to the /lib of pulsar folder as jars:
 
-- curve25519-elisabeth
 - vavr
 - protobuf
 - biscuit-java
@@ -28,7 +27,6 @@ We currently are using this script to put libs on pulsar nodes:
 ```bash
 #!/bin/bash
 
-wget -P "pulsar/lib" "https://repo1.maven.org/maven2/cafe/cryptography/curve25519-elisabeth/0.1.0/curve25519-elisabeth-0.1.0.jar"
 wget -P "pulsar/lib" "https://repo1.maven.org/maven2/io/vavr/vavr/0.10.3/vavr-0.10.3.jar"
 wget -P "pulsar/lib" "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.16.1/protobuf-java-3.16.1.jar"
 wget -P "pulsar/lib" "https://repo1.maven.org/maven2/com/clever-cloud/biscuit-java/<VERSION>/biscuit-java-<VERSION>.jar"
@@ -54,7 +52,6 @@ authorizationProvider=com.clevercloud.biscuitpulsar.AuthorizationProviderBiscuit
 
 ### --- Biscuit Authentication Provider --- ###
 biscuitPublicRootKey=@@BISCUIT_PUBLIC_ROOT_KEY@@
-biscuitSealingKey=@@BISCUIT_PUBLIC_SEALING_KEY@@
 # support JWT side by side with Biscuit for AuthenticationToken
 biscuitSupportJWT=true|false
 # biscuit verify run limits before TimeOut
@@ -69,10 +66,6 @@ biscuitRunLimitsMaxTimeMillis=30
 sed -i -e "s/@@BISCUIT_PUBLIC_ROOT_KEY@@/$1/" broker.conf
 sed -i -e "s/@@BISCUIT_PUBLIC_ROOT_KEY@@/$1/" proxy.conf
 sed -i -e "s/@@BISCUIT_PUBLIC_ROOT_KEY@@/$1/" standalone.conf
-
-sed -i -e "s/@@BISCUIT_PUBLIC_SEALING_KEY@@/$2/" broker.conf
-sed -i -e "s/@@BISCUIT_PUBLIC_SEALING_KEY@@/$2/" proxy.conf
-sed -i -e "s/@@BISCUIT_PUBLIC_SEALING_KEY@@/$2/" standalone.conf
 ```
 
 ## Usage