security.hpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. // Copyright Wouter van Kleunen 2021
  2. //
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt or copy at
  5. // http://www.boost.org/LICENSE_1_0.txt)
  6. #if !defined(MQTT_BROKER_SECURITY_HPP)
  7. #define MQTT_BROKER_SECURITY_HPP
  8. #include <string>
  9. #include <mqtt/broker/broker_namespace.hpp>
  10. #include <mqtt/broker/subscription_map.hpp>
  11. #include <mqtt/optional.hpp>
  12. #include <mqtt/log.hpp>
  13. #include <map>
  14. #include <set>
  15. #include <boost/property_tree/ptree.hpp>
  16. #include <boost/property_tree/json_parser.hpp>
  17. #include <boost/iterator/function_output_iterator.hpp>
  18. #include <boost/algorithm/hex.hpp>
  19. #include <boost/algorithm/string.hpp>
  20. #if MQTT_USE_TLS
  21. #include <openssl/evp.h>
  22. #endif
  23. MQTT_BROKER_NS_BEGIN
  24. /** Remove comments from a JSON file (comments start with # and are not inside ' ' or " ") */
  25. inline std::string json_remove_comments(std::istream& input) {
  26. bool inside_comment = false;
  27. bool inside_single_quote = false;
  28. bool inside_double_quote = false;
  29. std::ostringstream result;
  30. while (true) {
  31. char c;
  32. if (input.get(c).eof()) break;
  33. if (!inside_double_quote && !inside_single_quote && c == '#') inside_comment = true;
  34. if (!inside_double_quote && c == '\'') inside_single_quote = !inside_single_quote;
  35. if (!inside_single_quote && c == '"') inside_double_quote = !inside_double_quote;
  36. if (!inside_double_quote && c == '\n') inside_comment = false;
  37. if (!inside_comment) result << c;
  38. }
  39. return result.str();
  40. }
  41. struct security {
  42. static constexpr char const* any_group_name = "@any";
  43. struct authentication {
  44. enum class method {
  45. sha256,
  46. plain_password,
  47. client_cert,
  48. anonymous,
  49. unauthenticated
  50. };
  51. authentication(
  52. method auth_method = method::sha256,
  53. optional<std::string> const& digest = nullopt,
  54. std::string const& salt = std::string()
  55. )
  56. : auth_method(auth_method),
  57. digest(digest),
  58. salt(salt)
  59. {
  60. }
  61. method auth_method;
  62. optional<std::string> digest;
  63. std::string salt;
  64. std::vector<std::string> groups;
  65. };
  66. struct authorization {
  67. enum class type {
  68. deny, allow, none
  69. };
  70. authorization(string_view topic, std::size_t rule_nr)
  71. : topic(topic),
  72. rule_nr(rule_nr),
  73. sub_type(type::none),
  74. pub_type(type::none)
  75. {
  76. }
  77. std::vector<std::string> topic_tokens;
  78. std::string topic;
  79. std::size_t rule_nr;
  80. type sub_type;
  81. std::set<std::string> sub;
  82. type pub_type;
  83. std::set<std::string> pub;
  84. };
  85. struct group {
  86. std::string name;
  87. std::vector<std::string> members;
  88. };
  89. /** Return username of anonymous user */
  90. optional<std::string> const& login_anonymous() const {
  91. return anonymous;
  92. }
  93. /** Return username of unauthorized user */
  94. optional<std::string> const& login_unauthenticated() const {
  95. return unauthenticated;
  96. }
  97. template<typename T>
  98. static std::string to_hex(T start, T end) {
  99. std::string result;
  100. boost::algorithm::hex(start, end, std::back_inserter(result));
  101. return result;
  102. }
  103. #if defined(MQTT_USE_TLS)
  104. static std::string sha256hash(string_view message) {
  105. std::shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
  106. EVP_DigestInit_ex(mdctx.get(), EVP_sha256(), NULL);
  107. EVP_DigestUpdate(mdctx.get(), message.data(), message.size());
  108. std::vector<unsigned char> digest(static_cast<std::size_t>(EVP_MD_size(EVP_sha256())));
  109. unsigned int digest_size = static_cast<unsigned int>(digest.size());
  110. EVP_DigestFinal_ex(mdctx.get(), digest.data(), &digest_size);
  111. return to_hex(digest.data(), digest.data() + digest_size);
  112. }
  113. #else
  114. static std::string sha256hash(string_view message) {
  115. return std::string(message);
  116. }
  117. #endif
  118. bool login_cert(string_view username) const {
  119. auto i = authentication_.find(std::string(username));
  120. return
  121. i != authentication_.end() &&
  122. i->second.auth_method == security::authentication::method::client_cert;
  123. }
  124. optional<std::string> login(string_view username, string_view password) const {
  125. auto i = authentication_.find(std::string(username));
  126. if (i != authentication_.end() &&
  127. i->second.auth_method == security::authentication::method::sha256) {
  128. return [&] () -> optional<std::string> {
  129. if (boost::iequals(
  130. i->second.digest.value(),
  131. sha256hash(i->second.salt + std::string(password))
  132. )
  133. ) {
  134. return std::string(username);
  135. }
  136. else {
  137. return nullopt;
  138. }
  139. } ();
  140. }
  141. else if (
  142. i != authentication_.end() &&
  143. i->second.auth_method == security::authentication::method::plain_password) {
  144. return [&] () -> optional<std::string> {
  145. if (i->second.digest.value() == password) {
  146. return std::string(username);
  147. }
  148. else {
  149. return nullopt;
  150. }
  151. } ();
  152. }
  153. return nullopt;
  154. }
  155. static authorization::type get_auth_type(string_view type) {
  156. if (type == "allow") return authorization::type::allow;
  157. if (type == "deny") return authorization::type::deny;
  158. throw std::runtime_error(
  159. "An invalid authorization type was specified: " +
  160. std::string(type)
  161. );
  162. }
  163. static bool is_valid_group_name(string_view name) {
  164. return !name.empty() && name[0] == '@'; // TODO: validate utf-8
  165. }
  166. static bool is_valid_user_name(string_view name) {
  167. return !name.empty() && name[0] != '@'; // TODO: validate utf-8
  168. }
  169. std::size_t get_next_rule_nr() const {
  170. std::size_t rule_nr = 0;
  171. for (auto const& i: authorization_) {
  172. rule_nr = std::max(rule_nr, i.rule_nr);
  173. }
  174. return rule_nr + 1;
  175. }
  176. void default_config() {
  177. char const* username = "anonymous";
  178. authentication login(authentication::method::anonymous);
  179. authentication_.insert({ username, login});
  180. anonymous = username;
  181. char const* topic = "#";
  182. authorization auth(topic, get_next_rule_nr());
  183. auth.topic_tokens = get_topic_filter_tokens("#");
  184. auth.sub_type = authorization::type::allow;
  185. auth.sub.insert(username);
  186. auth.pub_type = authorization::type::allow;
  187. auth.pub.insert(username);
  188. authorization_.push_back(auth);
  189. groups_.insert({ std::string(any_group_name), group() });
  190. validate();
  191. }
  192. std::size_t add_auth(
  193. std::string const& topic_filter,
  194. std::set<std::string> const& pub,
  195. authorization::type auth_pub_type,
  196. std::set<std::string> const& sub,
  197. authorization::type auth_sub_type
  198. ) {
  199. for(auto const& j : pub) {
  200. if (!is_valid_user_name(j) && !is_valid_group_name(j)) {
  201. throw std::runtime_error(
  202. "An invalid username or groupname was specified for the authorization: " + j
  203. );
  204. }
  205. validate_entry("topic " + topic_filter, j);
  206. }
  207. for(auto const& j : sub) {
  208. if (!is_valid_user_name(j) && !is_valid_group_name(j)) {
  209. throw std::runtime_error(
  210. "An invalid username or groupname was specified for the authorization: " + j
  211. );
  212. }
  213. validate_entry("topic " + topic_filter, j);
  214. }
  215. std::size_t rule_nr = get_next_rule_nr();
  216. authorization auth(topic_filter, rule_nr);
  217. auth.topic_tokens = get_topic_filter_tokens(topic_filter);
  218. auth.pub = pub;
  219. auth.pub_type = auth_pub_type;
  220. auth.sub = sub;
  221. auth.sub_type = auth_sub_type;
  222. for (auto const& j: sub) {
  223. auth_sub_map.insert_or_assign(
  224. topic_filter,
  225. j,
  226. std::make_pair(auth_sub_type, rule_nr)
  227. );
  228. }
  229. for (auto const& j: pub) {
  230. auth_pub_map.insert_or_assign(
  231. topic_filter,
  232. j,
  233. std::make_pair(auth_pub_type, rule_nr)
  234. );
  235. }
  236. authorization_.push_back(auth);
  237. return rule_nr;
  238. }
  239. void remove_auth(std::size_t rule_nr)
  240. {
  241. for (auto i = authorization_.begin(); i != authorization_.end(); ++i) {
  242. if (i->rule_nr == rule_nr) {
  243. for (auto const& j: i->sub) {
  244. auth_sub_map.erase(i->topic, j);
  245. }
  246. for (auto const& j: i->pub) {
  247. auth_pub_map.erase(i->topic, j);
  248. }
  249. authorization_.erase(i);
  250. return;
  251. }
  252. }
  253. }
  254. void load_json(std::istream& input) {
  255. // Create a root
  256. boost::property_tree::ptree root;
  257. std::istringstream input_without_comments(json_remove_comments(input));
  258. boost::property_tree::read_json(input_without_comments, root);
  259. groups_.insert({ std::string(any_group_name), group() });
  260. for (auto const& i: root.get_child("authentication")) {
  261. std::string name = i.second.get<std::string>("name");
  262. if (!is_valid_user_name(name)) {
  263. throw std::runtime_error("An invalid username was specified: " + name);
  264. }
  265. std::string method = i.second.get<std::string>("method");
  266. if (method == "sha256") {
  267. std::string digest = i.second.get<std::string>("digest");
  268. std::string salt = i.second.get<std::string>("salt", "");
  269. authentication auth(authentication::method::sha256, digest, salt);
  270. authentication_.insert( { name, auth });
  271. }
  272. else if (method == "plain_password") {
  273. std::string digest = i.second.get<std::string>("password");
  274. authentication auth(authentication::method::plain_password, digest);
  275. authentication_.insert( { name, auth });
  276. }
  277. else if (method == "client_cert") {
  278. authentication auth(authentication::method::client_cert);
  279. authentication_.insert({ name, auth });
  280. }
  281. else if (method == "anonymous") {
  282. if(anonymous) {
  283. throw std::runtime_error(
  284. "Only a single anonymous user can be configured, anonymous user: " +
  285. *anonymous
  286. );
  287. }
  288. anonymous = name;
  289. authentication auth(authentication::method::anonymous);
  290. authentication_.insert( { name, auth });
  291. }
  292. else if (method == "unauthenticated") {
  293. if (unauthenticated) {
  294. throw std::runtime_error(
  295. "Only a single unauthenticated user can be configured, unauthenticated user: " +
  296. *unauthenticated
  297. );
  298. }
  299. unauthenticated = name;
  300. authentication auth(authentication::method::unauthenticated);
  301. authentication_.insert( { name, auth });
  302. }
  303. else {
  304. throw std::runtime_error("An invalid method was specified: " + method);
  305. }
  306. }
  307. if (root.get_child_optional("groups")) {
  308. for (auto const& i: root.get_child("groups")) {
  309. std::string name = i.second.get<std::string>("name");
  310. if (!is_valid_group_name(name)) {
  311. throw std::runtime_error("An invalid group name was specified: " + name);
  312. }
  313. group group;
  314. if (i.second.get_child_optional("members")) {
  315. for (auto const& j: i.second.get_child("members")) {
  316. auto username = j.second.get_value<std::string>();
  317. if (!is_valid_user_name(username)) {
  318. throw std::runtime_error("An invalid user name was specified: " + username);
  319. }
  320. group.members.push_back(username);
  321. }
  322. }
  323. groups_.insert({ name, group });
  324. }
  325. }
  326. for (auto const& i: root.get_child("authorization")) {
  327. std::string name = i.second.get<std::string>("topic");
  328. if (!validate_topic_filter(name)) {
  329. throw std::runtime_error("An invalid topic filter was specified: " + name);
  330. }
  331. authorization auth(name, get_next_rule_nr());
  332. auth.topic_tokens = get_topic_filter_tokens(name);
  333. if (i.second.get_child_optional("allow")) {
  334. auto &allow = i.second.get_child("allow");
  335. if (allow.get_child_optional("sub")) {
  336. for (auto const& j: allow.get_child("sub")) {
  337. auth.sub.insert(j.second.get_value<std::string>());
  338. }
  339. auth.sub_type = authorization::type::allow;
  340. }
  341. if (allow.get_child_optional("pub")) {
  342. for (auto const& j: allow.get_child("pub")) {
  343. auth.pub.insert(j.second.get_value<std::string>());
  344. }
  345. auth.pub_type = authorization::type::allow;
  346. }
  347. }
  348. if (i.second.get_child_optional("deny")) {
  349. auto &deny = i.second.get_child("deny");
  350. if (deny.get_child_optional("sub")) {
  351. for (auto const& j: deny.get_child("sub")) {
  352. auth.sub.insert(j.second.get_value<std::string>());
  353. }
  354. auth.sub_type = authorization::type::deny;
  355. }
  356. if (deny.get_child_optional("pub")) {
  357. for (auto const& j: deny.get_child("pub")) {
  358. auth.pub.insert(j.second.get_value<std::string>());
  359. }
  360. auth.pub_type = authorization::type::deny;
  361. }
  362. }
  363. authorization_.push_back(auth);
  364. }
  365. validate();
  366. }
  367. template<typename T>
  368. void get_auth_sub_by_user(string_view username, T&& callback) const {
  369. std::set<std::string> username_and_groups;
  370. username_and_groups.insert(std::string(username));
  371. for (auto const& i: groups_) {
  372. if (i.first == any_group_name ||
  373. std::find(
  374. i.second.members.begin(),
  375. i.second.members.end(),
  376. username
  377. ) != i.second.members.end()
  378. ) {
  379. username_and_groups.insert(i.first);
  380. }
  381. }
  382. for (auto const& i: authorization_) {
  383. if (i.sub_type != authorization::type::none) {
  384. bool sets_intersect = false;
  385. auto store_intersect =
  386. [&sets_intersect]
  387. (std::string const &) mutable {
  388. sets_intersect = true;
  389. };
  390. std::set_intersection(
  391. i.sub.begin(),
  392. i.sub.end(),
  393. username_and_groups.begin(),
  394. username_and_groups.end(),
  395. boost::make_function_output_iterator(std::ref(store_intersect))
  396. );
  397. if (sets_intersect) {
  398. std::forward<T>(callback)(i);
  399. }
  400. }
  401. }
  402. }
  403. authorization::type auth_pub(string_view topic, string_view username) const {
  404. authorization::type result_type = authorization::type::deny;
  405. std::set<std::string> username_and_groups;
  406. username_and_groups.insert(std::string(username));
  407. for (auto const& i : groups_) {
  408. if (i.first == any_group_name ||
  409. std::find(
  410. i.second.members.begin(),
  411. i.second.members.end(),
  412. username
  413. ) != i.second.members.end()
  414. ) {
  415. username_and_groups.insert(i.first);
  416. }
  417. }
  418. std::size_t priority = 0;
  419. auth_pub_map.find(
  420. topic,
  421. [&](
  422. std::string const& allowed_username,
  423. std::pair<authorization::type, std::size_t> entry
  424. ) {
  425. if (username_and_groups.find(allowed_username) != username_and_groups.end()) {
  426. if (entry.second >= priority) {
  427. result_type = entry.first;
  428. priority = entry.second;
  429. }
  430. }
  431. }
  432. );
  433. return result_type;
  434. }
  435. std::map<std::string, authorization::type> auth_sub(string_view topic) const {
  436. std::map<std::string, authorization::type> result;
  437. std::size_t priority = 0;
  438. auth_sub_map.find(
  439. topic,
  440. [&](
  441. std::string const &allowed_username,
  442. std::pair<authorization::type, unsigned int> entry
  443. ) {
  444. if (entry.second >= priority) {
  445. result[allowed_username] = entry.first;
  446. priority = entry.second;
  447. }
  448. }
  449. );
  450. return result;
  451. }
  452. authorization::type auth_sub_user(
  453. std::map<std::string, authorization::type> const& result,
  454. std::string const& username) const {
  455. auto i = result.find(username);
  456. if (i != result.end()) return i->second;
  457. for (auto const& i: groups_) {
  458. if (i.first == any_group_name ||
  459. std::find(
  460. i.second.members.begin(),
  461. i.second.members.end(),
  462. username
  463. ) != i.second.members.end()
  464. ) {
  465. auto j = result.find(i.first);
  466. if (j != result.end()) return j->second;
  467. }
  468. }
  469. return authorization::type::deny;
  470. }
  471. static bool is_hash(string_view level) { return level == "#"; }
  472. static bool is_plus(string_view level) { return level == "+"; }
  473. static bool is_literal(string_view level) { return !is_hash(level) && !is_plus(level); }
  474. static optional<std::string>
  475. is_subscribe_allowed(
  476. std::vector<std::string> const& authorized_filter,
  477. string_view subscription_filter
  478. ) {
  479. optional<std::string> result;
  480. auto append_result =
  481. [&result](string_view token) {
  482. if (result) {
  483. result.value() += topic_filter_separator;
  484. result.value().append(token.data(), token.size());
  485. }
  486. else {
  487. result = std::string(token);
  488. }
  489. };
  490. auto filter_begin = authorized_filter.begin();
  491. auto subscription_begin = subscription_filter.begin();
  492. auto subscription_next = topic_filter_tokenizer_next(subscription_begin, subscription_filter.end());
  493. while (true) {
  494. if (filter_begin == authorized_filter.end()) {
  495. return nullopt;
  496. }
  497. auto auth = *filter_begin;
  498. ++filter_begin;
  499. if (is_hash(auth)) {
  500. append_result(
  501. make_string_view(
  502. subscription_begin,
  503. subscription_filter.end()
  504. )
  505. );
  506. return result;
  507. }
  508. auto sub = make_string_view(
  509. subscription_begin,
  510. subscription_next
  511. );
  512. if (is_hash(sub)) {
  513. append_result(auth);
  514. while (filter_begin < authorized_filter.end()) {
  515. append_result(*filter_begin);
  516. ++filter_begin;
  517. }
  518. return result;
  519. }
  520. if (is_plus(auth)) {
  521. append_result(sub);
  522. }
  523. else if (is_plus(sub)) {
  524. append_result(auth);
  525. }
  526. else {
  527. if (auth != sub) {
  528. return nullopt;
  529. }
  530. append_result(auth);
  531. }
  532. if (subscription_next == subscription_filter.end()) break;
  533. subscription_begin = std::next(subscription_next);
  534. subscription_next =
  535. topic_filter_tokenizer_next(
  536. subscription_begin,
  537. subscription_filter.end()
  538. );
  539. }
  540. if (filter_begin < authorized_filter.end()) {
  541. return nullopt;
  542. }
  543. return result;
  544. }
  545. static bool is_subscribe_denied(
  546. std::vector<std::string> const& deny_filter,
  547. string_view subscription_filter
  548. ) {
  549. bool result = true;
  550. auto filter_begin = deny_filter.begin();
  551. auto tokens_count =
  552. topic_filter_tokenizer(
  553. subscription_filter,
  554. [&](auto sub) {
  555. if (filter_begin == deny_filter.end()) {
  556. result = false;
  557. return false;
  558. };
  559. std::string deny = *filter_begin;
  560. ++filter_begin;
  561. if (deny != sub) {
  562. if (is_hash(deny)) {
  563. result = true;
  564. return false;
  565. }
  566. if (is_hash(sub)) {
  567. result = false;
  568. return false;
  569. }
  570. if (is_plus(deny)) {
  571. result = true;
  572. return true;
  573. }
  574. result = false;
  575. return false;
  576. }
  577. return true;
  578. }
  579. );
  580. return result && (tokens_count == deny_filter.size());
  581. }
  582. std::vector<std::string>
  583. get_auth_sub_topics(string_view username, string_view topic_filter) const {
  584. std::vector<std::string> auth_topics;
  585. get_auth_sub_by_user(
  586. username,
  587. [&](authorization const& i) {
  588. if (i.sub_type == authorization::type::allow) {
  589. auto entry = is_subscribe_allowed(i.topic_tokens, topic_filter);
  590. if (entry) {
  591. auth_topics.push_back(entry.value());
  592. }
  593. }
  594. else {
  595. for (auto j = auth_topics.begin(); j != auth_topics.end();) {
  596. if (is_subscribe_denied(i.topic_tokens, topic_filter)) {
  597. j = auth_topics.erase(j);
  598. }
  599. else {
  600. ++j;
  601. }
  602. }
  603. }
  604. }
  605. );
  606. return auth_topics;
  607. }
  608. /**
  609. * @brief Determine if user is allowed to subscribe to the specified topic filter
  610. * @param username The username to check
  611. * @param topic_filter Topic filter the user would like to subscribe to
  612. * @return true if the user is authorized
  613. */
  614. bool is_subscribe_authorized(string_view username, string_view topic_filter) const {
  615. return !get_auth_sub_topics(username, topic_filter).empty();
  616. }
  617. // Get the individual path elements of the topic filter
  618. static std::vector<std::string> get_topic_filter_tokens(string_view topic_filter) {
  619. std::vector<std::string> result;
  620. topic_filter_tokenizer(
  621. topic_filter,
  622. [&result](auto str) {
  623. result.push_back(std::string(str));
  624. return true;
  625. }
  626. );
  627. return result;
  628. }
  629. std::map<std::string, authentication> authentication_;
  630. std::map<std::string, group> groups_;
  631. std::vector<authorization> authorization_;
  632. optional<std::string> anonymous;
  633. optional<std::string> unauthenticated;
  634. using auth_map_type = multiple_subscription_map<std::string, std::pair<authorization::type, unsigned int>>;
  635. auth_map_type auth_pub_map;
  636. auth_map_type auth_sub_map;
  637. private:
  638. void validate_entry(std::string const& context, std::string const& name) const {
  639. if (is_valid_group_name(name) && groups_.find(name) == groups_.end()) {
  640. throw std::runtime_error("An invalid group name was specified for " + context + ": " + name);
  641. }
  642. if (is_valid_user_name(name) && authentication_.find(name) == authentication_.end()) {
  643. throw std::runtime_error("An invalid username name was specified for " + context + ": " + name);
  644. }
  645. }
  646. void validate() {
  647. for (auto const& i : groups_) {
  648. for (auto const& j : i.second.members) {
  649. auto iter = authentication_.find(j);
  650. if(is_valid_user_name(j) && iter == authentication_.end())
  651. throw std::runtime_error("An invalid username name was specified for group " + i.first + ": " + j);
  652. }
  653. }
  654. std::string unsalted;
  655. for (auto const& i : authentication_) {
  656. if (i.second.auth_method == authentication::method::sha256 && i.second.salt.empty()) {
  657. if (!unsalted.empty()) unsalted += ", ";
  658. unsalted += i.first;
  659. }
  660. }
  661. if (!unsalted.empty()) {
  662. MQTT_LOG("mqtt_broker", warning)
  663. << "The following users have no salt specified: "
  664. << unsalted;
  665. }
  666. for (auto const& i : authorization_) {
  667. for (auto const& j: i.sub) {
  668. validate_entry("topic " + i.topic, j);
  669. if (is_valid_user_name(j) || is_valid_group_name(j)) {
  670. auth_sub_map.insert_or_assign(i.topic, j, std::make_pair(i.sub_type, i.rule_nr));
  671. }
  672. }
  673. for (auto const& j: i.pub) {
  674. validate_entry("topic " + i.topic, j);
  675. if(is_valid_user_name(j) || is_valid_group_name(j)) {
  676. auth_pub_map.insert_or_assign(i.topic, j, std::make_pair(i.pub_type, i.rule_nr));
  677. }
  678. }
  679. }
  680. }
  681. };
  682. MQTT_BROKER_NS_END
  683. #endif // MQTT_BROKER_SECURITY_HPP