| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815 | 
							- // Copyright Wouter van Kleunen 2021
 
- //
 
- // Distributed under the Boost Software License, Version 1.0.
 
- // (See accompanying file LICENSE_1_0.txt or copy at
 
- // http://www.boost.org/LICENSE_1_0.txt)
 
- #if !defined(MQTT_BROKER_SECURITY_HPP)
 
- #define MQTT_BROKER_SECURITY_HPP
 
- #include <string>
 
- #include <mqtt/broker/broker_namespace.hpp>
 
- #include <mqtt/broker/subscription_map.hpp>
 
- #include <mqtt/optional.hpp>
 
- #include <mqtt/log.hpp>
 
- #include <map>
 
- #include <set>
 
- #include <boost/property_tree/ptree.hpp>
 
- #include <boost/property_tree/json_parser.hpp>
 
- #include <boost/iterator/function_output_iterator.hpp>
 
- #include <boost/algorithm/hex.hpp>
 
- #include <boost/algorithm/string.hpp>
 
- #if MQTT_USE_TLS
 
- #include <openssl/evp.h>
 
- #endif
 
- MQTT_BROKER_NS_BEGIN
 
- /** Remove comments from a JSON file (comments start with # and are not inside ' ' or " ") */
 
- inline std::string json_remove_comments(std::istream& input) {
 
-     bool inside_comment = false;
 
-     bool inside_single_quote = false;
 
-     bool inside_double_quote = false;
 
-     std::ostringstream result;
 
-     while (true) {
 
-         char c;
 
-         if (input.get(c).eof()) break;
 
-         if (!inside_double_quote && !inside_single_quote && c == '#') inside_comment = true;
 
-         if (!inside_double_quote && c == '\'') inside_single_quote = !inside_single_quote;
 
-         if (!inside_single_quote && c == '"') inside_double_quote = !inside_double_quote;
 
-         if (!inside_double_quote && c == '\n') inside_comment = false;
 
-         if (!inside_comment) result << c;
 
-     }
 
-     return result.str();
 
- }
 
- struct security {
 
-     static constexpr char const* any_group_name = "@any";
 
-     struct authentication {
 
-         enum class method {
 
-             sha256,
 
-             plain_password,
 
-             client_cert,
 
-             anonymous,
 
-             unauthenticated
 
-         };
 
-         authentication(
 
-             method auth_method = method::sha256,
 
-             optional<std::string> const& digest = nullopt,
 
-             std::string const& salt = std::string()
 
-         )
 
-             : auth_method(auth_method),
 
-               digest(digest),
 
-               salt(salt)
 
-         {
 
-         }
 
-         method auth_method;
 
-         optional<std::string> digest;
 
-         std::string salt;
 
-         std::vector<std::string> groups;
 
-     };
 
-     struct authorization {
 
-         enum class type {
 
-             deny, allow, none
 
-         };
 
-         authorization(string_view topic, std::size_t rule_nr)
 
-             : topic(topic),
 
-               rule_nr(rule_nr),
 
-               sub_type(type::none),
 
-               pub_type(type::none)
 
-         {
 
-         }
 
-         std::vector<std::string> topic_tokens;
 
-         std::string topic;
 
-         std::size_t rule_nr;
 
-         type sub_type;
 
-         std::set<std::string> sub;
 
-         type pub_type;
 
-         std::set<std::string> pub;
 
-     };
 
-     struct group {
 
-         std::string name;
 
-         std::vector<std::string> members;
 
-     };
 
-     /** Return username of anonymous user */
 
-     optional<std::string> const& login_anonymous() const {
 
-         return anonymous;
 
-     }
 
-     /** Return username of unauthorized user */
 
-     optional<std::string> const& login_unauthenticated() const {
 
-         return unauthenticated;
 
-     }
 
-     template<typename T>
 
-     static std::string to_hex(T start, T end) {
 
-         std::string result;
 
-         boost::algorithm::hex(start, end, std::back_inserter(result));
 
-         return result;
 
-     }
 
- #if defined(MQTT_USE_TLS)
 
-     static std::string sha256hash(string_view message) {
 
-         std::shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
 
-         EVP_DigestInit_ex(mdctx.get(), EVP_sha256(), NULL);
 
-         EVP_DigestUpdate(mdctx.get(), message.data(), message.size());
 
-         std::vector<unsigned char> digest(static_cast<std::size_t>(EVP_MD_size(EVP_sha256())));
 
-         unsigned int digest_size = static_cast<unsigned int>(digest.size());
 
-         EVP_DigestFinal_ex(mdctx.get(), digest.data(), &digest_size);
 
-         return to_hex(digest.data(), digest.data() + digest_size);
 
-     }
 
- #else
 
-     static std::string sha256hash(string_view message) {
 
-         return std::string(message);
 
-     }
 
- #endif
 
-     bool login_cert(string_view username) const {
 
-         auto i = authentication_.find(std::string(username));
 
-         return
 
-             i != authentication_.end() &&
 
-             i->second.auth_method == security::authentication::method::client_cert;
 
-     }
 
-     optional<std::string> login(string_view username, string_view password) const {
 
-         auto i = authentication_.find(std::string(username));
 
-         if (i != authentication_.end() &&
 
-             i->second.auth_method == security::authentication::method::sha256) {
 
-             return [&] () -> optional<std::string> {
 
-                 if (boost::iequals(
 
-                         i->second.digest.value(),
 
-                         sha256hash(i->second.salt + std::string(password))
 
-                     )
 
-                 ) {
 
-                     return std::string(username);
 
-                 }
 
-                 else {
 
-                     return nullopt;
 
-                 }
 
-             } ();
 
-         }
 
-         else if (
 
-             i != authentication_.end() &&
 
-             i->second.auth_method == security::authentication::method::plain_password) {
 
-             return [&] () -> optional<std::string> {
 
-                 if (i->second.digest.value() == password) {
 
-                     return std::string(username);
 
-                 }
 
-                 else {
 
-                     return nullopt;
 
-                 }
 
-             } ();
 
-         }
 
-         return nullopt;
 
-     }
 
-     static authorization::type get_auth_type(string_view type) {
 
-         if (type == "allow") return authorization::type::allow;
 
-         if (type == "deny") return authorization::type::deny;
 
-         throw std::runtime_error(
 
-             "An invalid authorization type was specified: " +
 
-             std::string(type)
 
-         );
 
-     }
 
-     static bool is_valid_group_name(string_view name) {
 
-         return !name.empty() && name[0] == '@'; // TODO: validate utf-8
 
-     }
 
-     static bool is_valid_user_name(string_view name) {
 
-         return !name.empty() && name[0] != '@'; // TODO: validate utf-8
 
-     }
 
-     std::size_t get_next_rule_nr() const {
 
-         std::size_t rule_nr = 0;
 
-         for (auto const& i: authorization_) {
 
-             rule_nr = std::max(rule_nr, i.rule_nr);
 
-         }
 
-         return rule_nr + 1;
 
-     }
 
-     void default_config() {
 
-         char const* username = "anonymous";
 
-         authentication login(authentication::method::anonymous);
 
-         authentication_.insert({ username, login});
 
-         anonymous = username;
 
-         char const* topic = "#";
 
-         authorization auth(topic, get_next_rule_nr());
 
-         auth.topic_tokens = get_topic_filter_tokens("#");
 
-         auth.sub_type = authorization::type::allow;
 
-         auth.sub.insert(username);
 
-         auth.pub_type = authorization::type::allow;
 
-         auth.pub.insert(username);
 
-         authorization_.push_back(auth);
 
-         groups_.insert({ std::string(any_group_name), group() });
 
-         validate();
 
-     }
 
-     std::size_t add_auth(
 
-         std::string const& topic_filter,
 
-         std::set<std::string> const& pub,
 
-         authorization::type auth_pub_type,
 
-         std::set<std::string> const& sub,
 
-         authorization::type auth_sub_type
 
-     ) {
 
-         for(auto const& j : pub) {
 
-             if (!is_valid_user_name(j) && !is_valid_group_name(j)) {
 
-                 throw std::runtime_error(
 
-                     "An invalid username or groupname was specified for the authorization: " + j
 
-                 );
 
-             }
 
-             validate_entry("topic " + topic_filter, j);
 
-         }
 
-         for(auto const& j : sub) {
 
-             if (!is_valid_user_name(j) && !is_valid_group_name(j)) {
 
-                 throw std::runtime_error(
 
-                     "An invalid username or groupname was specified for the authorization: " + j
 
-                 );
 
-             }
 
-             validate_entry("topic " + topic_filter, j);
 
-         }
 
-         std::size_t rule_nr = get_next_rule_nr();
 
-         authorization auth(topic_filter, rule_nr);
 
-         auth.topic_tokens = get_topic_filter_tokens(topic_filter);
 
-         auth.pub = pub;
 
-         auth.pub_type = auth_pub_type;
 
-         auth.sub = sub;
 
-         auth.sub_type = auth_sub_type;
 
-         for (auto const& j: sub) {
 
-             auth_sub_map.insert_or_assign(
 
-                 topic_filter,
 
-                 j,
 
-                 std::make_pair(auth_sub_type, rule_nr)
 
-             );
 
-         }
 
-         for (auto const& j: pub) {
 
-             auth_pub_map.insert_or_assign(
 
-                 topic_filter,
 
-                 j,
 
-                 std::make_pair(auth_pub_type, rule_nr)
 
-             );
 
-         }
 
-         authorization_.push_back(auth);
 
-         return rule_nr;
 
-     }
 
-     void remove_auth(std::size_t rule_nr)
 
-     {
 
-         for (auto i = authorization_.begin(); i != authorization_.end(); ++i) {
 
-             if (i->rule_nr == rule_nr) {
 
-                 for (auto const& j: i->sub) {
 
-                     auth_sub_map.erase(i->topic, j);
 
-                 }
 
-                 for (auto const& j: i->pub) {
 
-                     auth_pub_map.erase(i->topic, j);
 
-                 }
 
-                 authorization_.erase(i);
 
-                 return;
 
-             }
 
-         }
 
-     }
 
-     void load_json(std::istream& input) {
 
-         // Create a root
 
-         boost::property_tree::ptree root;
 
-         std::istringstream input_without_comments(json_remove_comments(input));
 
-         boost::property_tree::read_json(input_without_comments, root);
 
-         groups_.insert({ std::string(any_group_name), group() });
 
-         for (auto const& i: root.get_child("authentication")) {
 
-             std::string name = i.second.get<std::string>("name");
 
-             if (!is_valid_user_name(name)) {
 
-                 throw std::runtime_error("An invalid username was specified: " + name);
 
-             }
 
-             std::string method = i.second.get<std::string>("method");
 
-             if (method == "sha256") {
 
-                 std::string digest = i.second.get<std::string>("digest");
 
-                 std::string salt = i.second.get<std::string>("salt", "");
 
-                 authentication auth(authentication::method::sha256, digest, salt);
 
-                 authentication_.insert( { name, auth });
 
-             }
 
-             else if (method == "plain_password") {
 
-                 std::string digest = i.second.get<std::string>("password");
 
-                 authentication auth(authentication::method::plain_password, digest);
 
-                 authentication_.insert( { name, auth });
 
-             }
 
-             else if (method == "client_cert") {
 
-                 authentication auth(authentication::method::client_cert);
 
-                 authentication_.insert({ name, auth });
 
-             }
 
-             else if (method == "anonymous") {
 
-                 if(anonymous) {
 
-                     throw std::runtime_error(
 
-                         "Only a single anonymous user can be configured, anonymous user: " +
 
-                         *anonymous
 
-                     );
 
-                 }
 
-                 anonymous = name;
 
-                 authentication auth(authentication::method::anonymous);
 
-                 authentication_.insert( { name, auth });
 
-             }
 
-             else if (method == "unauthenticated") {
 
-                 if (unauthenticated) {
 
-                     throw std::runtime_error(
 
-                         "Only a single unauthenticated user can be configured, unauthenticated user: " +
 
-                         *unauthenticated
 
-                     );
 
-                 }
 
-                 unauthenticated = name;
 
-                 authentication auth(authentication::method::unauthenticated);
 
-                 authentication_.insert( { name, auth });
 
-             }
 
-             else {
 
-                 throw std::runtime_error("An invalid method was specified: " + method);
 
-             }
 
-         }
 
-         if (root.get_child_optional("groups")) {
 
-             for (auto const& i: root.get_child("groups")) {
 
-                 std::string name = i.second.get<std::string>("name");
 
-                 if (!is_valid_group_name(name)) {
 
-                     throw std::runtime_error("An invalid group name was specified: " + name);
 
-                 }
 
-                 group group;
 
-                 if (i.second.get_child_optional("members")) {
 
-                     for (auto const& j: i.second.get_child("members")) {
 
-                         auto username = j.second.get_value<std::string>();
 
-                         if (!is_valid_user_name(username)) {
 
-                             throw std::runtime_error("An invalid user name was specified: " + username);
 
-                         }
 
-                         group.members.push_back(username);
 
-                     }
 
-                 }
 
-                 groups_.insert({ name, group });
 
-             }
 
-         }
 
-         for (auto const& i: root.get_child("authorization")) {
 
-             std::string name = i.second.get<std::string>("topic");
 
-             if (!validate_topic_filter(name)) {
 
-                 throw std::runtime_error("An invalid topic filter was specified: " + name);
 
-             }
 
-             authorization auth(name, get_next_rule_nr());
 
-             auth.topic_tokens = get_topic_filter_tokens(name);
 
-             if (i.second.get_child_optional("allow")) {
 
-                 auto &allow = i.second.get_child("allow");
 
-                 if (allow.get_child_optional("sub")) {
 
-                     for (auto const& j: allow.get_child("sub")) {
 
-                         auth.sub.insert(j.second.get_value<std::string>());
 
-                     }
 
-                     auth.sub_type = authorization::type::allow;
 
-                 }
 
-                 if (allow.get_child_optional("pub")) {
 
-                     for (auto const& j: allow.get_child("pub")) {
 
-                         auth.pub.insert(j.second.get_value<std::string>());
 
-                     }
 
-                     auth.pub_type = authorization::type::allow;
 
-                 }
 
-             }
 
-             if (i.second.get_child_optional("deny")) {
 
-                 auto &deny = i.second.get_child("deny");
 
-                 if (deny.get_child_optional("sub")) {
 
-                     for (auto const& j: deny.get_child("sub")) {
 
-                         auth.sub.insert(j.second.get_value<std::string>());
 
-                     }
 
-                     auth.sub_type = authorization::type::deny;
 
-                 }
 
-                 if (deny.get_child_optional("pub")) {
 
-                     for (auto const& j: deny.get_child("pub")) {
 
-                         auth.pub.insert(j.second.get_value<std::string>());
 
-                     }
 
-                     auth.pub_type = authorization::type::deny;
 
-                 }
 
-             }
 
-             authorization_.push_back(auth);
 
-         }
 
-         validate();
 
-     }
 
-     template<typename T>
 
-     void get_auth_sub_by_user(string_view username, T&& callback) const {
 
-         std::set<std::string> username_and_groups;
 
-         username_and_groups.insert(std::string(username));
 
-         for (auto const& i: groups_) {
 
-             if (i.first == any_group_name ||
 
-                 std::find(
 
-                     i.second.members.begin(),
 
-                     i.second.members.end(),
 
-                     username
 
-                 ) != i.second.members.end()
 
-             ) {
 
-                 username_and_groups.insert(i.first);
 
-             }
 
-         }
 
-         for (auto const& i: authorization_) {
 
-             if (i.sub_type != authorization::type::none) {
 
-                 bool sets_intersect = false;
 
-                 auto store_intersect =
 
-                     [&sets_intersect]
 
-                     (std::string const &) mutable {
 
-                         sets_intersect = true;
 
-                     };
 
-                 std::set_intersection(
 
-                     i.sub.begin(),
 
-                     i.sub.end(),
 
-                     username_and_groups.begin(),
 
-                     username_and_groups.end(),
 
-                     boost::make_function_output_iterator(std::ref(store_intersect))
 
-                 );
 
-                 if (sets_intersect) {
 
-                     std::forward<T>(callback)(i);
 
-                 }
 
-             }
 
-         }
 
-     }
 
-     authorization::type auth_pub(string_view topic, string_view username) const {
 
-         authorization::type result_type = authorization::type::deny;
 
-         std::set<std::string> username_and_groups;
 
-         username_and_groups.insert(std::string(username));
 
-         for (auto const& i : groups_) {
 
-             if (i.first == any_group_name ||
 
-                 std::find(
 
-                     i.second.members.begin(),
 
-                     i.second.members.end(),
 
-                     username
 
-                 ) != i.second.members.end()
 
-             ) {
 
-                 username_and_groups.insert(i.first);
 
-             }
 
-         }
 
-         std::size_t priority = 0;
 
-         auth_pub_map.find(
 
-             topic,
 
-             [&](
 
-                 std::string const& allowed_username,
 
-                 std::pair<authorization::type, std::size_t> entry
 
-             ) {
 
-                 if (username_and_groups.find(allowed_username) != username_and_groups.end()) {
 
-                     if (entry.second >= priority) {
 
-                         result_type = entry.first;
 
-                         priority = entry.second;
 
-                     }
 
-                 }
 
-             }
 
-         );
 
-         return result_type;
 
-     }
 
-     std::map<std::string, authorization::type> auth_sub(string_view topic) const {
 
-         std::map<std::string, authorization::type> result;
 
-         std::size_t priority = 0;
 
-         auth_sub_map.find(
 
-             topic,
 
-             [&](
 
-                 std::string const &allowed_username,
 
-                 std::pair<authorization::type, unsigned int> entry
 
-             ) {
 
-                 if (entry.second >= priority) {
 
-                     result[allowed_username] = entry.first;
 
-                     priority = entry.second;
 
-                 }
 
-             }
 
-         );
 
-         return result;
 
-     }
 
-     authorization::type auth_sub_user(
 
-         std::map<std::string, authorization::type> const& result,
 
-         std::string const& username) const {
 
-         auto i = result.find(username);
 
-         if (i != result.end()) return i->second;
 
-         for (auto const& i: groups_) {
 
-             if (i.first == any_group_name ||
 
-                 std::find(
 
-                     i.second.members.begin(),
 
-                     i.second.members.end(),
 
-                     username
 
-                 ) != i.second.members.end()
 
-             ) {
 
-                 auto j = result.find(i.first);
 
-                 if (j != result.end()) return j->second;
 
-             }
 
-         }
 
-         return authorization::type::deny;
 
-     }
 
-     static bool is_hash(string_view level) { return level == "#"; }
 
-     static bool is_plus(string_view level) { return level == "+"; }
 
-     static bool is_literal(string_view level) { return !is_hash(level) && !is_plus(level); }
 
-     static optional<std::string>
 
-     is_subscribe_allowed(
 
-         std::vector<std::string> const& authorized_filter,
 
-         string_view subscription_filter
 
-     ) {
 
-         optional<std::string> result;
 
-         auto append_result =
 
-             [&result](string_view token) {
 
-                 if (result) {
 
-                     result.value() += topic_filter_separator;
 
-                     result.value().append(token.data(), token.size());
 
-                 }
 
-                 else {
 
-                     result = std::string(token);
 
-                 }
 
-             };
 
-         auto filter_begin = authorized_filter.begin();
 
-         auto subscription_begin = subscription_filter.begin();
 
-         auto subscription_next = topic_filter_tokenizer_next(subscription_begin, subscription_filter.end());
 
-         while (true) {
 
-             if (filter_begin == authorized_filter.end()) {
 
-                 return nullopt;
 
-             }
 
-             auto auth = *filter_begin;
 
-             ++filter_begin;
 
-             if (is_hash(auth)) {
 
-                 append_result(
 
-                     make_string_view(
 
-                         subscription_begin,
 
-                         subscription_filter.end()
 
-                     )
 
-                 );
 
-                 return result;
 
-             }
 
-             auto sub = make_string_view(
 
-                 subscription_begin,
 
-                 subscription_next
 
-             );
 
-             if (is_hash(sub)) {
 
-                 append_result(auth);
 
-                 while (filter_begin < authorized_filter.end()) {
 
-                     append_result(*filter_begin);
 
-                     ++filter_begin;
 
-                 }
 
-                 return result;
 
-             }
 
-             if (is_plus(auth)) {
 
-                 append_result(sub);
 
-             }
 
-             else if (is_plus(sub)) {
 
-                 append_result(auth);
 
-             }
 
-             else {
 
-                 if (auth != sub)  {
 
-                     return nullopt;
 
-                 }
 
-                 append_result(auth);
 
-             }
 
-             if (subscription_next == subscription_filter.end()) break;
 
-             subscription_begin = std::next(subscription_next);
 
-             subscription_next =
 
-                 topic_filter_tokenizer_next(
 
-                     subscription_begin,
 
-                     subscription_filter.end()
 
-                 );
 
-         }
 
-         if (filter_begin < authorized_filter.end()) {
 
-             return nullopt;
 
-         }
 
-         return result;
 
-     }
 
-     static bool is_subscribe_denied(
 
-         std::vector<std::string> const& deny_filter,
 
-         string_view subscription_filter
 
-     ) {
 
-         bool result = true;
 
-         auto filter_begin = deny_filter.begin();
 
-         auto tokens_count =
 
-             topic_filter_tokenizer(
 
-                 subscription_filter,
 
-                 [&](auto sub) {
 
-                     if (filter_begin == deny_filter.end()) {
 
-                         result = false;
 
-                         return false;
 
-                     };
 
-                     std::string deny = *filter_begin;
 
-                     ++filter_begin;
 
-                     if (deny != sub) {
 
-                         if (is_hash(deny)) {
 
-                             result = true;
 
-                             return false;
 
-                         }
 
-                         if (is_hash(sub)) {
 
-                             result = false;
 
-                             return false;
 
-                         }
 
-                         if (is_plus(deny)) {
 
-                             result = true;
 
-                             return true;
 
-                         }
 
-                         result = false;
 
-                         return false;
 
-                     }
 
-                     return true;
 
-                 }
 
-             );
 
-         return result && (tokens_count == deny_filter.size());
 
-     }
 
-     std::vector<std::string>
 
-     get_auth_sub_topics(string_view username, string_view topic_filter) const {
 
-         std::vector<std::string> auth_topics;
 
-         get_auth_sub_by_user(
 
-             username,
 
-             [&](authorization const& i) {
 
-                 if (i.sub_type == authorization::type::allow) {
 
-                     auto entry = is_subscribe_allowed(i.topic_tokens, topic_filter);
 
-                     if (entry) {
 
-                         auth_topics.push_back(entry.value());
 
-                     }
 
-                 }
 
-                 else {
 
-                     for (auto j = auth_topics.begin(); j != auth_topics.end();) {
 
-                         if (is_subscribe_denied(i.topic_tokens, topic_filter)) {
 
-                             j = auth_topics.erase(j);
 
-                         }
 
-                         else {
 
-                             ++j;
 
-                         }
 
-                     }
 
-                 }
 
-             }
 
-         );
 
-         return auth_topics;
 
-     }
 
-     /**
 
-      * @brief Determine if user is allowed to subscribe to the specified topic filter
 
-      * @param username The username to check
 
-      * @param topic_filter Topic filter the user would like to subscribe to
 
-      * @return true if the user is authorized
 
-      */
 
-     bool is_subscribe_authorized(string_view username, string_view topic_filter) const {
 
-         return !get_auth_sub_topics(username, topic_filter).empty();
 
-     }
 
-     // Get the individual path elements of the topic filter
 
-     static std::vector<std::string> get_topic_filter_tokens(string_view topic_filter) {
 
-         std::vector<std::string> result;
 
-         topic_filter_tokenizer(
 
-             topic_filter,
 
-             [&result](auto str) {
 
-                 result.push_back(std::string(str));
 
-                 return true;
 
-             }
 
-         );
 
-         return result;
 
-     }
 
-     std::map<std::string, authentication> authentication_;
 
-     std::map<std::string, group> groups_;
 
-     std::vector<authorization> authorization_;
 
-     optional<std::string> anonymous;
 
-     optional<std::string> unauthenticated;
 
-     using auth_map_type = multiple_subscription_map<std::string, std::pair<authorization::type, unsigned int>>;
 
-     auth_map_type auth_pub_map;
 
-     auth_map_type auth_sub_map;
 
- private:
 
-     void validate_entry(std::string const& context, std::string const& name) const {
 
-         if (is_valid_group_name(name) && groups_.find(name) == groups_.end()) {
 
-             throw std::runtime_error("An invalid group name was specified for " + context + ": " + name);
 
-         }
 
-         if (is_valid_user_name(name) && authentication_.find(name) == authentication_.end()) {
 
-             throw std::runtime_error("An invalid username name was specified for " + context + ": " + name);
 
-         }
 
-     }
 
-     void validate() {
 
-         for (auto const& i : groups_) {
 
-             for (auto const& j : i.second.members) {
 
-                 auto iter = authentication_.find(j);
 
-                 if(is_valid_user_name(j) && iter == authentication_.end())
 
-                     throw std::runtime_error("An invalid username name was specified for group " + i.first + ": " + j);
 
-             }
 
-         }
 
-         std::string unsalted;
 
-         for (auto const& i : authentication_) {
 
-             if (i.second.auth_method == authentication::method::sha256 && i.second.salt.empty()) {
 
-                 if (!unsalted.empty()) unsalted += ", ";
 
-                 unsalted += i.first;
 
-             }
 
-         }
 
-         if (!unsalted.empty()) {
 
-             MQTT_LOG("mqtt_broker", warning)
 
-                 << "The following users have no salt specified: "
 
-                 << unsalted;
 
-         }
 
-         for (auto const& i : authorization_) {
 
-             for (auto const& j: i.sub) {
 
-                 validate_entry("topic " + i.topic, j);
 
-                 if (is_valid_user_name(j) || is_valid_group_name(j)) {
 
-                     auth_sub_map.insert_or_assign(i.topic, j, std::make_pair(i.sub_type, i.rule_nr));
 
-                 }
 
-             }
 
-             for (auto const& j: i.pub) {
 
-                 validate_entry("topic " + i.topic, j);
 
-                 if(is_valid_user_name(j) || is_valid_group_name(j)) {
 
-                     auth_pub_map.insert_or_assign(i.topic, j, std::make_pair(i.pub_type, i.rule_nr));
 
-                 }
 
-             }
 
-         }
 
-     }
 
- };
 
- MQTT_BROKER_NS_END
 
- #endif // MQTT_BROKER_SECURITY_HPP
 
 
  |