flashmq_plugin.h
9.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
/*
* This file is part of FlashMQ (https://www.flashmq.org). It defines the
* authentication plugin interface.
*
* This interface definition is public domain and you are encouraged
* to copy it to your authentication plugin project, for portability. Including
* this file in your project does not make it a 'derived work', does not require
* your code to have a compatibile license nor requires you to open source it.
*
* Compile like: gcc -fPIC -shared authplugin.cpp -o authplugin.so
*/
#ifndef FLASHMQ_PLUGIN_H
#define FLASHMQ_PLUGIN_H
#include <string>
#include <vector>
#include <unordered_map>
#include <memory>
#define FLASHMQ_PLUGIN_VERSION 1
// Compatible with Mosquitto, for auth plugin compatability.
#define LOG_NONE 0x00
#define LOG_INFO 0x01
#define LOG_NOTICE 0x02
#define LOG_WARNING 0x04
#define LOG_ERR 0x08
#define LOG_DEBUG 0x10
#define LOG_SUBSCRIBE 0x20
#define LOG_UNSUBSCRIBE 0x40
extern "C"
{
/**
* @brief The AclAccess enum's numbers are compatible with Mosquitto's 'int access'.
*
* read = reading a publish published by someone else.
* write = doing a publish.
* subscribe = subscribing.
*/
enum class AclAccess
{
none = 0,
read = 1,
write = 2,
subscribe = 4
};
/**
* @brief The AuthResult enum's numbers are compatible with Mosquitto's auth result.
*/
enum class AuthResult
{
success = 0,
auth_method_not_supported = 10,
acl_denied = 12,
login_denied = 11,
error = 13,
auth_continue = -4
};
/**
* @brief The FlashMQMessage struct contains the meta data of a publish.
*
* The subtopics is the topic split, so you don't have to do that anymore.
*
* As for 'retain', keep in mind that for existing subscribers, this will always be false [MQTT-3.3.1-9]. Only publishes or
* retain messages as a result of a subscribe can have that set to true.
*
* For subscribtions, 'retain' is always false.
*/
struct FlashMQMessage
{
const std::string &topic;
const std::vector<std::string> &subtopics;
const std::vector<std::pair<std::string, std::string>> *userProperties;
const char qos;
const bool retain;
FlashMQMessage(const std::string &topic, const std::vector<std::string> &subtopics, const char qos, const bool retain,
const std::vector<std::pair<std::string, std::string>> *userProperties);
};
enum class ExtendedAuthStage
{
None = 0,
Auth = 10,
Reauth = 20,
Continue = 30
};
/**
* @brief flashmq_logf calls the internal logger of FlashMQ. The logger mutexes all access, so is thread-safe, and writes to disk
* asynchronously, so it won't hold you up.
* @param level is any of the levels defined above, starting with LOG_.
* @param str
*
* FlashMQ makes no distinction between INFO and NOTICE.
*/
void flashmq_logf(int level, const char *str, ...);
/**
* @brief flashmq_plugin_version must return FLASHMQ_PLUGIN_VERSION.
* @return FLASHMQ_PLUGIN_VERSION.
*/
int flashmq_auth_plugin_version();
/**
* @brief flashmq_auth_plugin_allocate_thread_memory is called once by each thread. Never again.
* @param thread_data. Create a memory structure and assign it to *thread_data.
* @param auth_opts. Map of flashmq_auth_opt_* from the config file.
*
* Only allocate the plugin's memory here. Don't open connections, etc. That's because the reload mechanism doesn't call this function.
*
* Because of the multi-core design of FlashMQ, you should treat each thread as its own domain with its own data. You can use static
* variables for global scope if you must, or even create threads, but do provide proper locking where necessary.
*
* You can throw exceptions on errors.
*/
void flashmq_auth_plugin_allocate_thread_memory(void **thread_data, std::unordered_map<std::string, std::string> &auth_opts);
/**
* @brief flashmq_auth_plugin_deallocate_thread_memory is called once by each thread. Never again.
* @param thread_data. Delete this memory.
* @param auth_opts. Map of flashmq_auth_opt_* from the config file.
*
* You can throw exceptions on errors.
*/
void flashmq_auth_plugin_deallocate_thread_memory(void *thread_data, std::unordered_map<std::string, std::string> &auth_opts);
/**
* @brief flashmq_auth_plugin_init is called on thread start and config reload. It is the main place to initialize the plugin.
* @param thread_data is memory allocated in flashmq_auth_plugin_allocate_thread_memory().
* @param auth_opts. Map of flashmq_auth_opt_* from the config file.
* @param reloading.
*
* The best approach to state keeping is doing everything per thread. You can initialize connections to database servers, load encryption keys,
* create maps, etc.
*
* Keep in mind that libraries you use may not be thread safe (by default). Sometimes they use global scope in treacherous ways. As a random
* example: Qt's QSqlDatabase needs a unique name for each connection, otherwise it is not thread safe and will crash.
*
* There is the option to set 'auth_plugin_serialize_init true' in the config file, which allows some mitigation in
* case you run into problems.
*
* You can throw exceptions on errors.
*/
void flashmq_auth_plugin_init(void *thread_data, std::unordered_map<std::string, std::string> &auth_opts, bool reloading);
/**
* @brief flashmq_auth_plugin_deinit is called on thread stop and config reload. It is the precursor to initializing.
* @param thread_data is memory allocated in flashmq_auth_plugin_allocate_thread_memory().
* @param auth_opts. Map of flashmq_auth_opt_* from the config file.
* @param reloading
*
* You can throw exceptions on errors.
*/
void flashmq_auth_plugin_deinit(void *thread_data, std::unordered_map<std::string, std::string> &auth_opts, bool reloading);
/**
* @brief flashmq_auth_plugin_periodic is called every x seconds as defined in the config file.
* @param thread_data is memory allocated in flashmq_auth_plugin_allocate_thread_memory().
*
* You may need to periodically refresh data from a database, post stats, etc. You can do that from here. It's queued
* in each thread at the same time, so you can perform somewhat synchronized events in all threads.
*
* Note that it's executed in the event loop, so it blocks the thread if you block here. If you need asynchronous operation,
* you can make threads yourself. Be sure to synchronize data access properly in that case.
*
* The setting auth_plugin_timer_period sets this interval in seconds.
*
* Implementing this is optional.
*
* You can throw exceptions on errors.
*/
void flashmq_auth_plugin_periodic_event(void *thread_data);
/**
* @brief flashmq_auth_plugin_login_check is called on login of a client.
* @param thread_data is memory allocated in flashmq_auth_plugin_allocate_thread_memory().
* @param username
* @param password
* @return
*
* You could throw exceptions here, but that will be slow and pointless. It will just get converted into AuthResult::error,
* because there's nothing else to do: the state of FlashMQ won't change.
*
* Note that there is a setting 'auth_plugin_serialize_auth_checks'. Use only as a last resort if your plugin is not
* thread-safe. It will negate much of FlashMQ's multi-core model.
*/
AuthResult flashmq_auth_plugin_login_check(void *thread_data, const std::string &username, const std::string &password,
const std::vector<std::pair<std::string, std::string>> *userProperties);
/**
* @brief flashmq_auth_plugin_acl_check is called on publish, deliver and subscribe.
* @param thread_data is memory allocated in flashmq_auth_plugin_allocate_thread_memory().
* @param access
* @param clientid
* @param username
* @param msg. See FlashMQMessage.
* @return
*
* You could throw exceptions here, but that will be slow and pointless. It will just get converted into AuthResult::error,
* because there's nothing else to do: the state of FlashMQ won't change.
*
* Controlling subscribe access can have several benefits. For instance, you may want to avoid subscriptions that cause
* a lot of server load. If clients pester you with many subscriptions like '+/+/+/+/+/+/+/+/+/', that causes a lot
* of tree walking. Similarly, if all clients subscribe to '#' because it's easy, every single message passing through
* the server will have to be ACL checked for every subscriber.
*
* Note that only MQTT 3.1.1 or higher has a 'failed' return code for subscribing, so older clients will see a normal
* ack and won't know it failed.
*
* Note that there is a setting 'auth_plugin_serialize_auth_checks'. Use only as a last resort if your plugin is not
* thread-safe. It will negate much of FlashMQ's multi-core model.
*/
AuthResult flashmq_auth_plugin_acl_check(void *thread_data, AclAccess access, const std::string &clientid, const std::string &username, const FlashMQMessage &msg);
/**
* @brief flashmq_extended_auth can be used to implement MQTT 5 extended auth. This is optional.
* @param thread_data is the memory you allocated in flashmq_auth_plugin_allocate_thread_memory.
* @param clientid
* @param stage
* @param authMethod
* @param authData
* @param userProperties are optional (and are nullptr in that case)
* @param returnData is a non-const string, that you can set to include data back to the client in an AUTH packet.
* @param username is a non-const string. You can set it, which will then apply to ACL checking and show in the logs.
* @return an AuthResult enum class value
*/
AuthResult flashmq_extended_auth(void *thread_data, const std::string &clientid, ExtendedAuthStage stage, const std::string &authMethod,
const std::string &authData, const std::vector<std::pair<std::string, std::string>> *userProperties, std::string &returnData,
std::string &username);
}
#endif // FLASHMQ_PLUGIN_H