-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Implementation of basic API authentication #1228
Changes from 2 commits
93276c0
6326ed6
6201335
934c406
ea9d0f4
3461cae
758e29a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,8 +2,9 @@ | |
|
||
#include <ethminer-buildinfo.h> | ||
|
||
ApiServer::ApiServer(boost::asio::io_service& io_service, int portnum, bool readonly, Farm& f) : | ||
ApiServer::ApiServer(boost::asio::io_service& io_service, int portnum, bool readonly, string password, Farm& f) : | ||
m_readonly(readonly), | ||
m_password(std::move(password)), | ||
m_portnumber(portnum), | ||
m_acceptor(io_service), | ||
m_io_strand(io_service), | ||
|
@@ -17,7 +18,7 @@ void ApiServer::start() | |
if (m_portnumber == 0) return; | ||
|
||
m_running.store(true, std::memory_order_relaxed); | ||
|
||
tcp::endpoint endpoint(tcp::v4(), m_portnumber); | ||
|
||
// Try to bind to port number | ||
|
@@ -36,7 +37,7 @@ void ApiServer::start() | |
return; | ||
} | ||
|
||
cnote << "Api server listening for connections on port " + to_string(m_acceptor.local_endpoint().port()); | ||
cnote << "Api server listening for connections on port " + to_string(m_acceptor.local_endpoint().port()) << (m_password.empty() ? "" : " Authentication needed"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's just a sophistication ... that line will be quickly scrolled outside the boundaries of the screen. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can remove "for connections" part of the message. |
||
m_workThread = std::thread{ boost::bind(&ApiServer::begin_accept, this) }; | ||
|
||
} | ||
|
@@ -60,7 +61,7 @@ void ApiServer::begin_accept() | |
if (!isRunning()) return; | ||
|
||
dev::setThreadName("Api"); | ||
std::shared_ptr<ApiConnection> session = std::make_shared<ApiConnection>(m_acceptor.get_io_service(), ++lastSessionId, m_readonly, m_farm); | ||
std::shared_ptr<ApiConnection> session = std::make_shared<ApiConnection>(m_acceptor.get_io_service(), ++lastSessionId, m_readonly, m_password, m_farm); | ||
m_acceptor.async_accept(session->socket(), m_io_strand.wrap(boost::bind(&ApiServer::handle_accept, this, session, boost::asio::placeholders::error))); | ||
} | ||
|
||
|
@@ -142,63 +143,101 @@ void ApiConnection::processRequest(Json::Value& requestObject) | |
std::string _method = requestObject.get("method", "").asString(); | ||
jRes["id"] = requestObject.get("id", 0).asInt(); | ||
|
||
// Check authentication | ||
if (!m_is_authenticated) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you tried integrate clang-format to your editor. It would make it easier to have consistent coding style. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok for formatting. |
||
if (_method == "api_authorize") { | ||
|
||
if (_method == "miner_getstat1") | ||
{ | ||
jRes["result"] = getMinerStat1(); | ||
} | ||
else if (_method == "miner_getstathr") | ||
{ | ||
jRes["result"] = getMinerStatHR(); | ||
} | ||
else if (_method == "miner_shuffle") | ||
{ | ||
|
||
// Gives nonce scrambler a new range | ||
cnote << "Miner Shuffle requested"; | ||
jRes["result"] = true; | ||
m_farm.shuffle(); | ||
if (!requestObject.isMember("params") || requestObject["params"].empty() || !requestObject["params"].isObject()) { | ||
jRes["error"]["code"] = -32602; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be |
||
jRes["error"]["message"] = "Missing params object member"; | ||
} | ||
else { | ||
Json::Value jPrm = requestObject["params"]; | ||
if (!jPrm.isMember("password") || jPrm["password"].empty() || !jPrm["password"].isString()) { | ||
jRes["error"]["code"] = -32602; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The error code There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And in fact here we have invalid params (the "password" member is missing). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, you are right. Sorry. |
||
jRes["error"]["message"] = "Missing password"; | ||
} | ||
else | ||
{ | ||
if (jPrm.get("password", "").asString() == m_password) { | ||
m_is_authenticated = true; | ||
} | ||
else { | ||
jRes["error"]["code"] = -32602; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrong error code. |
||
jRes["error"]["message"] = "Invalid password"; | ||
} | ||
} | ||
} | ||
|
||
} | ||
else if (_method == "miner_ping") | ||
{ | ||
} | ||
else { | ||
|
||
// Replies back to (check for liveness) | ||
jRes["result"] = "pong"; | ||
jRes["error"]["code"] = -32601; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrong error code. |
||
jRes["error"]["message"] = "Authorization needed"; | ||
|
||
} | ||
} | ||
else if (_method == "miner_restart") | ||
|
||
if (m_is_authenticated) | ||
{ | ||
// Send response to client of success | ||
// and invoke an async restart | ||
// to prevent locking | ||
if (m_readonly) | ||
if (_method == "miner_getstat1") | ||
{ | ||
jRes["error"]["code"] = -32601; | ||
jRes["error"]["message"] = "Method not available"; | ||
jRes["result"] = getMinerStat1(); | ||
} | ||
else | ||
else if (_method == "miner_getstathr") | ||
{ | ||
cnote << "Miner Restart requested"; | ||
jRes["result"] = getMinerStatHR(); | ||
} | ||
else if (_method == "miner_shuffle") | ||
{ | ||
|
||
// Gives nonce scrambler a new range | ||
cnote << "Miner Shuffle requested"; | ||
jRes["result"] = true; | ||
m_farm.restart_async(); | ||
m_farm.shuffle(); | ||
|
||
} | ||
else if (_method == "miner_ping") | ||
{ | ||
|
||
} | ||
else if (_method == "miner_reboot") | ||
{ | ||
// Replies back to (check for liveness) | ||
jRes["result"] = "pong"; | ||
|
||
// Not implemented yet | ||
jRes["error"]["code"] = -32601; | ||
jRes["error"]["message"] = "Method not implemented"; | ||
} | ||
else if (_method == "miner_restart") | ||
{ | ||
// Send response to client of success | ||
// and invoke an async restart | ||
// to prevent locking | ||
if (m_readonly) | ||
{ | ||
jRes["error"]["code"] = -32601; | ||
jRes["error"]["message"] = "Method not available"; | ||
} | ||
else | ||
{ | ||
cnote << "Miner Restart requested"; | ||
jRes["result"] = true; | ||
m_farm.restart_async(); | ||
} | ||
|
||
} | ||
else | ||
{ | ||
} | ||
else if (_method == "miner_reboot") | ||
{ | ||
|
||
// Not implemented yet | ||
jRes["error"]["code"] = -32601; | ||
jRes["error"]["message"] = "Method not implemented"; | ||
|
||
} | ||
else | ||
{ | ||
|
||
// Any other method not found | ||
jRes["error"]["code"] = -32601; | ||
jRes["error"]["message"] = "Method not found"; | ||
} | ||
|
||
// Any other method not found | ||
jRes["error"]["code"] = -32601; | ||
jRes["error"]["message"] = "Method not found"; | ||
} | ||
|
||
// Send response | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,12 +17,18 @@ class ApiConnection | |
{ | ||
public: | ||
|
||
ApiConnection(boost::asio::io_service& io_service, int id, bool readonly, Farm& f) : | ||
ApiConnection(boost::asio::io_service& io_service, int id, bool readonly, string password, Farm& f) : | ||
m_sessionId(id), | ||
m_socket(io_service), | ||
m_io_strand(io_service), | ||
m_readonly(readonly), | ||
m_farm(f) {} | ||
m_password(std::move(password)), | ||
m_farm(f) | ||
{ | ||
|
||
if (!m_password.empty()) m_is_authenticated = false; | ||
|
||
} | ||
|
||
~ApiConnection() {} | ||
|
||
|
@@ -58,16 +64,19 @@ class ApiConnection | |
Json::FastWriter m_jWriter; | ||
|
||
bool m_readonly = false ; | ||
std::string m_password = ""; | ||
Farm& m_farm; | ||
|
||
bool m_is_authenticated = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we always init that to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And in fact it is like that. It's m_is_authenticated, not m_requires_auth |
||
|
||
}; | ||
|
||
|
||
class ApiServer | ||
{ | ||
public: | ||
|
||
ApiServer(boost::asio::io_service& io_service, int portnum, bool readonly, Farm& f); | ||
ApiServer(boost::asio::io_service& io_service, int portnum, bool readonly, string password, Farm& f); | ||
bool isRunning() { return m_running.load(std::memory_order_relaxed); }; | ||
void start(); | ||
void stop(); | ||
|
@@ -81,6 +90,7 @@ class ApiServer | |
|
||
std::thread m_workThread; | ||
std::atomic<bool> m_readonly = { false }; | ||
std::string m_password = ""; | ||
std::atomic<bool> m_running = { false }; | ||
int m_portnumber; | ||
tcp::acceptor m_acceptor; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
= ""
is not needed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok