mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-29 15:05:50 +00:00
Secure gateway:
This is designed for use by proxies in front of rippled. Configured IPs can forward identifying user data in HTTP headers, including user name and origin IP. If the user name exists, then resource limits are lifted for that session. However, administrative commands are still reserved only for administrative sessions.
This commit is contained in:
committed by
Nik Bougalis
parent
810175ae95
commit
496fea5995
@@ -3525,6 +3525,9 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\server\impl\PlainPeer.h">
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\server\impl\Port.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\server\impl\Role.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
|
||||
@@ -4167,6 +4167,9 @@
|
||||
<ClInclude Include="..\..\src\ripple\server\impl\PlainPeer.h">
|
||||
<Filter>ripple\server\impl</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\server\impl\Port.cpp">
|
||||
<Filter>ripple\server\impl</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\server\impl\Role.cpp">
|
||||
<Filter>ripple\server\impl</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -212,6 +212,29 @@
|
||||
# in the submitted JSON for any administrative command requests when
|
||||
# invoking JSON-RPC commands on remote servers.
|
||||
#
|
||||
# secure_gateway = [ IP, IP, IP, ... ]
|
||||
#
|
||||
# A comma-separated list of IP addresses.
|
||||
#
|
||||
# When set, allows the specified IP addresses to pass HTTP headers
|
||||
# containing username and remote IP address for each session. If a
|
||||
# non-empty username is passed in this way, then resource controls
|
||||
# such as often resulting in "tooBusy" errors will be lifted. However,
|
||||
# administrative RPC commands such as "stop" will not be allowed.
|
||||
# The HTTP headers that secure_gateway hosts can set are X-User and
|
||||
# X-Forwarded-For. Only the X-User header affects resource controls.
|
||||
# However, both header values are logged to help identify user activity.
|
||||
# If no X-User header is passed, or if its value is empty, then
|
||||
# resource controls will default to those for non-administrative users.
|
||||
#
|
||||
# The secure_gateway IP addresses are intended to represent
|
||||
# proxies. Since rippled trusts these hosts, they must be
|
||||
# responsible for properly authenticating the remote user.
|
||||
#
|
||||
# The same IP address cannot be used in both "admin" and "secure_gateway"
|
||||
# lists for the same port. In this case, rippled will abort with an error
|
||||
# message to the console shortly after startup
|
||||
#
|
||||
# ssl_key = <filename>
|
||||
# ssl_cert = <filename>
|
||||
# ssl_chain = <filename>
|
||||
|
||||
@@ -221,18 +221,18 @@ public:
|
||||
|
||||
void processTransaction (
|
||||
std::shared_ptr<Transaction>& transaction,
|
||||
bool bAdmin, bool bLocal, FailHard failType) override;
|
||||
bool bUnlimited, bool bLocal, FailHard failType) override;
|
||||
|
||||
/**
|
||||
* For transactions submitted directly by a client, apply batch of
|
||||
* transactions and wait for this transaction to complete.
|
||||
*
|
||||
* @param transaction Transaction object.
|
||||
* @param bAdmin Whether an administrative client connection submitted it.
|
||||
* @param bUnliimited Whether a privileged client connection submitted it.
|
||||
* @param failType fail_hard setting from transaction submission.
|
||||
*/
|
||||
void doTransactionSync (std::shared_ptr<Transaction> transaction,
|
||||
bool bAdmin, FailHard failType);
|
||||
bool bUnlimited, FailHard failType);
|
||||
|
||||
/**
|
||||
* For transactions not submitted by a locally connected client, fire and
|
||||
@@ -240,11 +240,11 @@ public:
|
||||
* currently being applied.
|
||||
*
|
||||
* @param transaction Transaction object
|
||||
* @param bAdmin Whether an administrative client connection submitted it.
|
||||
* @param bUnlimited Whether a privileged client connection submitted it.
|
||||
* @param failType fail_hard setting from transaction submission.
|
||||
*/
|
||||
void doTransactionAsync (std::shared_ptr<Transaction> transaction,
|
||||
bool bAdmin, FailHard failtype);
|
||||
bool bUnlimited, FailHard failtype);
|
||||
|
||||
/**
|
||||
* Apply transactions in batches. Continue until none are queued.
|
||||
@@ -270,7 +270,7 @@ public:
|
||||
// Book functions.
|
||||
//
|
||||
|
||||
void getBookPage (bool bAdmin, std::shared_ptr<ReadView const>& lpLedger,
|
||||
void getBookPage (bool bUnlimited, std::shared_ptr<ReadView const>& lpLedger,
|
||||
Book const&, AccountID const& uTakerID, const bool bProof,
|
||||
const unsigned int iLimit,
|
||||
Json::Value const& jvMarker, Json::Value& jvResult)
|
||||
@@ -366,19 +366,19 @@ public:
|
||||
std::string selection, AccountID const& account,
|
||||
std::int32_t minLedger, std::int32_t maxLedger,
|
||||
bool descending, std::uint32_t offset, int limit,
|
||||
bool binary, bool count, bool bAdmin);
|
||||
bool binary, bool count, bool bUnlimited);
|
||||
|
||||
// Client information retrieval functions.
|
||||
using NetworkOPs::AccountTxs;
|
||||
AccountTxs getAccountTxs (
|
||||
AccountID const& account,
|
||||
std::int32_t minLedger, std::int32_t maxLedger, bool descending,
|
||||
std::uint32_t offset, int limit, bool bAdmin) override;
|
||||
std::uint32_t offset, int limit, bool bUnlimited) override;
|
||||
|
||||
AccountTxs getTxsAccount (
|
||||
AccountID const& account, std::int32_t minLedger,
|
||||
std::int32_t maxLedger, bool forward, Json::Value& token, int limit,
|
||||
bool bAdmin) override;
|
||||
bool bUnlimited) override;
|
||||
|
||||
using NetworkOPs::txnMetaLedgerType;
|
||||
using NetworkOPs::MetaTxsList;
|
||||
@@ -387,13 +387,13 @@ public:
|
||||
getAccountTxsB (
|
||||
AccountID const& account, std::int32_t minLedger,
|
||||
std::int32_t maxLedger, bool descending, std::uint32_t offset,
|
||||
int limit, bool bAdmin) override;
|
||||
int limit, bool bUnlimited) override;
|
||||
|
||||
MetaTxsList
|
||||
getTxsAccountB (
|
||||
AccountID const& account, std::int32_t minLedger,
|
||||
std::int32_t maxLedger, bool forward, Json::Value& token,
|
||||
int limit, bool bAdmin) override;
|
||||
int limit, bool bUnlimited) override;
|
||||
|
||||
//
|
||||
// Monitoring: publisher side.
|
||||
@@ -787,7 +787,7 @@ void NetworkOPsImp::submitTransaction (std::shared_ptr<STTx const> const& iTrans
|
||||
}
|
||||
|
||||
void NetworkOPsImp::processTransaction (std::shared_ptr<Transaction>& transaction,
|
||||
bool bAdmin, bool bLocal, FailHard failType)
|
||||
bool bUnlimited, bool bLocal, FailHard failType)
|
||||
{
|
||||
auto ev = m_job_queue.getLoadEventAP (jtTXN_PROC, "ProcessTXN");
|
||||
auto const newFlags = app_.getHashRouter ().getFlags (transaction->getID ());
|
||||
@@ -826,20 +826,20 @@ void NetworkOPsImp::processTransaction (std::shared_ptr<Transaction>& transactio
|
||||
app_.getMasterTransaction ().canonicalize (&transaction);
|
||||
|
||||
if (bLocal)
|
||||
doTransactionSync (transaction, bAdmin, failType);
|
||||
doTransactionSync (transaction, bUnlimited, failType);
|
||||
else
|
||||
doTransactionAsync (transaction, bAdmin, failType);
|
||||
doTransactionAsync (transaction, bUnlimited, failType);
|
||||
}
|
||||
|
||||
void NetworkOPsImp::doTransactionAsync (std::shared_ptr<Transaction> transaction,
|
||||
bool bAdmin, FailHard failType)
|
||||
bool bUnlimited, FailHard failType)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mMutex);
|
||||
|
||||
if (transaction->getApplying())
|
||||
return;
|
||||
|
||||
mTransactions.push_back (TransactionStatus (transaction, bAdmin, false,
|
||||
mTransactions.push_back (TransactionStatus (transaction, bUnlimited, false,
|
||||
failType));
|
||||
transaction->setApplying();
|
||||
|
||||
@@ -852,14 +852,14 @@ void NetworkOPsImp::doTransactionAsync (std::shared_ptr<Transaction> transaction
|
||||
}
|
||||
|
||||
void NetworkOPsImp::doTransactionSync (std::shared_ptr<Transaction> transaction,
|
||||
bool bAdmin, FailHard failType)
|
||||
bool bUnlimited, FailHard failType)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock (mMutex);
|
||||
|
||||
if (! transaction->getApplying())
|
||||
{
|
||||
mTransactions.push_back (TransactionStatus (transaction, bAdmin, true,
|
||||
failType));
|
||||
mTransactions.push_back (TransactionStatus (transaction, bUnlimited,
|
||||
true, failType));
|
||||
transaction->setApplying();
|
||||
}
|
||||
|
||||
@@ -925,7 +925,7 @@ void NetworkOPsImp::apply (std::unique_lock<std::mutex>& batchLock)
|
||||
// we check before addingto the batch
|
||||
ApplyFlags flags = tapNO_CHECK_SIGN;
|
||||
if (e.admin)
|
||||
flags = flags | tapADMIN;
|
||||
flags = flags | tapUNLIMITED;
|
||||
|
||||
auto const result = app_.getTxQ().apply(
|
||||
app_, view, e.transaction->getSTransaction(),
|
||||
@@ -1690,7 +1690,7 @@ NetworkOPsImp::transactionsSQL (
|
||||
std::string selection, AccountID const& account,
|
||||
std::int32_t minLedger, std::int32_t maxLedger, bool descending,
|
||||
std::uint32_t offset, int limit,
|
||||
bool binary, bool count, bool bAdmin)
|
||||
bool binary, bool count, bool bUnlimited)
|
||||
{
|
||||
std::uint32_t NONBINARY_PAGE_LENGTH = 200;
|
||||
std::uint32_t BINARY_PAGE_LENGTH = 500;
|
||||
@@ -1705,7 +1705,7 @@ NetworkOPsImp::transactionsSQL (
|
||||
{
|
||||
numberOfResults = binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH;
|
||||
}
|
||||
else if (!bAdmin)
|
||||
else if (!bUnlimited)
|
||||
{
|
||||
numberOfResults = std::min (
|
||||
binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH,
|
||||
@@ -1772,14 +1772,15 @@ NetworkOPsImp::transactionsSQL (
|
||||
NetworkOPs::AccountTxs NetworkOPsImp::getAccountTxs (
|
||||
AccountID const& account,
|
||||
std::int32_t minLedger, std::int32_t maxLedger, bool descending,
|
||||
std::uint32_t offset, int limit, bool bAdmin)
|
||||
std::uint32_t offset, int limit, bool bUnlimited)
|
||||
{
|
||||
// can be called with no locks
|
||||
AccountTxs ret;
|
||||
|
||||
std::string sql = transactionsSQL (
|
||||
"AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta", account,
|
||||
minLedger, maxLedger, descending, offset, limit, false, false, bAdmin);
|
||||
minLedger, maxLedger, descending, offset, limit, false, false,
|
||||
bUnlimited);
|
||||
|
||||
{
|
||||
auto db = app_.getTxnDB ().checkoutDb ();
|
||||
@@ -1837,7 +1838,7 @@ NetworkOPs::AccountTxs NetworkOPsImp::getAccountTxs (
|
||||
std::vector<NetworkOPsImp::txnMetaLedgerType> NetworkOPsImp::getAccountTxsB (
|
||||
AccountID const& account,
|
||||
std::int32_t minLedger, std::int32_t maxLedger, bool descending,
|
||||
std::uint32_t offset, int limit, bool bAdmin)
|
||||
std::uint32_t offset, int limit, bool bUnlimited)
|
||||
{
|
||||
// can be called with no locks
|
||||
std::vector<txnMetaLedgerType> ret;
|
||||
@@ -1845,7 +1846,7 @@ std::vector<NetworkOPsImp::txnMetaLedgerType> NetworkOPsImp::getAccountTxsB (
|
||||
std::string sql = transactionsSQL (
|
||||
"AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta", account,
|
||||
minLedger, maxLedger, descending, offset, limit, true/*binary*/, false,
|
||||
bAdmin);
|
||||
bUnlimited);
|
||||
|
||||
{
|
||||
auto db = app_.getTxnDB ().checkoutDb ();
|
||||
@@ -1887,7 +1888,7 @@ NetworkOPsImp::AccountTxs
|
||||
NetworkOPsImp::getTxsAccount (
|
||||
AccountID const& account, std::int32_t minLedger,
|
||||
std::int32_t maxLedger, bool forward, Json::Value& token,
|
||||
int limit, bool bAdmin)
|
||||
int limit, bool bUnlimited)
|
||||
{
|
||||
static std::uint32_t const page_length (200);
|
||||
|
||||
@@ -1907,7 +1908,7 @@ NetworkOPsImp::getTxsAccount (
|
||||
accountTxPage(app_.getTxnDB (), app_.accountIDCache(),
|
||||
std::bind(saveLedgerAsync, std::ref(app_),
|
||||
std::placeholders::_1), bound, account, minLedger,
|
||||
maxLedger, forward, token, limit, bAdmin,
|
||||
maxLedger, forward, token, limit, bUnlimited,
|
||||
page_length);
|
||||
|
||||
return ret;
|
||||
@@ -1917,7 +1918,7 @@ NetworkOPsImp::MetaTxsList
|
||||
NetworkOPsImp::getTxsAccountB (
|
||||
AccountID const& account, std::int32_t minLedger,
|
||||
std::int32_t maxLedger, bool forward, Json::Value& token,
|
||||
int limit, bool bAdmin)
|
||||
int limit, bool bUnlimited)
|
||||
{
|
||||
static const std::uint32_t page_length (500);
|
||||
|
||||
@@ -1935,7 +1936,7 @@ NetworkOPsImp::getTxsAccountB (
|
||||
accountTxPage(app_.getTxnDB (), app_.accountIDCache(),
|
||||
std::bind(saveLedgerAsync, std::ref(app_),
|
||||
std::placeholders::_1), bound, account, minLedger,
|
||||
maxLedger, forward, token, limit, bAdmin,
|
||||
maxLedger, forward, token, limit, bUnlimited,
|
||||
page_length);
|
||||
return ret;
|
||||
}
|
||||
@@ -2703,7 +2704,7 @@ InfoSub::pointer NetworkOPsImp::addRpcSub (
|
||||
//
|
||||
// FIXME : support iLimit.
|
||||
void NetworkOPsImp::getBookPage (
|
||||
bool bAdmin,
|
||||
bool bUnlimited,
|
||||
std::shared_ptr<ReadView const>& lpLedger,
|
||||
Book const& book,
|
||||
AccountID const& uTakerID,
|
||||
@@ -2746,7 +2747,7 @@ void NetworkOPsImp::getBookPage (
|
||||
auto viewJ = app_.journal ("View");
|
||||
|
||||
unsigned int left (iLimit == 0 ? 300 : iLimit);
|
||||
if (! bAdmin && left > 300)
|
||||
if (! bUnlimited && left > 300)
|
||||
left = 300;
|
||||
|
||||
while (!bDone && left-- > 0)
|
||||
@@ -2927,7 +2928,7 @@ void NetworkOPsImp::getBookPage (
|
||||
|
||||
// FIXME : support iLimit.
|
||||
void NetworkOPsImp::getBookPage (
|
||||
bool bAdmin,
|
||||
bool bUnlimited,
|
||||
std::shared_ptr<ReadView const> lpLedger,
|
||||
Book const& book,
|
||||
AccountID const& uTakerID,
|
||||
@@ -2949,7 +2950,7 @@ void NetworkOPsImp::getBookPage (
|
||||
lesActive.isGlobalFrozen (book.in.account);
|
||||
|
||||
unsigned int left (iLimit == 0 ? 300 : iLimit);
|
||||
if (! bAdmin && left > 300)
|
||||
if (! bUnlimited && left > 300)
|
||||
left = 300;
|
||||
|
||||
while (left-- > 0 && obIterator.nextOffer ())
|
||||
|
||||
@@ -118,12 +118,12 @@ public:
|
||||
* submitted by clients. Process local transactions synchronously
|
||||
*
|
||||
* @param transaction Transaction object
|
||||
* @param bAdmin Whether an administrative client connection submitted it.
|
||||
* @param bUnlimited Whether a privileged client connection submitted it.
|
||||
* @param bLocal Client submission.
|
||||
* @param failType fail_hard setting from transaction submission.
|
||||
*/
|
||||
virtual void processTransaction (std::shared_ptr<Transaction>& transaction,
|
||||
bool bAdmin, bool bLocal, FailHard failType) = 0;
|
||||
bool bUnlimited, bool bLocal, FailHard failType) = 0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
@@ -139,7 +139,7 @@ public:
|
||||
//
|
||||
|
||||
virtual void getBookPage (
|
||||
bool bAdmin,
|
||||
bool bUnlimited,
|
||||
std::shared_ptr<ReadView const>& lpLedger,
|
||||
Book const& book,
|
||||
AccountID const& uTakerID,
|
||||
@@ -205,23 +205,23 @@ public:
|
||||
virtual AccountTxs getAccountTxs (
|
||||
AccountID const& account,
|
||||
std::int32_t minLedger, std::int32_t maxLedger, bool descending,
|
||||
std::uint32_t offset, int limit, bool bAdmin) = 0;
|
||||
std::uint32_t offset, int limit, bool bUnlimited) = 0;
|
||||
|
||||
virtual AccountTxs getTxsAccount (
|
||||
AccountID const& account,
|
||||
std::int32_t minLedger, std::int32_t maxLedger, bool forward,
|
||||
Json::Value& token, int limit, bool bAdmin) = 0;
|
||||
Json::Value& token, int limit, bool bUnlimited) = 0;
|
||||
|
||||
using txnMetaLedgerType = std::tuple<std::string, std::string, std::uint32_t>;
|
||||
using MetaTxsList = std::vector<txnMetaLedgerType>;
|
||||
|
||||
virtual MetaTxsList getAccountTxsB (AccountID const& account,
|
||||
std::int32_t minLedger, std::int32_t maxLedger, bool descending,
|
||||
std::uint32_t offset, int limit, bool bAdmin) = 0;
|
||||
std::uint32_t offset, int limit, bool bUnlimited) = 0;
|
||||
|
||||
virtual MetaTxsList getTxsAccountB (AccountID const& account,
|
||||
std::int32_t minLedger, std::int32_t maxLedger, bool forward,
|
||||
Json::Value& token, int limit, bool bAdmin) = 0;
|
||||
Json::Value& token, int limit, bool bUnlimited) = 0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
@@ -98,7 +98,7 @@ calculateFee(Application& app, std::uint64_t const baseFee,
|
||||
Fees const& fees, ApplyFlags flags)
|
||||
{
|
||||
return app.getFeeTrack().scaleFeeLoad(
|
||||
baseFee, fees.base, fees.units, flags & tapADMIN);
|
||||
baseFee, fees.base, fees.units, flags & tapUNLIMITED);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
|
||||
// Scale using load as well as base rate
|
||||
std::uint64_t scaleFeeLoad (std::uint64_t fee, std::uint64_t baseFee,
|
||||
std::uint32_t referenceFeeUnits, bool bAdmin) const;
|
||||
std::uint32_t referenceFeeUnits, bool bUnlimited) const;
|
||||
|
||||
void setRemoteFee (std::uint32_t f)
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@ LoadFeeTrack::scaleFeeBase (std::uint64_t fee, std::uint64_t baseFee,
|
||||
// Scale using load as well as base rate
|
||||
std::uint64_t
|
||||
LoadFeeTrack::scaleFeeLoad (std::uint64_t fee, std::uint64_t baseFee,
|
||||
std::uint32_t referenceFeeUnits, bool bAdmin) const
|
||||
std::uint32_t referenceFeeUnits, bool bUnlimited) const
|
||||
{
|
||||
if (fee == 0)
|
||||
return fee;
|
||||
@@ -49,9 +49,9 @@ LoadFeeTrack::scaleFeeLoad (std::uint64_t fee, std::uint64_t baseFee,
|
||||
feeFactor = std::max(mLocalTxnLoadFee, mRemoteTxnLoadFee);
|
||||
uRemFee = std::max(mRemoteTxnLoadFee, mClusterTxnLoadFee);
|
||||
}
|
||||
// Let admins pay the normal fee until
|
||||
// Let privileged users pay the normal fee until
|
||||
// the local load exceeds four times the remote.
|
||||
if (bAdmin && (feeFactor > uRemFee) && (feeFactor < (4 * uRemFee)))
|
||||
if (bUnlimited && (feeFactor > uRemFee) && (feeFactor < (4 * uRemFee)))
|
||||
feeFactor = uRemFee;
|
||||
|
||||
// Compute:
|
||||
|
||||
@@ -47,7 +47,7 @@ enum ApplyFlags
|
||||
tapRETRY = 0x20,
|
||||
|
||||
// Transaction came from a privileged source
|
||||
tapADMIN = 0x400,
|
||||
tapUNLIMITED = 0x400,
|
||||
};
|
||||
|
||||
inline
|
||||
|
||||
@@ -318,6 +318,7 @@ JSS ( response ); // websocket
|
||||
JSS ( result ); // RPC
|
||||
JSS ( ripple_lines ); // out: NetworkOPs
|
||||
JSS ( ripple_state ); // in: LedgerEntr
|
||||
JSS ( role ); // out: Ping.cpp
|
||||
JSS ( rt_accounts ); // in: Subscribe, Unsubscribe
|
||||
JSS ( sanity ); // out: PeerImp
|
||||
JSS ( search_depth ); // in: RipplePathFind
|
||||
@@ -387,6 +388,7 @@ JSS ( type ); // in: AccountObjects
|
||||
// paths/Node.cpp, OverlayImpl, Logic
|
||||
JSS ( type_hex ); // out: STPathSet
|
||||
JSS ( unl ); // out: UnlList
|
||||
JSS ( unlimited); // out: Connection.h
|
||||
JSS ( uptime ); // out: GetCounts
|
||||
JSS ( url ); // in/out: Subscribe, Unsubscribe
|
||||
JSS ( url_password ); // in: Subscribe
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
std::string to_string () const;
|
||||
|
||||
/** Returns `true` if this is a privileged endpoint. */
|
||||
bool admin () const;
|
||||
bool isUnlimited () const;
|
||||
|
||||
/** Raise the Consumer's privilege level to a Named endpoint.
|
||||
The reference to the original endpoint descriptor is released.
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
virtual Consumer newOutboundEndpoint (beast::IP::Endpoint const& address) = 0;
|
||||
|
||||
/** Create a new endpoint keyed by name. */
|
||||
virtual Consumer newAdminEndpoint (std::string const& name) = 0;
|
||||
virtual Consumer newUnlimitedEndpoint (std::string const& name) = 0;
|
||||
|
||||
/** Extract packaged consumer information for export. */
|
||||
virtual Gossip exportConsumers () = 0;
|
||||
|
||||
@@ -78,20 +78,14 @@ std::string Consumer::to_string () const
|
||||
return m_entry->to_string();
|
||||
}
|
||||
|
||||
bool Consumer::admin () const
|
||||
bool Consumer::isUnlimited () const
|
||||
{
|
||||
if (m_entry)
|
||||
return m_entry->admin();
|
||||
return m_entry->isUnlimited();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Consumer::elevate (std::string const& name)
|
||||
{
|
||||
bassert (m_entry != nullptr);
|
||||
m_entry = &m_logic->elevateToAdminEndpoint (*m_entry, name);
|
||||
}
|
||||
|
||||
Disposition Consumer::disposition() const
|
||||
{
|
||||
Disposition d = ok;
|
||||
|
||||
@@ -56,7 +56,7 @@ struct Entry
|
||||
{
|
||||
case kindInbound: return key->address.to_string();
|
||||
case kindOutbound: return key->address.to_string();
|
||||
case kindAdmin: return std::string ("\"") + key->name + "\"";
|
||||
case kindUnlimited: return std::string ("\"") + key->name + "\"";
|
||||
default:
|
||||
bassertfalse;
|
||||
}
|
||||
@@ -64,10 +64,14 @@ struct Entry
|
||||
return "(undefined)";
|
||||
}
|
||||
|
||||
// Returns `true` if this connection is privileged
|
||||
bool admin () const
|
||||
/**
|
||||
* Returns `true` if this connection should have no
|
||||
* resource limits applied--it is still possible for certain RPC commands
|
||||
* to be forbidden, but that depends on Role.
|
||||
*/
|
||||
bool isUnlimited () const
|
||||
{
|
||||
return key->kind == kindAdmin;
|
||||
return key->kind == kindUnlimited;
|
||||
}
|
||||
|
||||
// Balance including remote contributions
|
||||
|
||||
@@ -36,23 +36,19 @@ struct Key
|
||||
|
||||
Key () = delete;
|
||||
|
||||
// Constructor for Inbound and Outbound (non-Admin) keys
|
||||
// Constructor for Inbound and Outbound (non-Unlimited) keys
|
||||
Key (Kind k, beast::IP::Endpoint const& addr)
|
||||
: kind(k)
|
||||
, address(addr)
|
||||
, name()
|
||||
{
|
||||
assert(kind != kindAdmin);
|
||||
assert(kind != kindUnlimited);
|
||||
}
|
||||
|
||||
// Constructor for Admin keys
|
||||
Key (Kind k, std::string const& n)
|
||||
: kind(k)
|
||||
, address()
|
||||
// Constructor for Unlimited keys
|
||||
Key (std::string const& n)
|
||||
: kind(kindUnlimited)
|
||||
, name(n)
|
||||
{
|
||||
assert(kind == kindAdmin);
|
||||
}
|
||||
{}
|
||||
|
||||
struct hasher
|
||||
{
|
||||
@@ -64,7 +60,7 @@ struct Key
|
||||
case kindOutbound:
|
||||
return m_addr_hash (v.address);
|
||||
|
||||
case kindAdmin:
|
||||
case kindUnlimited:
|
||||
return m_name_hash (v.name);
|
||||
|
||||
default:
|
||||
@@ -92,7 +88,7 @@ struct Key
|
||||
case kindOutbound:
|
||||
return lhs.address == rhs.address;
|
||||
|
||||
case kindAdmin:
|
||||
case kindUnlimited:
|
||||
return lhs.name == rhs.name;
|
||||
|
||||
default:
|
||||
|
||||
@@ -23,12 +23,19 @@
|
||||
namespace ripple {
|
||||
namespace Resource {
|
||||
|
||||
// Kind of consumer
|
||||
/**
|
||||
* Kind of consumer.
|
||||
* kindInbound: Inbound connection.
|
||||
* kindOutbound: Outbound connection.
|
||||
* kindUnlimited: Inbound connection with no resource limits, but could be
|
||||
* subjected to administrative restrictions, such as
|
||||
* use of some RPC commands like "stop".
|
||||
*/
|
||||
enum Kind
|
||||
{
|
||||
kindInbound
|
||||
,kindOutbound
|
||||
,kindAdmin
|
||||
,kindUnlimited
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -165,7 +165,12 @@ public:
|
||||
return Consumer (*this, *entry);
|
||||
}
|
||||
|
||||
Consumer newAdminEndpoint (std::string const& name)
|
||||
/**
|
||||
* Create endpoint that should not have resource limits applied. Other
|
||||
* restrictions, such as permission to perform certain RPC calls, may be
|
||||
* enabled.
|
||||
*/
|
||||
Consumer newUnlimitedEndpoint (std::string const& name)
|
||||
{
|
||||
Entry* entry (nullptr);
|
||||
|
||||
@@ -173,7 +178,7 @@ public:
|
||||
std::lock_guard<std::recursive_mutex> _(lock_);
|
||||
auto result =
|
||||
table_.emplace (std::piecewise_construct,
|
||||
std::make_tuple (kindAdmin, name), // Key
|
||||
std::make_tuple (name), // Key
|
||||
std::make_tuple (m_clock.now())); // Entry
|
||||
|
||||
entry = &result.first->second;
|
||||
@@ -189,42 +194,11 @@ public:
|
||||
}
|
||||
|
||||
m_journal.debug <<
|
||||
"New admin endpoint " << *entry;
|
||||
"New unlimited endpoint " << *entry;
|
||||
|
||||
return Consumer (*this, *entry);
|
||||
}
|
||||
|
||||
Entry& elevateToAdminEndpoint (Entry& prior, std::string const& name)
|
||||
{
|
||||
m_journal.info <<
|
||||
"Elevate " << prior << " to " << name;
|
||||
|
||||
Entry* entry (nullptr);
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(lock_);
|
||||
auto result =
|
||||
table_.emplace (std::piecewise_construct,
|
||||
std::make_tuple (kindAdmin, name), // Key
|
||||
std::make_tuple (m_clock.now())); // Entry
|
||||
|
||||
entry = &result.first->second;
|
||||
entry->key = &result.first->first;
|
||||
++entry->refcount;
|
||||
if (entry->refcount == 1)
|
||||
{
|
||||
if (! result.second)
|
||||
inactive_.erase (
|
||||
inactive_.iterator_to (*entry));
|
||||
admin_.push_back (*entry);
|
||||
}
|
||||
|
||||
release (prior);
|
||||
}
|
||||
|
||||
return *entry;
|
||||
}
|
||||
|
||||
Json::Value getJson ()
|
||||
{
|
||||
return getJson (warningThreshold);
|
||||
@@ -450,7 +424,7 @@ public:
|
||||
outbound_.erase (
|
||||
outbound_.iterator_to (entry));
|
||||
break;
|
||||
case kindAdmin:
|
||||
case kindUnlimited:
|
||||
admin_.erase (
|
||||
admin_.iterator_to (entry));
|
||||
break;
|
||||
@@ -475,7 +449,7 @@ public:
|
||||
|
||||
bool warn (Entry& entry)
|
||||
{
|
||||
if (entry.admin())
|
||||
if (entry.isUnlimited())
|
||||
return false;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> _(lock_);
|
||||
@@ -498,7 +472,7 @@ public:
|
||||
|
||||
bool disconnect (Entry& entry)
|
||||
{
|
||||
if (entry.admin())
|
||||
if (entry.isUnlimited())
|
||||
return false;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> _(lock_);
|
||||
|
||||
@@ -73,9 +73,9 @@ public:
|
||||
return logic_.newOutboundEndpoint (address);
|
||||
}
|
||||
|
||||
Consumer newAdminEndpoint (std::string const& name) override
|
||||
Consumer newUnlimitedEndpoint (std::string const& name) override
|
||||
{
|
||||
return logic_.newAdminEndpoint (name);
|
||||
return logic_.newUnlimitedEndpoint (name);
|
||||
}
|
||||
|
||||
Gossip exportConsumers () override
|
||||
|
||||
@@ -38,6 +38,15 @@ namespace RPC {
|
||||
/** The context of information needed to call an RPC. */
|
||||
struct Context
|
||||
{
|
||||
/**
|
||||
* Data passed in from HTTP headers.
|
||||
*/
|
||||
struct Headers
|
||||
{
|
||||
std::string user;
|
||||
std::string forwardedFor;
|
||||
};
|
||||
|
||||
beast::Journal j;
|
||||
Json::Value params;
|
||||
Application& app;
|
||||
@@ -47,6 +56,7 @@ struct Context
|
||||
Role role;
|
||||
std::shared_ptr<JobCoro> jobCoro;
|
||||
InfoSub::pointer infoSub;
|
||||
Headers headers;
|
||||
};
|
||||
|
||||
} // RPC
|
||||
|
||||
@@ -129,7 +129,7 @@ Json::Value doAccountTx (RPC::Context& context)
|
||||
{
|
||||
auto txns = context.netOps.getTxsAccountB (
|
||||
*account, uLedgerMin, uLedgerMax, bForward, resumeToken, limit,
|
||||
context.role == Role::ADMIN);
|
||||
isUnlimited (context.role));
|
||||
|
||||
for (auto& it: txns)
|
||||
{
|
||||
@@ -150,7 +150,7 @@ Json::Value doAccountTx (RPC::Context& context)
|
||||
{
|
||||
auto txns = context.netOps.getTxsAccount (
|
||||
*account, uLedgerMin, uLedgerMax, bForward, resumeToken, limit,
|
||||
context.role == Role::ADMIN);
|
||||
isUnlimited (context.role));
|
||||
|
||||
for (auto& it: txns)
|
||||
{
|
||||
|
||||
@@ -147,7 +147,7 @@ Json::Value doAccountTxOld (RPC::Context& context)
|
||||
{
|
||||
auto txns = context.netOps.getAccountTxsB (
|
||||
*raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit,
|
||||
context.role == Role::ADMIN);
|
||||
isUnlimited (context.role));
|
||||
|
||||
for (auto it = txns.begin (), end = txns.end (); it != end; ++it)
|
||||
{
|
||||
@@ -169,7 +169,7 @@ Json::Value doAccountTxOld (RPC::Context& context)
|
||||
{
|
||||
auto txns = context.netOps.getAccountTxs (
|
||||
*raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit,
|
||||
context.role == Role::ADMIN);
|
||||
isUnlimited (context.role));
|
||||
|
||||
for (auto it = txns.begin (), end = txns.end (); it != end; ++it)
|
||||
{
|
||||
|
||||
@@ -183,7 +183,7 @@ Json::Value doBookOffers (RPC::Context& context)
|
||||
: Json::Value (Json::nullValue));
|
||||
|
||||
context.netOps.getBookPage (
|
||||
context.role == Role::ADMIN,
|
||||
isUnlimited (context.role),
|
||||
lpLedger,
|
||||
{{pay_currency, pay_issuer}, {get_currency, get_issuer}},
|
||||
takerID ? *takerID : zero, bProof, limit, jvMarker, jvResult);
|
||||
|
||||
@@ -69,7 +69,7 @@ Json::Value doLedgerData (RPC::Context& context)
|
||||
}
|
||||
|
||||
auto maxLimit = RPC::Tuning::pageLength(isBinary);
|
||||
if ((limit < 0) || ((limit > maxLimit) && (context.role != Role::ADMIN)))
|
||||
if ((limit < 0) || ((limit > maxLimit) && (! isUnlimited (context.role))))
|
||||
limit = maxLimit;
|
||||
|
||||
jvResult[jss::ledger_hash] = to_string (lpLedger->info().hash);
|
||||
|
||||
@@ -64,11 +64,11 @@ Status LedgerHandler::check()
|
||||
{
|
||||
// Until some sane way to get full ledgers has been implemented,
|
||||
// disallow retrieving all state nodes.
|
||||
if (context_.role != Role::ADMIN)
|
||||
if (! isUnlimited (context_.role))
|
||||
return rpcNO_PERMISSION;
|
||||
|
||||
if (context_.app.getFeeTrack().isLoadedLocal() &&
|
||||
context_.role != Role::ADMIN)
|
||||
!isUnlimited (context_.role))
|
||||
{
|
||||
return rpcTOO_BUSY;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/server/Role.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -28,7 +31,29 @@ struct Context;
|
||||
|
||||
Json::Value doPing (RPC::Context& context)
|
||||
{
|
||||
// For testing connection privileges.
|
||||
if (isUnlimited(context.role))
|
||||
{
|
||||
Json::Value ret;
|
||||
|
||||
switch (context.role)
|
||||
{
|
||||
case Role::ADMIN:
|
||||
ret[jss::role] = "admin";
|
||||
break;
|
||||
case Role::IDENTIFIED:
|
||||
ret[jss::role] = "identified";
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Json::Value (Json::objectValue);
|
||||
}
|
||||
}
|
||||
|
||||
} // ripple
|
||||
|
||||
@@ -116,7 +116,7 @@ Json::Value doRipplePathFind (RPC::Context& context)
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
RPC::LegacyPathFind lpf (context.role == Role::ADMIN, context.app);
|
||||
RPC::LegacyPathFind lpf (isUnlimited (context.role), context.app);
|
||||
if (! lpf.isOk ())
|
||||
return rpcError (rpcTOO_BUSY);
|
||||
|
||||
@@ -214,7 +214,7 @@ Json::Value doRipplePathFind (RPC::Context& context)
|
||||
&& context.params[jss::search_depth].isIntegral())
|
||||
{
|
||||
int rLev = context.params[jss::search_depth].asInt ();
|
||||
if ((rLev < level) || (context.role == Role::ADMIN))
|
||||
if ((rLev < level) || (isUnlimited (context.role)))
|
||||
level = rLev;
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ Json::Value doSubmit (RPC::Context& context)
|
||||
auto const failType = getFailHard (context);
|
||||
|
||||
context.netOps.processTransaction (
|
||||
tpTrans, context.role == Role::ADMIN, true, failType);
|
||||
tpTrans, isUnlimited (context.role), true, failType);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
|
||||
@@ -324,7 +324,7 @@ Json::Value doSubscribe (RPC::Context& context)
|
||||
|
||||
auto add = [&](Json::StaticString field)
|
||||
{
|
||||
context.netOps.getBookPage (context.role == Role::ADMIN,
|
||||
context.netOps.getBookPage (isUnlimited (context.role),
|
||||
lpLedger, field == jss::asks ? reversed (book) : book,
|
||||
takerID ? *takerID : noAccount(), false, 0, jvMarker,
|
||||
jvOffers);
|
||||
|
||||
@@ -44,7 +44,7 @@ Json::Value doTxHistory (RPC::Context& context)
|
||||
|
||||
unsigned int startIndex = context.params[jss::start].asUInt ();
|
||||
|
||||
if ((startIndex > 10000) && (context.role != Role::ADMIN))
|
||||
if ((startIndex > 10000) && (! isUnlimited (context.role)))
|
||||
return rpcError (rpcNO_PERMISSION);
|
||||
|
||||
Json::Value obj;
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace {
|
||||
error_code_i fillHandler (Context& context,
|
||||
boost::optional<Handler const&>& result)
|
||||
{
|
||||
if (context.role != Role::ADMIN)
|
||||
if (! isUnlimited (context.role))
|
||||
{
|
||||
// VFALCO NOTE Should we also add up the jtRPC jobs?
|
||||
//
|
||||
@@ -232,7 +232,27 @@ Status doCommand (
|
||||
}
|
||||
|
||||
if (auto method = handler->valueMethod_)
|
||||
{
|
||||
if (! context.headers.user.empty() ||
|
||||
! context.headers.forwardedFor.empty())
|
||||
{
|
||||
context.j.debug << "start command: " << handler->name_ <<
|
||||
", X-User: " << context.headers.user << ", X-Forwarded-For: " <<
|
||||
context.headers.forwardedFor;
|
||||
|
||||
auto ret = callMethod (context, method, handler->name_, result);
|
||||
|
||||
context.j.debug << "finish command: " << handler->name_ <<
|
||||
", X-User: " << context.headers.user << ", X-Forwarded-For: " <<
|
||||
context.headers.forwardedFor;
|
||||
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
return callMethod (context, method, handler->name_, result);
|
||||
}
|
||||
}
|
||||
|
||||
return rpcUNKNOWN_COMMAND;
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ static Json::Value checkPayment(
|
||||
"Cannot build XRP to XRP paths.");
|
||||
|
||||
{
|
||||
LegacyPathFind lpf (role == Role::ADMIN, app);
|
||||
LegacyPathFind lpf (isUnlimited (role), app);
|
||||
if (!lpf.isOk ())
|
||||
return rpcError (rpcTOO_BUSY);
|
||||
|
||||
@@ -284,7 +284,7 @@ checkTxJsonFields (
|
||||
}
|
||||
|
||||
// Check for load.
|
||||
if (feeTrack.isLoadedCluster() && (role != Role::ADMIN))
|
||||
if (feeTrack.isLoadedCluster() && !isUnlimited (role))
|
||||
{
|
||||
ret.first = rpcError (rpcTOO_BUSY);
|
||||
return ret;
|
||||
@@ -646,10 +646,10 @@ Json::Value checkFee (
|
||||
// Default fee in fee units.
|
||||
std::uint64_t const feeDefault = config.TRANSACTION_FEE_BASE;
|
||||
|
||||
// Administrative endpoints are exempt from local fees.
|
||||
// Administrative and identified endpoints are exempt from local fees.
|
||||
std::uint64_t const fee =
|
||||
feeTrack.scaleFeeLoad (feeDefault,
|
||||
ledger->fees().base, ledger->fees().units, role == Role::ADMIN);
|
||||
ledger->fees().base, ledger->fees().units, isUnlimited (role));
|
||||
|
||||
std::uint64_t const limit = mult * feeTrack.scaleFeeBase (
|
||||
feeDefault, ledger->fees().base, ledger->fees().units);
|
||||
@@ -741,7 +741,7 @@ Json::Value transactionSubmit (
|
||||
{
|
||||
// FIXME: For performance, should use asynch interface
|
||||
processTransaction (
|
||||
txn.second, role == Role::ADMIN, true, failType);
|
||||
txn.second, isUnlimited (role), true, failType);
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
@@ -1112,7 +1112,7 @@ Json::Value transactionSubmitMultiSigned (
|
||||
{
|
||||
// FIXME: For performance, should use asynch interface
|
||||
processTransaction (
|
||||
txn.second, role == Role::ADMIN, true, failType);
|
||||
txn.second, isUnlimited (role), true, failType);
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
|
||||
@@ -65,14 +65,14 @@ Json::Value checkFee (
|
||||
// Return a std::function<> that calls NetworkOPs::processTransaction.
|
||||
using ProcessTransactionFn =
|
||||
std::function<void (std::shared_ptr<Transaction>& transaction,
|
||||
bool bAdmin, bool bLocal, NetworkOPs::FailHard failType)>;
|
||||
bool bUnlimited, bool bLocal, NetworkOPs::FailHard failType)>;
|
||||
|
||||
inline ProcessTransactionFn getProcessTxnFn (NetworkOPs& netOPs)
|
||||
{
|
||||
return [&netOPs](std::shared_ptr<Transaction>& transaction,
|
||||
bool bAdmin, bool bLocal, NetworkOPs::FailHard failType)
|
||||
bool bUnlimited, bool bLocal, NetworkOPs::FailHard failType)
|
||||
{
|
||||
netOPs.processTransaction(transaction, bAdmin, bLocal, failType);
|
||||
netOPs.processTransaction(transaction, bUnlimited, bLocal, failType);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ boost::optional<Json::Value> readLimitField(
|
||||
return RPC::expected_field_error (jss::limit, "unsigned integer");
|
||||
|
||||
limit = jvLimit.asUInt();
|
||||
if (context.role != Role::ADMIN)
|
||||
if (! isUnlimited (context.role))
|
||||
limit = std::max(range.rmin, std::min(range.rmax, limit));
|
||||
}
|
||||
return boost::none;
|
||||
|
||||
@@ -41,6 +41,7 @@ struct Port
|
||||
std::uint16_t port = 0;
|
||||
std::set<std::string, beast::ci_less> protocol;
|
||||
std::vector<beast::IP::Address> admin_ip;
|
||||
std::vector<beast::IP::Address> secure_gateway_ip;
|
||||
std::string user;
|
||||
std::string password;
|
||||
std::string admin_user;
|
||||
@@ -94,22 +95,8 @@ Port::protocols() const
|
||||
return s;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream&
|
||||
operator<< (std::ostream& os, Port const& p)
|
||||
{
|
||||
os << "'" << p.name << "' (ip=" << p.ip << ":" << p.port << ", ";
|
||||
|
||||
if (! p.admin_ip.empty ())
|
||||
{
|
||||
os << "admin IPs:";
|
||||
for (auto const& ip : p.admin_ip)
|
||||
os << ip.to_string () << ", ";
|
||||
}
|
||||
|
||||
os << p.protocols () << ")";
|
||||
return os;
|
||||
}
|
||||
operator<< (std::ostream& os, Port const& p);
|
||||
|
||||
} // HTTP
|
||||
} // ripple
|
||||
|
||||
@@ -28,11 +28,17 @@
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/** Indicates the level of administrative permission to grant. */
|
||||
/** Indicates the level of administrative permission to grant.
|
||||
* IDENTIFIED role has unlimited resources but cannot perform some
|
||||
* RPC commands.
|
||||
* ADMIN role has unlimited resources and is able to perform all RPC
|
||||
* commands.
|
||||
*/
|
||||
enum class Role
|
||||
{
|
||||
GUEST,
|
||||
USER,
|
||||
IDENTIFIED,
|
||||
ADMIN,
|
||||
FORBID
|
||||
};
|
||||
@@ -46,12 +52,27 @@ enum class Role
|
||||
*/
|
||||
Role
|
||||
requestRole (Role const& required, HTTP::Port const& port,
|
||||
Json::Value const& jsonRPC, beast::IP::Endpoint const& remoteIp);
|
||||
Json::Value const& jsonRPC, beast::IP::Endpoint const& remoteIp,
|
||||
std::string const& user);
|
||||
|
||||
Resource::Consumer
|
||||
requestInboundEndpoint (Resource::Manager& manager,
|
||||
beast::IP::Endpoint const& remoteAddress,
|
||||
HTTP::Port const& port);
|
||||
HTTP::Port const& port, std::string const& user);
|
||||
|
||||
/**
|
||||
* Check if the role entitles the user to unlimited resources.
|
||||
*/
|
||||
bool
|
||||
isUnlimited (Role const& role);
|
||||
|
||||
/**
|
||||
* If the HTTP header X-User exists with a non-empty value was passed by an IP
|
||||
* configured as secure_gateway, then the user can be positively identified.
|
||||
*/
|
||||
bool
|
||||
isIdentified (HTTP::Port const& port, beast::IP::Address const& remoteIp,
|
||||
std::string const& user);
|
||||
|
||||
} // ripple
|
||||
|
||||
|
||||
@@ -68,6 +68,16 @@ public:
|
||||
beast::IP::Endpoint
|
||||
remoteAddress() = 0;
|
||||
|
||||
/** Returns the user name if behind a proxy (secure_gateway). */
|
||||
virtual
|
||||
std::string
|
||||
user() = 0;
|
||||
|
||||
/** Returns X-Forwarded-For if behind a proxy (secure_gateway). */
|
||||
virtual
|
||||
std::string
|
||||
forwarded_for() = 0;
|
||||
|
||||
/** Returns the current HTTP request. */
|
||||
virtual
|
||||
beast::http::message&
|
||||
|
||||
@@ -87,6 +87,8 @@ protected:
|
||||
boost::asio::io_service::strand strand_;
|
||||
waitable_timer timer_;
|
||||
endpoint_type remote_address_;
|
||||
std::string forwarded_for_;
|
||||
std::string user_;
|
||||
beast::Journal journal_;
|
||||
|
||||
std::string id_;
|
||||
@@ -183,6 +185,18 @@ protected:
|
||||
return beast::IPAddressConversion::from_asio(remote_address_);
|
||||
}
|
||||
|
||||
std::string
|
||||
user() override
|
||||
{
|
||||
return user_;
|
||||
}
|
||||
|
||||
std::string
|
||||
forwarded_for() override
|
||||
{
|
||||
return forwarded_for_;
|
||||
}
|
||||
|
||||
beast::http::message&
|
||||
request() override
|
||||
{
|
||||
@@ -376,10 +390,21 @@ Peer<Impl>::do_read (yield_context yield)
|
||||
if (! ec)
|
||||
{
|
||||
if (parser.complete())
|
||||
{
|
||||
auto const iter = message_.headers.find ("X-Forwarded-For");
|
||||
if (iter != message_.headers.end())
|
||||
forwarded_for_ = iter->second;
|
||||
auto const iter2 = message_.headers.find ("X-User");
|
||||
if (iter2 != message_.headers.end())
|
||||
user_ = iter2->second;
|
||||
|
||||
return do_request();
|
||||
}
|
||||
else if (eof)
|
||||
{
|
||||
ec = boost::asio::error::eof; // incomplete request
|
||||
}
|
||||
}
|
||||
|
||||
if (ec)
|
||||
return fail (ec, "read");
|
||||
|
||||
49
src/ripple/server/impl/Port.cpp
Normal file
49
src/ripple/server/impl/Port.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/server/Port.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace HTTP {
|
||||
|
||||
std::ostream&
|
||||
operator<< (std::ostream& os, Port const& p)
|
||||
{
|
||||
os << "'" << p.name << "' (ip=" << p.ip << ":" << p.port << ", ";
|
||||
|
||||
if (! p.admin_ip.empty ())
|
||||
{
|
||||
os << "admin IPs:";
|
||||
for (auto const& ip : p.admin_ip)
|
||||
os << ip.to_string () << ", ";
|
||||
}
|
||||
|
||||
if (! p.secure_gateway_ip.empty ())
|
||||
{
|
||||
os << "secure_gateway IPs:";
|
||||
for (auto const& ip : p.secure_gateway_ip)
|
||||
os << ip.to_string () << ", ";
|
||||
}
|
||||
|
||||
os << p.protocols () << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
} // HTTP
|
||||
} // ripple
|
||||
@@ -56,25 +56,59 @@ isAdmin (HTTP::Port const& port, Json::Value const& params,
|
||||
|
||||
Role
|
||||
requestRole (Role const& required, HTTP::Port const& port,
|
||||
Json::Value const& params, beast::IP::Endpoint const& remoteIp)
|
||||
Json::Value const& params, beast::IP::Endpoint const& remoteIp,
|
||||
std::string const& user)
|
||||
{
|
||||
Role role (Role::GUEST);
|
||||
if (isAdmin(port, params, remoteIp.address ()))
|
||||
role = Role::ADMIN;
|
||||
if (required == Role::ADMIN && role != required)
|
||||
role = Role::FORBID;
|
||||
return role;
|
||||
if (isAdmin(port, params, remoteIp.address()))
|
||||
return Role::ADMIN;
|
||||
|
||||
if (required == Role::ADMIN)
|
||||
return Role::FORBID;
|
||||
|
||||
if (isIdentified(port, remoteIp.address(), user))
|
||||
return Role::IDENTIFIED;
|
||||
|
||||
return Role::GUEST;
|
||||
}
|
||||
|
||||
/**
|
||||
* ADMIN and IDENTIFIED roles shall have unlimited resources.
|
||||
*/
|
||||
bool
|
||||
isUnlimited (Role const& required, HTTP::Port const& port,
|
||||
Json::Value const¶ms, beast::IP::Endpoint const& remoteIp,
|
||||
std::string const& user)
|
||||
{
|
||||
Role role = requestRole(required, port, params, remoteIp, user);
|
||||
|
||||
if (role == Role::ADMIN || role == Role::IDENTIFIED)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
isUnlimited (Role const& role)
|
||||
{
|
||||
return role == Role::ADMIN || role == Role::IDENTIFIED;
|
||||
}
|
||||
|
||||
Resource::Consumer
|
||||
requestInboundEndpoint (Resource::Manager& manager,
|
||||
beast::IP::Endpoint const& remoteAddress,
|
||||
HTTP::Port const& port)
|
||||
HTTP::Port const& port, std::string const& user)
|
||||
{
|
||||
if (requestRole (Role::GUEST, port, Json::Value(), remoteAddress) ==
|
||||
Role::ADMIN)
|
||||
return manager.newAdminEndpoint (to_string (remoteAddress));
|
||||
if (isUnlimited (Role::GUEST, port, Json::Value(), remoteAddress, user))
|
||||
return manager.newUnlimitedEndpoint (to_string (remoteAddress));
|
||||
|
||||
return manager.newInboundEndpoint(remoteAddress);
|
||||
}
|
||||
|
||||
bool
|
||||
isIdentified (HTTP::Port const& port, beast::IP::Address const& remoteIp,
|
||||
std::string const& user)
|
||||
{
|
||||
return ! user.empty() && ipAllowed (remoteIp, port.secure_gateway_ip);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -204,7 +204,8 @@ ServerHandlerImp::processSession (std::shared_ptr<HTTP::Session> const& session,
|
||||
std::shared_ptr<JobCoro> jobCoro)
|
||||
{
|
||||
processRequest (session->port(), to_string (session->body()),
|
||||
session->remoteAddress().at_port (0), makeOutput (*session), jobCoro);
|
||||
session->remoteAddress().at_port (0), makeOutput (*session), jobCoro,
|
||||
session->forwarded_for(), session->user());
|
||||
|
||||
if (session->request().keep_alive())
|
||||
session->complete();
|
||||
@@ -215,7 +216,8 @@ ServerHandlerImp::processSession (std::shared_ptr<HTTP::Session> const& session,
|
||||
void
|
||||
ServerHandlerImp::processRequest (HTTP::Port const& port,
|
||||
std::string const& request, beast::IP::Endpoint const& remoteIPAddress,
|
||||
Output&& output, std::shared_ptr<JobCoro> jobCoro)
|
||||
Output&& output, std::shared_ptr<JobCoro> jobCoro,
|
||||
std::string forwardedFor, std::string user)
|
||||
{
|
||||
auto rpcJ = app_.journal ("RPC");
|
||||
// Move off the webserver thread onto the JobQueue.
|
||||
@@ -260,18 +262,28 @@ ServerHandlerImp::processRequest (HTTP::Port const& port,
|
||||
jsonRPC["params"][Json::UInt(0)].isObject())
|
||||
{
|
||||
role = requestRole(required, port, jsonRPC["params"][Json::UInt(0)],
|
||||
remoteIPAddress);
|
||||
remoteIPAddress, user);
|
||||
}
|
||||
else
|
||||
{
|
||||
role = requestRole(required, port, Json::objectValue,
|
||||
remoteIPAddress);
|
||||
remoteIPAddress, user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear header-assigned values if not positively identified from a
|
||||
* secure_gateway.
|
||||
*/
|
||||
if (role != Role::IDENTIFIED)
|
||||
{
|
||||
forwardedFor.clear();
|
||||
user.clear();
|
||||
}
|
||||
|
||||
Resource::Consumer usage;
|
||||
|
||||
if (role == Role::ADMIN)
|
||||
usage = m_resourceManager.newAdminEndpoint (
|
||||
if (isUnlimited (role))
|
||||
usage = m_resourceManager.newUnlimitedEndpoint (
|
||||
remoteIPAddress.to_string());
|
||||
else
|
||||
usage = m_resourceManager.newInboundEndpoint(remoteIPAddress);
|
||||
@@ -338,7 +350,8 @@ ServerHandlerImp::processRequest (HTTP::Port const& port,
|
||||
auto const start (std::chrono::high_resolution_clock::now ());
|
||||
|
||||
RPC::Context context {m_journal, params, app_, loadType, m_networkOPs,
|
||||
app_.getLedgerMaster(), role, jobCoro};
|
||||
app_.getLedgerMaster(), role, jobCoro, InfoSub::pointer(),
|
||||
{user, forwardedFor}};
|
||||
Json::Value result;
|
||||
RPC::doCommand (context, result);
|
||||
|
||||
@@ -474,8 +487,73 @@ struct ParsedPort
|
||||
boost::optional<boost::asio::ip::address> ip;
|
||||
boost::optional<std::uint16_t> port;
|
||||
boost::optional<std::vector<beast::IP::Address>> admin_ip;
|
||||
boost::optional<std::vector<beast::IP::Address>> secure_gateway_ip;
|
||||
};
|
||||
|
||||
void
|
||||
populate (Section const& section, std::string const& field, std::ostream& log,
|
||||
boost::optional<std::vector<beast::IP::Address>>& ips,
|
||||
bool allowAllIps, std::vector<beast::IP::Address> const& admin_ip)
|
||||
{
|
||||
auto const result = section.find(field);
|
||||
if (result.second)
|
||||
{
|
||||
std::stringstream ss (result.first);
|
||||
std::string ip;
|
||||
bool has_any (false);
|
||||
|
||||
ips.emplace();
|
||||
while (std::getline (ss, ip, ','))
|
||||
{
|
||||
auto const addr = beast::IP::Endpoint::from_string_checked (ip);
|
||||
if (! addr.second)
|
||||
{
|
||||
log << "Invalid value '" << ip << "' for key '" << field <<
|
||||
"' in [" << section.name () << "]\n";
|
||||
Throw<std::exception> ();
|
||||
}
|
||||
|
||||
if (is_unspecified (addr.first))
|
||||
{
|
||||
if (! allowAllIps)
|
||||
{
|
||||
log << "0.0.0.0 not allowed'" <<
|
||||
"' for key '" << field << "' in [" <<
|
||||
section.name () << "]\n";
|
||||
throw std::exception ();
|
||||
}
|
||||
else
|
||||
{
|
||||
has_any = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_any && ! ips->empty ())
|
||||
{
|
||||
log << "IP specified along with 0.0.0.0 '" << ip <<
|
||||
"' for key '" << field << "' in [" <<
|
||||
section.name () << "]\n";
|
||||
Throw<std::exception> ();
|
||||
}
|
||||
|
||||
auto const& address = addr.first.address();
|
||||
if (std::find_if (admin_ip.begin(), admin_ip.end(),
|
||||
[&address] (beast::IP::Address const& ip)
|
||||
{
|
||||
return address == ip;
|
||||
}
|
||||
) != admin_ip.end())
|
||||
{
|
||||
log << "IP specified for " << field << " is also for " <<
|
||||
"admin: " << ip << " in [" << section.name() << "]\n";
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
ips->emplace_back (addr.first.address ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
parse_Port (ParsedPort& port, Section const& section, std::ostream& log)
|
||||
{
|
||||
@@ -527,39 +605,9 @@ parse_Port (ParsedPort& port, Section const& section, std::ostream& log)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = section.find("admin");
|
||||
if (result.second)
|
||||
{
|
||||
std::stringstream ss (result.first);
|
||||
std::string ip;
|
||||
bool has_any (false);
|
||||
|
||||
port.admin_ip.emplace ();
|
||||
while (std::getline (ss, ip, ','))
|
||||
{
|
||||
auto const addr = beast::IP::Endpoint::from_string_checked (ip);
|
||||
if (! addr.second)
|
||||
{
|
||||
log << "Invalid value '" << ip << "' for key 'admin' in ["
|
||||
<< section.name () << "]\n";
|
||||
Throw<std::exception> ();
|
||||
}
|
||||
|
||||
if (is_unspecified (addr.first))
|
||||
has_any = true;
|
||||
|
||||
if (has_any && ! port.admin_ip->empty ())
|
||||
{
|
||||
log << "IP specified along with 0.0.0.0 '" << ip <<
|
||||
"' for key 'admin' in [" << section.name () << "]\n";
|
||||
Throw<std::exception> ();
|
||||
}
|
||||
|
||||
port.admin_ip->emplace_back (addr.first.address ());
|
||||
}
|
||||
}
|
||||
}
|
||||
populate (section, "admin", log, port.admin_ip, true, {});
|
||||
populate (section, "secure_gateway", log, port.secure_gateway_ip, false,
|
||||
port.admin_ip.get_value_or({}));
|
||||
|
||||
set(port.user, "user", section);
|
||||
set(port.password, "password", section);
|
||||
@@ -596,6 +644,8 @@ to_Port(ParsedPort const& parsed, std::ostream& log)
|
||||
p.port = *parsed.port;
|
||||
if (parsed.admin_ip)
|
||||
p.admin_ip = *parsed.admin_ip;
|
||||
if (parsed.secure_gateway_ip)
|
||||
p.secure_gateway_ip = *parsed.secure_gateway_ip;
|
||||
|
||||
if (parsed.protocol.empty())
|
||||
{
|
||||
|
||||
@@ -114,7 +114,8 @@ private:
|
||||
void
|
||||
processRequest (HTTP::Port const& port, std::string const& request,
|
||||
beast::IP::Endpoint const& remoteIPAddress, Output&&,
|
||||
std::shared_ptr<JobCoro> jobCoro);
|
||||
std::shared_ptr<JobCoro> jobCoro,
|
||||
std::string forwardedFor, std::string user);
|
||||
|
||||
//
|
||||
// PropertyStream
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <ripple/server/impl/Door.cpp>
|
||||
#include <ripple/server/impl/JSONRPCUtil.cpp>
|
||||
#include <ripple/server/impl/Port.cpp>
|
||||
#include <ripple/server/impl/Role.cpp>
|
||||
#include <ripple/server/impl/ServerImpl.cpp>
|
||||
#include <ripple/server/impl/ServerHandlerImp.cpp>
|
||||
|
||||
@@ -74,7 +74,8 @@ public:
|
||||
handler_type& handler,
|
||||
connection_ptr const& cpConnection,
|
||||
beast::IP::Endpoint const& remoteAddress,
|
||||
boost::asio::io_service& io_service);
|
||||
boost::asio::io_service& io_service,
|
||||
std::pair<std::string, std::string> identity);
|
||||
|
||||
void preDestroy ();
|
||||
|
||||
@@ -108,6 +109,8 @@ private:
|
||||
Resource::Manager& m_resourceManager;
|
||||
Resource::Consumer m_usage;
|
||||
beast::IP::Endpoint const m_remoteAddress;
|
||||
std::string const m_forwardedFor;
|
||||
std::string const m_user;
|
||||
std::mutex m_receiveQueueMutex;
|
||||
std::deque <message_ptr> m_receiveQueue;
|
||||
NetworkOPs& m_netOPs;
|
||||
@@ -133,13 +136,18 @@ ConnectionImpl <WebSocket>::ConnectionImpl (
|
||||
handler_type& handler,
|
||||
connection_ptr const& cpConnection,
|
||||
beast::IP::Endpoint const& remoteAddress,
|
||||
boost::asio::io_service& io_service)
|
||||
boost::asio::io_service& io_service,
|
||||
std::pair<std::string, std::string> identity)
|
||||
: InfoSub (source, requestInboundEndpoint (
|
||||
resourceManager, remoteAddress, handler.port()))
|
||||
resourceManager, remoteAddress, handler.port(), identity.second))
|
||||
, app_(app)
|
||||
, m_port (handler.port ())
|
||||
, m_resourceManager (resourceManager)
|
||||
, m_remoteAddress (remoteAddress)
|
||||
, m_forwardedFor (isIdentified (m_port, m_remoteAddress.address(),
|
||||
identity.second) ? identity.first : std::string())
|
||||
, m_user (isIdentified (m_port, m_remoteAddress.address(),
|
||||
identity.second) ? identity.second : std::string())
|
||||
, m_netOPs (app_.getOPs ())
|
||||
, m_io_service (io_service)
|
||||
, m_pingTimer (io_service)
|
||||
@@ -150,6 +158,12 @@ ConnectionImpl <WebSocket>::ConnectionImpl (
|
||||
{
|
||||
// VFALCO Disabled since it might cause hangs
|
||||
pingFreq_ = 0;
|
||||
|
||||
if (! m_forwardedFor.empty() || ! m_user.empty())
|
||||
{
|
||||
j_.debug << "connect secure_gateway X-Forwarded-For: " <<
|
||||
m_forwardedFor << ", X-User: " << m_user;
|
||||
}
|
||||
}
|
||||
|
||||
template <class WebSocket>
|
||||
@@ -273,7 +287,8 @@ Json::Value ConnectionImpl <WebSocket>::invokeCommand (
|
||||
Json::Value jvResult (Json::objectValue);
|
||||
|
||||
auto required = RPC::roleRequired (jvRequest[jss::command].asString());
|
||||
auto role = requestRole (required, m_port, jvRequest, m_remoteAddress);
|
||||
auto role = requestRole (required, m_port, jvRequest, m_remoteAddress,
|
||||
m_user);
|
||||
|
||||
if (Role::FORBID == role)
|
||||
{
|
||||
@@ -283,7 +298,7 @@ Json::Value ConnectionImpl <WebSocket>::invokeCommand (
|
||||
{
|
||||
RPC::Context context {app_.journal ("RPCHandler"), jvRequest,
|
||||
app_, loadType, m_netOPs, app_.getLedgerMaster(), role,
|
||||
jobCoro, this->shared_from_this ()};
|
||||
jobCoro, this->shared_from_this (), {m_user, m_forwardedFor}};
|
||||
RPC::doCommand (context, jvResult[jss::result]);
|
||||
}
|
||||
|
||||
@@ -308,6 +323,13 @@ Json::Value ConnectionImpl <WebSocket>::invokeCommand (
|
||||
else
|
||||
{
|
||||
jvResult[jss::status] = jss::success;
|
||||
|
||||
// For testing resource limits on this connection.
|
||||
if (jvRequest[jss::command].asString() == "ping")
|
||||
{
|
||||
if (getConsumer().isUnlimited())
|
||||
jvResult[jss::unlimited] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (jvRequest.isMember (jss::id))
|
||||
@@ -323,6 +345,12 @@ Json::Value ConnectionImpl <WebSocket>::invokeCommand (
|
||||
template <class WebSocket>
|
||||
void ConnectionImpl <WebSocket>::preDestroy ()
|
||||
{
|
||||
if (! m_forwardedFor.empty() || ! m_user.empty())
|
||||
{
|
||||
j_.debug << "disconnect secure_gateway X-Forwarded-For: " <<
|
||||
m_forwardedFor << ", X-User: " << m_user;
|
||||
}
|
||||
|
||||
// sever connection
|
||||
this->m_pingTimer.cancel ();
|
||||
m_connection.reset ();
|
||||
|
||||
@@ -206,7 +206,8 @@ public:
|
||||
*this,
|
||||
cpClient,
|
||||
makeBeastEndpoint (remoteEndpoint),
|
||||
WebSocket::getStrand (*cpClient).get_io_service ());
|
||||
WebSocket::getStrand (*cpClient).get_io_service (),
|
||||
cpClient->get_identity());
|
||||
connection->setPingTimer ();
|
||||
auto result = mMap.emplace (cpClient, std::move (connection));
|
||||
|
||||
|
||||
@@ -345,7 +345,27 @@ public:
|
||||
return m_strand;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Set values based on HTTP headers X-Forwarded-For and X-User to
|
||||
* be passed to Connection object. This is used to identify users
|
||||
* connecting through a secure_gateway.
|
||||
*/
|
||||
void set_identity (std::string const& forwarded_for,
|
||||
std::string const& user)
|
||||
{
|
||||
m_identity = std::make_pair (forwarded_for, user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get values derived from contents of HTTP headers X-Forwarded-For and
|
||||
* X-User. This identifies users connecting through a secure_gateway.
|
||||
*/
|
||||
std::pair<std::string, std::string> get_identity()
|
||||
{
|
||||
return m_identity;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Initialize transport for reading
|
||||
/**
|
||||
* init_asio is called once immediately after construction to initialize
|
||||
@@ -1148,6 +1168,9 @@ private:
|
||||
|
||||
async_read_handler m_async_read_handler;
|
||||
async_write_handler m_async_write_handler;
|
||||
|
||||
// Header identification: X-Forwarded-For, X-User
|
||||
std::pair<std::string, std::string> m_identity;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -753,6 +753,27 @@ public:
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
return m_strand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set values based on HTTP headers X-Forwarded-For and X-User to
|
||||
* be passed to Connection object. This is used to identify users
|
||||
* connecting through a secure_gateway.
|
||||
*/
|
||||
void set_identity (std::string const& forwarded_for,
|
||||
std::string const& user)
|
||||
{
|
||||
m_identity = std::make_pair (forwarded_for, user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get values derived from contents of HTTP headers X-Forwarded-For and
|
||||
* X-User. This identifies users connecting through a secure_gateway.
|
||||
*/
|
||||
std::pair<std::string, std::string> get_identity()
|
||||
{
|
||||
return m_identity;
|
||||
}
|
||||
|
||||
public:
|
||||
//protected: TODO: figure out why VCPP2010 doesn't like protected here
|
||||
|
||||
@@ -1522,6 +1543,9 @@ public:
|
||||
mutable boost::recursive_mutex m_lock;
|
||||
boost::asio::strand m_strand;
|
||||
bool m_detached; // TODO: this should be atomic
|
||||
|
||||
// Header identification: X-Forwarded-For, X-User
|
||||
std::pair<std::string, std::string> m_identity;
|
||||
};
|
||||
|
||||
// connection related types that it and its policy classes need.
|
||||
|
||||
@@ -913,6 +913,9 @@ void server<endpoint>::connection<connection_type>::handle_write_response(
|
||||
|
||||
m_connection.m_state = session::state::OPEN;
|
||||
|
||||
m_connection.set_identity (get_request_header ("X-Forwarded-For"),
|
||||
get_request_header ("X-User"));
|
||||
|
||||
m_connection.get_handler()->on_open(m_connection.shared_from_this());
|
||||
|
||||
get_io_service().post(
|
||||
|
||||
Reference in New Issue
Block a user