mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-20 11:45:53 +00:00
@@ -69,6 +69,9 @@ public:
|
|||||||
if (specifiesCurrentOrClosedLedger(request))
|
if (specifiesCurrentOrClosedLedger(request))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (isForcedForward(ctx))
|
||||||
|
return true;
|
||||||
|
|
||||||
auto const checkAccountInfoForward = [&]() {
|
auto const checkAccountInfoForward = [&]() {
|
||||||
return ctx.method == "account_info" and request.contains("queue") and request.at("queue").is_bool() and
|
return ctx.method == "account_info" and request.contains("queue") and request.at("queue").is_bool() and
|
||||||
request.at("queue").as_bool();
|
request.at("queue").as_bool();
|
||||||
@@ -138,6 +141,14 @@ private:
|
|||||||
{
|
{
|
||||||
return handlerProvider_->contains(method) || isProxied(method);
|
return handlerProvider_->contains(method) || isProxied(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
isForcedForward(web::Context const& ctx) const
|
||||||
|
{
|
||||||
|
static constexpr auto FORCE_FORWARD = "force_forward";
|
||||||
|
return ctx.isAdmin and ctx.params.contains(FORCE_FORWARD) and ctx.params.at(FORCE_FORWARD).is_bool() and
|
||||||
|
ctx.params.at(FORCE_FORWARD).as_bool();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace rpc::impl
|
} // namespace rpc::impl
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "util/MockCounters.hpp"
|
#include "util/MockCounters.hpp"
|
||||||
#include "util/MockHandlerProvider.hpp"
|
#include "util/MockHandlerProvider.hpp"
|
||||||
#include "util/MockLoadBalancer.hpp"
|
#include "util/MockLoadBalancer.hpp"
|
||||||
|
#include "util/NameGenerator.hpp"
|
||||||
#include "util/Taggable.hpp"
|
#include "util/Taggable.hpp"
|
||||||
#include "util/config/Config.hpp"
|
#include "util/config/Config.hpp"
|
||||||
#include "web/Context.hpp"
|
#include "web/Context.hpp"
|
||||||
@@ -32,10 +33,12 @@
|
|||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using namespace rpc;
|
using namespace rpc;
|
||||||
using namespace testing;
|
using namespace testing;
|
||||||
@@ -59,235 +62,159 @@ protected:
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(RPCForwardingProxyTest, ShouldForwardReturnsFalseIfClioOnly)
|
struct ShouldForwardParamTestCaseBundle {
|
||||||
|
std::string testName;
|
||||||
|
std::uint32_t apiVersion;
|
||||||
|
std::string method;
|
||||||
|
std::string testJson;
|
||||||
|
bool mockedIsClioOnly;
|
||||||
|
std::uint32_t called;
|
||||||
|
bool isAdmin;
|
||||||
|
bool expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShouldForwardParameterTest : public RPCForwardingProxyTest,
|
||||||
|
WithParamInterface<ShouldForwardParamTestCaseBundle> {};
|
||||||
|
|
||||||
|
static auto
|
||||||
|
generateTestValuesForParametersTest()
|
||||||
{
|
{
|
||||||
|
auto const isClioOnly = true;
|
||||||
|
auto const isAdmin = true;
|
||||||
|
auto const shouldForward = true;
|
||||||
|
|
||||||
|
return std::vector<ShouldForwardParamTestCaseBundle>{
|
||||||
|
{"ShouldForwardReturnsFalseIfClioOnly", 2u, "test", "{}", isClioOnly, 1, !isAdmin, !shouldForward},
|
||||||
|
{"ShouldForwardReturnsTrueIfProxied", 2u, "submit", "{}", !isClioOnly, 1, !isAdmin, shouldForward},
|
||||||
|
{"ShouldForwardReturnsTrueIfCurrentLedgerSpecified",
|
||||||
|
2u,
|
||||||
|
"anymethod",
|
||||||
|
R"({"ledger_index": "current"})",
|
||||||
|
!isClioOnly,
|
||||||
|
1,
|
||||||
|
!isAdmin,
|
||||||
|
shouldForward},
|
||||||
|
{"ShouldForwardReturnsTrueIfClosedLedgerSpecified",
|
||||||
|
2u,
|
||||||
|
"anymethod",
|
||||||
|
R"({"ledger_index": "closed"})",
|
||||||
|
!isClioOnly,
|
||||||
|
1,
|
||||||
|
!isAdmin,
|
||||||
|
shouldForward},
|
||||||
|
{"ShouldForwardReturnsTrueIfAccountInfoWithQueueSpecified",
|
||||||
|
2u,
|
||||||
|
"account_info",
|
||||||
|
R"({"queue": true})",
|
||||||
|
!isClioOnly,
|
||||||
|
1,
|
||||||
|
!isAdmin,
|
||||||
|
shouldForward},
|
||||||
|
{"ShouldForwardReturnsFalseIfAccountInfoQueueIsFalse",
|
||||||
|
2u,
|
||||||
|
"account_info",
|
||||||
|
R"({"queue": false})",
|
||||||
|
!isClioOnly,
|
||||||
|
1,
|
||||||
|
!isAdmin,
|
||||||
|
!shouldForward},
|
||||||
|
{"ShouldForwardReturnsTrueIfLedgerWithQueueSpecified",
|
||||||
|
2u,
|
||||||
|
"ledger",
|
||||||
|
R"({"queue": true})",
|
||||||
|
!isClioOnly,
|
||||||
|
1,
|
||||||
|
!isAdmin,
|
||||||
|
shouldForward},
|
||||||
|
{"ShouldForwardReturnsFalseIfLedgerQueueIsFalse",
|
||||||
|
2u,
|
||||||
|
"ledger",
|
||||||
|
R"({"queue": false})",
|
||||||
|
!isClioOnly,
|
||||||
|
1,
|
||||||
|
!isAdmin,
|
||||||
|
!shouldForward},
|
||||||
|
{"ShouldNotForwardReturnsTrueIfAPIVersionIsV1",
|
||||||
|
1u,
|
||||||
|
"api_version_check",
|
||||||
|
"{}",
|
||||||
|
!isClioOnly,
|
||||||
|
1,
|
||||||
|
!isAdmin,
|
||||||
|
!shouldForward},
|
||||||
|
{"ShouldForwardReturnsFalseIfAPIVersionIsV2",
|
||||||
|
2u,
|
||||||
|
"api_version_check",
|
||||||
|
"{}",
|
||||||
|
!isClioOnly,
|
||||||
|
1,
|
||||||
|
!isAdmin,
|
||||||
|
!shouldForward},
|
||||||
|
{"ShouldNeverForwardSubscribe", 1u, "subscribe", "{}", !isClioOnly, 0, !isAdmin, !shouldForward},
|
||||||
|
{"ShouldNeverForwardUnsubscribe", 1u, "unsubscribe", "{}", !isClioOnly, 0, !isAdmin, !shouldForward},
|
||||||
|
{"ForceForwardTrue", 1u, "any_method", R"({"force_forward": true})", !isClioOnly, 1, isAdmin, shouldForward},
|
||||||
|
{"ForceForwardFalse", 1u, "any_method", R"({"force_forward": false})", !isClioOnly, 1, isAdmin, !shouldForward},
|
||||||
|
{"ForceForwardNotAdmin",
|
||||||
|
1u,
|
||||||
|
"any_method",
|
||||||
|
R"({"force_forward": true})",
|
||||||
|
!isClioOnly,
|
||||||
|
1,
|
||||||
|
!isAdmin,
|
||||||
|
!shouldForward},
|
||||||
|
{"ForceForwardSubscribe",
|
||||||
|
1u,
|
||||||
|
"subscribe",
|
||||||
|
R"({"force_forward": true})",
|
||||||
|
!isClioOnly,
|
||||||
|
0,
|
||||||
|
isAdmin,
|
||||||
|
not shouldForward},
|
||||||
|
{"ForceForwardUnsubscribe",
|
||||||
|
1u,
|
||||||
|
"unsubscribe",
|
||||||
|
R"({"force_forward": true})",
|
||||||
|
!isClioOnly,
|
||||||
|
0,
|
||||||
|
isAdmin,
|
||||||
|
!shouldForward},
|
||||||
|
{"ForceForwardClioOnly",
|
||||||
|
1u,
|
||||||
|
"clio_only_method",
|
||||||
|
R"({"force_forward": true})",
|
||||||
|
isClioOnly,
|
||||||
|
1,
|
||||||
|
isAdmin,
|
||||||
|
!shouldForward},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(
|
||||||
|
ShouldForwardTest,
|
||||||
|
ShouldForwardParameterTest,
|
||||||
|
ValuesIn(generateTestValuesForParametersTest()),
|
||||||
|
tests::util::NameGenerator
|
||||||
|
);
|
||||||
|
|
||||||
|
TEST_P(ShouldForwardParameterTest, Test)
|
||||||
|
{
|
||||||
|
auto const testBundle = GetParam();
|
||||||
auto const rawHandlerProviderPtr = handlerProvider.get();
|
auto const rawHandlerProviderPtr = handlerProvider.get();
|
||||||
auto const apiVersion = 2u;
|
auto const apiVersion = testBundle.apiVersion;
|
||||||
auto const method = "test";
|
auto const method = testBundle.method;
|
||||||
auto const params = json::parse("{}");
|
auto const params = json::parse(testBundle.testJson);
|
||||||
|
|
||||||
ON_CALL(*rawHandlerProviderPtr, isClioOnly(_)).WillByDefault(Return(true));
|
ON_CALL(*rawHandlerProviderPtr, isClioOnly(_)).WillByDefault(Return(testBundle.mockedIsClioOnly));
|
||||||
EXPECT_CALL(*rawHandlerProviderPtr, isClioOnly(method)).Times(1);
|
EXPECT_CALL(*rawHandlerProviderPtr, isClioOnly(method)).Times(testBundle.called);
|
||||||
|
|
||||||
runSpawn([&](auto yield) {
|
runSpawn([&](auto yield) {
|
||||||
auto const range = backend->fetchLedgerRange();
|
auto const range = backend->fetchLedgerRange();
|
||||||
auto const ctx =
|
auto const ctx = web::Context(
|
||||||
web::Context(yield, method, apiVersion, params.as_object(), nullptr, tagFactory, *range, CLIENT_IP, true);
|
yield, method, apiVersion, params.as_object(), nullptr, tagFactory, *range, CLIENT_IP, testBundle.isAdmin
|
||||||
|
);
|
||||||
|
|
||||||
auto const res = proxy.shouldForward(ctx);
|
auto const res = proxy.shouldForward(ctx);
|
||||||
ASSERT_FALSE(res);
|
ASSERT_EQ(res, testBundle.expected);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(RPCForwardingProxyTest, ShouldForwardReturnsTrueIfProxied)
|
|
||||||
{
|
|
||||||
auto const rawHandlerProviderPtr = handlerProvider.get();
|
|
||||||
auto const apiVersion = 2u;
|
|
||||||
auto const method = "submit";
|
|
||||||
auto const params = json::parse("{}");
|
|
||||||
|
|
||||||
ON_CALL(*rawHandlerProviderPtr, isClioOnly(_)).WillByDefault(Return(false));
|
|
||||||
EXPECT_CALL(*rawHandlerProviderPtr, isClioOnly(method)).Times(1);
|
|
||||||
|
|
||||||
runSpawn([&](auto yield) {
|
|
||||||
auto const range = backend->fetchLedgerRange();
|
|
||||||
auto const ctx =
|
|
||||||
web::Context(yield, method, apiVersion, params.as_object(), nullptr, tagFactory, *range, CLIENT_IP, true);
|
|
||||||
|
|
||||||
auto const res = proxy.shouldForward(ctx);
|
|
||||||
ASSERT_TRUE(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(RPCForwardingProxyTest, ShouldForwardReturnsTrueIfCurrentLedgerSpecified)
|
|
||||||
{
|
|
||||||
auto const rawHandlerProviderPtr = handlerProvider.get();
|
|
||||||
auto const apiVersion = 2u;
|
|
||||||
auto const method = "anymethod";
|
|
||||||
auto const params = json::parse(R"({"ledger_index": "current"})");
|
|
||||||
|
|
||||||
ON_CALL(*rawHandlerProviderPtr, isClioOnly(_)).WillByDefault(Return(false));
|
|
||||||
EXPECT_CALL(*rawHandlerProviderPtr, isClioOnly(method)).Times(1);
|
|
||||||
|
|
||||||
runSpawn([&](auto yield) {
|
|
||||||
auto const range = backend->fetchLedgerRange();
|
|
||||||
auto const ctx =
|
|
||||||
web::Context(yield, method, apiVersion, params.as_object(), nullptr, tagFactory, *range, CLIENT_IP, true);
|
|
||||||
|
|
||||||
auto const res = proxy.shouldForward(ctx);
|
|
||||||
ASSERT_TRUE(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(RPCForwardingProxyTest, ShouldForwardReturnsTrueIfClosedLedgerSpecified)
|
|
||||||
{
|
|
||||||
auto const rawHandlerProviderPtr = handlerProvider.get();
|
|
||||||
auto const apiVersion = 2u;
|
|
||||||
auto const method = "anymethod";
|
|
||||||
auto const params = json::parse(R"({"ledger_index": "closed"})");
|
|
||||||
|
|
||||||
ON_CALL(*rawHandlerProviderPtr, isClioOnly(_)).WillByDefault(Return(false));
|
|
||||||
EXPECT_CALL(*rawHandlerProviderPtr, isClioOnly(method)).Times(1);
|
|
||||||
|
|
||||||
runSpawn([&](auto yield) {
|
|
||||||
auto const range = backend->fetchLedgerRange();
|
|
||||||
auto const ctx =
|
|
||||||
web::Context(yield, method, apiVersion, params.as_object(), nullptr, tagFactory, *range, CLIENT_IP, true);
|
|
||||||
|
|
||||||
auto const res = proxy.shouldForward(ctx);
|
|
||||||
ASSERT_TRUE(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(RPCForwardingProxyTest, ShouldForwardReturnsTrueIfAccountInfoWithQueueSpecified)
|
|
||||||
{
|
|
||||||
auto const rawHandlerProviderPtr = handlerProvider.get();
|
|
||||||
auto const apiVersion = 2u;
|
|
||||||
auto const method = "account_info";
|
|
||||||
auto const params = json::parse(R"({"queue": true})");
|
|
||||||
|
|
||||||
ON_CALL(*rawHandlerProviderPtr, isClioOnly(_)).WillByDefault(Return(false));
|
|
||||||
EXPECT_CALL(*rawHandlerProviderPtr, isClioOnly(method)).Times(1);
|
|
||||||
|
|
||||||
runSpawn([&](auto yield) {
|
|
||||||
auto const range = backend->fetchLedgerRange();
|
|
||||||
auto const ctx =
|
|
||||||
web::Context(yield, method, apiVersion, params.as_object(), nullptr, tagFactory, *range, CLIENT_IP, true);
|
|
||||||
|
|
||||||
auto const res = proxy.shouldForward(ctx);
|
|
||||||
ASSERT_TRUE(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(RPCForwardingProxyTest, ShouldForwardReturnsFalseIfAccountInfoQueueIsFalse)
|
|
||||||
{
|
|
||||||
auto const rawHandlerProviderPtr = handlerProvider.get();
|
|
||||||
auto const apiVersion = 2u;
|
|
||||||
auto const method = "account_info";
|
|
||||||
auto const params = json::parse(R"({"queue": false})");
|
|
||||||
|
|
||||||
ON_CALL(*rawHandlerProviderPtr, isClioOnly(_)).WillByDefault(Return(false));
|
|
||||||
EXPECT_CALL(*rawHandlerProviderPtr, isClioOnly(method)).Times(1);
|
|
||||||
|
|
||||||
runSpawn([&](auto yield) {
|
|
||||||
auto const range = backend->fetchLedgerRange();
|
|
||||||
auto const ctx =
|
|
||||||
web::Context(yield, method, apiVersion, params.as_object(), nullptr, tagFactory, *range, CLIENT_IP, true);
|
|
||||||
|
|
||||||
auto const res = proxy.shouldForward(ctx);
|
|
||||||
ASSERT_FALSE(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(RPCForwardingProxyTest, ShouldForwardReturnsTrueIfLedgerWithQueueSpecified)
|
|
||||||
{
|
|
||||||
auto const rawHandlerProviderPtr = handlerProvider.get();
|
|
||||||
auto const apiVersion = 2u;
|
|
||||||
auto const method = "ledger";
|
|
||||||
auto const params = json::parse(R"({"queue": true})");
|
|
||||||
|
|
||||||
ON_CALL(*rawHandlerProviderPtr, isClioOnly(_)).WillByDefault(Return(false));
|
|
||||||
EXPECT_CALL(*rawHandlerProviderPtr, isClioOnly(method)).Times(1);
|
|
||||||
|
|
||||||
runSpawn([&](auto yield) {
|
|
||||||
auto const range = backend->fetchLedgerRange();
|
|
||||||
auto const ctx =
|
|
||||||
web::Context(yield, method, apiVersion, params.as_object(), nullptr, tagFactory, *range, CLIENT_IP, true);
|
|
||||||
|
|
||||||
auto const res = proxy.shouldForward(ctx);
|
|
||||||
ASSERT_TRUE(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(RPCForwardingProxyTest, ShouldForwardReturnsFalseIfLedgerQueueIsFalse)
|
|
||||||
{
|
|
||||||
auto const rawHandlerProviderPtr = handlerProvider.get();
|
|
||||||
auto const apiVersion = 2u;
|
|
||||||
auto const method = "ledger";
|
|
||||||
auto const params = json::parse(R"({"queue": false})");
|
|
||||||
|
|
||||||
ON_CALL(*rawHandlerProviderPtr, isClioOnly(_)).WillByDefault(Return(false));
|
|
||||||
EXPECT_CALL(*rawHandlerProviderPtr, isClioOnly(method)).Times(1);
|
|
||||||
|
|
||||||
runSpawn([&](auto yield) {
|
|
||||||
auto const range = backend->fetchLedgerRange();
|
|
||||||
auto const ctx =
|
|
||||||
web::Context(yield, method, apiVersion, params.as_object(), nullptr, tagFactory, *range, CLIENT_IP, true);
|
|
||||||
|
|
||||||
auto const res = proxy.shouldForward(ctx);
|
|
||||||
ASSERT_FALSE(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(RPCForwardingProxyTest, ShouldNotForwardReturnsTrueIfAPIVersionIsV1)
|
|
||||||
{
|
|
||||||
auto const apiVersion = 1u;
|
|
||||||
auto const method = "api_version_check";
|
|
||||||
auto const params = json::parse("{}");
|
|
||||||
|
|
||||||
auto const rawHandlerProviderPtr = handlerProvider.get();
|
|
||||||
ON_CALL(*rawHandlerProviderPtr, isClioOnly(_)).WillByDefault(Return(false));
|
|
||||||
EXPECT_CALL(*rawHandlerProviderPtr, isClioOnly(method)).Times(1);
|
|
||||||
|
|
||||||
runSpawn([&](auto yield) {
|
|
||||||
auto const range = backend->fetchLedgerRange();
|
|
||||||
auto const ctx =
|
|
||||||
web::Context(yield, method, apiVersion, params.as_object(), nullptr, tagFactory, *range, CLIENT_IP, true);
|
|
||||||
|
|
||||||
auto const res = proxy.shouldForward(ctx);
|
|
||||||
ASSERT_FALSE(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(RPCForwardingProxyTest, ShouldForwardReturnsFalseIfAPIVersionIsV2)
|
|
||||||
{
|
|
||||||
auto const rawHandlerProviderPtr = handlerProvider.get();
|
|
||||||
auto const apiVersion = 2u;
|
|
||||||
auto const method = "api_version_check";
|
|
||||||
auto const params = json::parse("{}");
|
|
||||||
|
|
||||||
ON_CALL(*rawHandlerProviderPtr, isClioOnly(_)).WillByDefault(Return(false));
|
|
||||||
EXPECT_CALL(*rawHandlerProviderPtr, isClioOnly(method)).Times(1);
|
|
||||||
|
|
||||||
runSpawn([&](auto yield) {
|
|
||||||
auto const range = backend->fetchLedgerRange();
|
|
||||||
auto const ctx =
|
|
||||||
web::Context(yield, method, apiVersion, params.as_object(), nullptr, tagFactory, *range, CLIENT_IP, true);
|
|
||||||
|
|
||||||
auto const res = proxy.shouldForward(ctx);
|
|
||||||
ASSERT_FALSE(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(RPCForwardingProxyTest, ShouldNeverForwardSubscribe)
|
|
||||||
{
|
|
||||||
auto const apiVersion = 1u;
|
|
||||||
auto const method = "subscribe";
|
|
||||||
auto const params = json::parse("{}");
|
|
||||||
|
|
||||||
runSpawn([&](auto yield) {
|
|
||||||
auto const range = backend->fetchLedgerRange();
|
|
||||||
auto const ctx =
|
|
||||||
web::Context(yield, method, apiVersion, params.as_object(), nullptr, tagFactory, *range, CLIENT_IP, true);
|
|
||||||
|
|
||||||
auto const res = proxy.shouldForward(ctx);
|
|
||||||
ASSERT_FALSE(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(RPCForwardingProxyTest, ShouldNeverForwardUnsubscribe)
|
|
||||||
{
|
|
||||||
auto const apiVersion = 1u;
|
|
||||||
auto const method = "unsubscribe";
|
|
||||||
auto const params = json::parse("{}");
|
|
||||||
|
|
||||||
runSpawn([&](auto yield) {
|
|
||||||
auto const range = backend->fetchLedgerRange();
|
|
||||||
auto const ctx =
|
|
||||||
web::Context(yield, method, apiVersion, params.as_object(), nullptr, tagFactory, *range, CLIENT_IP, true);
|
|
||||||
|
|
||||||
auto const res = proxy.shouldForward(ctx);
|
|
||||||
ASSERT_FALSE(res);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user