add UserInfo and limit publish/subscribe to authorized messages only
This commit is contained in:
@@ -50,10 +50,11 @@ const struct argp_child* datahandler_getargs() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool datahandler_register(BusHandler* busHandler, MessageMap* messages, list<DataHandler*>& handlers) {
|
||||
bool datahandler_register(UserInfo* userInfo, BusHandler* busHandler, MessageMap* messages,
|
||||
list<DataHandler*>& handlers) {
|
||||
bool success = true;
|
||||
#ifdef HAVE_MQTT
|
||||
DataHandler* handler = mqtthandler_register(busHandler, messages);
|
||||
DataHandler* handler = mqtthandler_register(userInfo, busHandler, messages);
|
||||
if (handler) {
|
||||
handlers.push_back(handler);
|
||||
} else {
|
||||
@@ -64,7 +65,9 @@ bool datahandler_register(BusHandler* busHandler, MessageMap* messages, list<Dat
|
||||
}
|
||||
|
||||
void DataSink::notifyUpdate(Message* message) {
|
||||
m_updatedMessages[message]++;
|
||||
if (message && message->hasLevel(m_levels)) {
|
||||
m_updatedMessages[message]++;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+47
-2
@@ -22,6 +22,7 @@
|
||||
#include <argp.h>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include "ebusd/bushandler.h"
|
||||
#include "lib/ebus/message.h"
|
||||
|
||||
@@ -36,6 +37,7 @@ namespace ebusd {
|
||||
using std::list;
|
||||
using std::map;
|
||||
|
||||
class UserInfo;
|
||||
class DataHandler;
|
||||
|
||||
/**
|
||||
@@ -46,12 +48,48 @@ const struct argp_child* datahandler_getargs();
|
||||
|
||||
/**
|
||||
* Registration function that is called once during initialization.
|
||||
* @param userInfo the @a UserInfo instance.
|
||||
* @param busHandler the @a BusHandler instance.
|
||||
* @param messages the @a MessageMap instance.
|
||||
* @param handlers the @a list to which new @a DataHandler instances shall be added.
|
||||
* @return true if registration was successful.
|
||||
*/
|
||||
bool datahandler_register(BusHandler* busHandler, MessageMap* messages, list<DataHandler*>& handlers);
|
||||
bool datahandler_register(UserInfo* userInfo, BusHandler* busHandler, MessageMap* messages,
|
||||
list<DataHandler*>& handlers);
|
||||
|
||||
|
||||
/**
|
||||
* Helper interface for user authentication.
|
||||
*/
|
||||
class UserInfo {
|
||||
public:
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~UserInfo() {}
|
||||
|
||||
/**
|
||||
* Check whether the specified user exists.
|
||||
* @param user the user name.
|
||||
* @return whether the user exists.
|
||||
*/
|
||||
virtual bool hasUser(const string user) = 0; // abstract
|
||||
|
||||
/**
|
||||
* Check whether the secret string matches the one of the specified user.
|
||||
* @param user the user name.
|
||||
* @param secret the secret to check.
|
||||
* @return whether the secret string is valid.
|
||||
*/
|
||||
virtual bool checkSecret(const string user, const string secret) = 0; // abstract
|
||||
|
||||
/**
|
||||
* Get the access levels associated with the specified user.
|
||||
* @param user the user name, or empty for default levels.
|
||||
* @return the access levels separated by semicolon.
|
||||
*/
|
||||
virtual string getLevels(const string user) = 0; // abstract
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@@ -95,8 +133,12 @@ class DataSink : virtual public DataHandler {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param userInfo the @a UserInfo instance.
|
||||
* @param user the user name for determining the allowed access levels (fall back to default levels).
|
||||
*/
|
||||
DataSink() {}
|
||||
DataSink(UserInfo* userInfo, string user) {
|
||||
m_levels = userInfo->getLevels(userInfo->hasUser(user) ? user : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
@@ -114,6 +156,9 @@ class DataSink : virtual public DataHandler {
|
||||
|
||||
|
||||
protected:
|
||||
/** the allowed access levels. */
|
||||
string m_levels;
|
||||
|
||||
/** a map of updated @p Message instances. */
|
||||
map<Message*, int> m_updatedMessages;
|
||||
};
|
||||
|
||||
+16
-19
@@ -165,8 +165,8 @@ const struct argp_child* mqtthandler_getargs() {
|
||||
return &g_mqtt_argp_child;
|
||||
}
|
||||
|
||||
DataHandler* mqtthandler_register(BusHandler* busHandler, MessageMap* messages) {
|
||||
return new MqttHandler(busHandler, messages);
|
||||
DataHandler* mqtthandler_register(UserInfo* userInfo, BusHandler* busHandler, MessageMap* messages) {
|
||||
return new MqttHandler(userInfo, busHandler, messages);
|
||||
}
|
||||
|
||||
/** the known topic column names. */
|
||||
@@ -177,7 +177,7 @@ static const char* columnNames[] = {
|
||||
};
|
||||
|
||||
/** the known topic column IDs. */
|
||||
static const size_t columnIds[] = {
|
||||
static const column_t columnIds[] = {
|
||||
COLUMN_CIRCUIT,
|
||||
COLUMN_NAME,
|
||||
COLUMN_FIELDS,
|
||||
@@ -194,24 +194,25 @@ static const size_t columnCount = sizeof(columnNames) / sizeof(char*);
|
||||
* @param cols the @a vector to which the column parts shall be added.
|
||||
* @return true on success, false on malformed topic template.
|
||||
*/
|
||||
bool parseTopic(const string topic, vector<string> &strs, vector<size_t> &cols) {
|
||||
bool parseTopic(const string topic, vector<string> &strs, vector<column_t> &cols) {
|
||||
size_t lastpos = 0;
|
||||
size_t end = topic.length();
|
||||
vector<string> columns;
|
||||
for (size_t pos=topic.find('%', lastpos); pos != string::npos; ) {
|
||||
size_t col = columnCount;
|
||||
size_t idx = columnCount;
|
||||
size_t len = 0;
|
||||
for (size_t i = 0; i < columnCount; i++) {
|
||||
len = strlen(columnNames[i]);
|
||||
if (topic.substr(pos+1, len) == columnNames[i]) {
|
||||
col = columnIds[i];
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (col == columnCount) {
|
||||
if (idx== columnCount) {
|
||||
return false;
|
||||
}
|
||||
for (vector<size_t>::iterator it=cols.begin(); it != cols.end(); it++) {
|
||||
column_t col = columnIds[idx];
|
||||
for (vector<column_t>::iterator it=cols.begin(); it != cols.end(); it++) {
|
||||
if (*it == col) {
|
||||
return false; // duplicate column
|
||||
}
|
||||
@@ -259,8 +260,8 @@ void on_connect(
|
||||
}
|
||||
|
||||
|
||||
MqttHandler::MqttHandler(BusHandler* busHandler, MessageMap* messages)
|
||||
: DataSink(), DataSource(busHandler), Thread(), m_messages(messages), m_connected(false) {
|
||||
MqttHandler::MqttHandler(UserInfo* userInfo, BusHandler* busHandler, MessageMap* messages)
|
||||
: DataSink(userInfo, "mqtt"), DataSource(busHandler), Thread(), m_messages(messages), m_connected(false) {
|
||||
bool enabled = g_port != 0;
|
||||
m_publishByField = false;
|
||||
m_mosquitto = NULL;
|
||||
@@ -396,17 +397,16 @@ void MqttHandler::notifyTopic(string topic, string data) {
|
||||
if (pos == string::npos) {
|
||||
return;
|
||||
}
|
||||
string suffix = topic.substr(pos+1);
|
||||
string direction = topic.substr(pos+1);
|
||||
bool isWrite = false;
|
||||
if (suffix.empty()) {
|
||||
if (direction.empty()) {
|
||||
return;
|
||||
}
|
||||
string direction = suffix.substr(0, 3);
|
||||
isWrite = direction == "set";
|
||||
if (!isWrite && direction != "get") {
|
||||
return;
|
||||
}
|
||||
suffix = suffix.substr(3); // security level
|
||||
|
||||
logOtherDebug("mqtt", "received topic %s", topic.c_str(), data.c_str());
|
||||
string remain = topic.substr(0, pos);
|
||||
size_t last = 0;
|
||||
@@ -457,12 +457,9 @@ void MqttHandler::notifyTopic(string topic, string data) {
|
||||
return;
|
||||
}
|
||||
logOtherInfo("mqtt", "received topic for %s %s", circuit.c_str(), name.c_str());
|
||||
if (suffix.length() > 0) {
|
||||
circuit += "#"+suffix;
|
||||
}
|
||||
Message* message = m_messages->find(circuit, name, isWrite);
|
||||
Message* message = m_messages->find(circuit, name, m_levels, isWrite);
|
||||
if (message == NULL) {
|
||||
message = m_messages->find(circuit, name, isWrite, true);
|
||||
message = m_messages->find(circuit, name, m_levels, isWrite, true);
|
||||
}
|
||||
if (message == NULL) {
|
||||
logOtherError("mqtt", "%s message %s %s not found", isWrite?"write":"read", circuit.c_str(), name.c_str());
|
||||
|
||||
@@ -45,11 +45,12 @@ const struct argp_child* mqtthandler_getargs();
|
||||
|
||||
/**
|
||||
* Registration function that is called once during initialization.
|
||||
* @param userInfo the @a UserInfo instance.
|
||||
* @param busHandler the @a BusHandler instance.
|
||||
* @param messages the @a MessageMap instance.
|
||||
* @return the create @a DataHandler, or NULL on error.
|
||||
*/
|
||||
DataHandler* mqtthandler_register(BusHandler* busHandler, MessageMap* messages);
|
||||
DataHandler* mqtthandler_register(UserInfo* userInfo, BusHandler* busHandler, MessageMap* messages);
|
||||
|
||||
/**
|
||||
* The main class supporting MQTT data handling.
|
||||
@@ -58,10 +59,11 @@ class MqttHandler : public DataSink, public DataSource, public Thread {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param userInfo the @a UserInfo instance.
|
||||
* @param busHandler the @a BusHandler instance.
|
||||
* @param messages the @a MessageMap instance.
|
||||
*/
|
||||
MqttHandler(BusHandler* busHandler, MessageMap* messages);
|
||||
MqttHandler(UserInfo* userInfo, BusHandler* busHandler, MessageMap* messages);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
@@ -120,7 +122,7 @@ class MqttHandler : public DataSink, public DataSource, public Thread {
|
||||
vector<string> m_topicStrs;
|
||||
|
||||
/** the MQTT topic column parts. */
|
||||
vector<size_t> m_topicCols;
|
||||
vector<column_t> m_topicCols;
|
||||
|
||||
/** the global topic prefix. */
|
||||
string m_globalTopic;
|
||||
|
||||
Reference in New Issue
Block a user