class NodePermissioningController {

  public boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinationEnode) {
    LOG.trace("Node permissioning: Checking {} -> {}", sourceEnode, destinationEnode);

    if (syncStatusNodePermissioningProvider
        .map(p -> !p.hasReachedSync() && p.isPermitted(sourceEnode, destinationEnode))
        .orElse(false)) {

      LOG.trace(
          "Node permissioning - Sync Status: Permitted {} -> {}", sourceEnode, destinationEnode);

      return true;
    }

    final Optional<Boolean> insufficientPeerPermission =
        insufficientPeersPermissioningProvider.flatMap(
            p -> p.isPermitted(sourceEnode, destinationEnode));

    if (insufficientPeerPermission.isPresent()) {
      final Boolean permitted = insufficientPeerPermission.get();

      LOG.trace(
          "Node permissioning - Insufficient Peers: {} {} -> {}",
          permitted ? "Permitted" : "Rejected",
          sourceEnode,
          destinationEnode);

      return permitted;
    }

    if (syncStatusNodePermissioningProvider.isPresent()
        && !syncStatusNodePermissioningProvider.get().isPermitted(sourceEnode, destinationEnode)) {

      LOG.trace(
          "Node permissioning - Sync Status: Rejected {} -> {}", sourceEnode, destinationEnode);

      return false;
    } else {
      for (final NodePermissioningProvider provider : providers) {
        if (!provider.isPermitted(sourceEnode, destinationEnode)) {
          LOG.trace(
              "Node permissioning - {}: Rejected {} -> {}",
              provider.getClass().getSimpleName(),
              sourceEnode,
              destinationEnode);

          return false;
        }
      }
    }

    LOG.trace("Node permissioning: Permitted {} -> {}", sourceEnode, destinationEnode);

    return true;
  }

  public  NodePermissioningController(
      final Optional<SyncStatusNodePermissioningProvider> syncStatusNodePermissioningProvider,
      final List<NodePermissioningProvider> providers);

}