diff --git a/examples/keys.yaml b/examples/keys.yaml new file mode 100644 index 0000000..0120f98 --- /dev/null +++ b/examples/keys.yaml @@ -0,0 +1 @@ +default: /home/jimmy/Develop/mosquitto-plugin/examples/key.pem \ No newline at end of file diff --git a/examples/mosquitto.conf b/examples/mosquitto.conf index d2e8205..c471e7b 100644 --- a/examples/mosquitto.conf +++ b/examples/mosquitto.conf @@ -5,15 +5,13 @@ listener 8082 protocol websockets allow_anonymous false auth_plugin /home/jimmy/Develop/mosquitto-plugin/build/libj7s-plugin.so -auth_opt_issuer https://auth.jpace121.net/realms/jpace121-main -auth_opt_public_key /home/jimmy/Develop/mosquitto-plugin/examples/key.pem +auth_opt_key_file /home/jimmy/Develop/mosquitto-plugin/examples/keys.yaml auth_opt_acl_file /home/jimmy/Develop/mosquitto-plugin/examples/acl.yaml listener 8081 protocol mqtt allow_anonymous false auth_plugin /home/jimmy/Develop/mosquitto-plugin/build/libj7s-plugin.so -auth_opt_issuer https://auth.jpace121.net/realms/jpace121-main -auth_opt_public_key /home/jimmy/Develop/mosquitto-plugin/examples/key.pem +auth_opt_key_file /home/jimmy/Develop/mosquitto-plugin/examples/keys.yaml auth_opt_acl_file /home/jimmy/Develop/mosquitto-plugin/examples/acl.yaml diff --git a/include/j7s-plugin/Authorizer.hpp b/include/j7s-plugin/Authorizer.hpp index 64275cc..8c62512 100644 --- a/include/j7s-plugin/Authorizer.hpp +++ b/include/j7s-plugin/Authorizer.hpp @@ -1,4 +1,4 @@ -// Copyright 2021 James Pace +// Copyright 2021-2022 James Pace // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ class Authorizer { public: - Authorizer(const std::string& pub_key, const std::string& issuer, const std::string& aclFilePath); + Authorizer(const std::string& keyFilePath, const std::string& aclFilePath); static std::optional read_key(const std::string& key_file); void add_unknown(const std::string& username); bool is_unknown(const std::string& username); @@ -38,6 +38,7 @@ private: AuthList _unknownList; YAML::Node _aclFile; + YAML::Node _keyFile; std::string _pub_key; std::string _issuer; diff --git a/include/j7s-plugin/utils.h b/include/j7s-plugin/utils.h index cdfb1d5..dfa2231 100644 --- a/include/j7s-plugin/utils.h +++ b/include/j7s-plugin/utils.h @@ -22,7 +22,6 @@ std::optional read_key(const std::string & key_file); std::tuple> validate( const std::string & token, const std::string & username, - const std::string & issuer, const std::string & pub_key); std::string gen_token( diff --git a/src/Authorizer.cpp b/src/Authorizer.cpp index e765072..20998fe 100644 --- a/src/Authorizer.cpp +++ b/src/Authorizer.cpp @@ -1,4 +1,4 @@ -// Copyright 2021 James Pace +// Copyright 2021-2022 James Pace // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,30 +18,31 @@ #include #include +#include // Util. -std::tuple checkACL(const YAML::Node& user); +std::tuple checkACL(const std::string& user, const YAML::Node& aclFile); +std::optional getKey(const std::string& user, const YAML::Node& keyFile); // Class implementation. Authorizer::Authorizer( - const std::string & pub_key, const std::string & issuer, const std::string & aclFilePath) : - _pub_key{pub_key}, _issuer{issuer}, _aclFile{aclFilePath} + const std::string & keyFilePath, const std::string & aclFilePath) : + _keyFile{keyFilePath}, _aclFile{aclFilePath} { } -void Authorizer::add_unknown(const std::string & username) -{ - _unknownList.add(username, time_T::max()); -} - -bool Authorizer::is_unknown(const std::string & username) -{ - return (username.empty() or _unknownList.confirm(username)); -} bool Authorizer::add(const std::string & token, const std::string & username) { - const auto [validated, expr_time] = validate(token, username, _issuer, _pub_key); + const auto key = getKey(username, _keyFile); + + if(not key) + { + std::cerr << "Could not read key for user." << std::endl; + return false; + } + + const auto [validated, expr_time] = validate(token, username, key.value()); if (not validated) { std::cerr << "Not validated." << std::endl; @@ -49,13 +50,7 @@ bool Authorizer::add(const std::string & token, const std::string & username) } // Check the ACL file. - // TODO: Make sure default is in ACL file. - if (not _aclFile[username]) - { - const auto checkACL(_aclFile["default"]); - return true; - } - const auto [can_read, can_write] = checkACL(_aclFile[username]); + const auto [can_read, can_write] = checkACL(username, _aclFile); if (can_read) { @@ -87,19 +82,57 @@ void Authorizer::logout(const std::string & username) _unknownList.remove(username); } -// Util. -std::tuple checkACL(const YAML::Node& user) +void Authorizer::add_unknown(const std::string & username) { + _unknownList.add(username, time_T::max()); +} + +bool Authorizer::is_unknown(const std::string & username) +{ + return (username.empty() or _unknownList.confirm(username)); +} + + +// Util. +std::tuple checkACL(const std::string& user, const YAML::Node& aclFile) +{ + // TODO: Make sure default exists. + YAML::Node userDict; + if(aclFile[user]) + { + userDict = aclFile[user]; + } + else + { + userDict = aclFile["default"]; + } + bool can_read = false; bool can_write = false; - if(user["can_read"] and user["can_read"].as()) + if(userDict["can_read"] and userDict["can_read"].as()) { can_read = true; } - if(user["can_write"] and user["can_write"].as()) + if(userDict["can_write"] and userDict["can_write"].as()) { can_write = true; } return std::make_tuple(can_read, can_write); } + +std::optional getKey(const std::string& user, const YAML::Node& keyFile) +{ + // TODO: Make sure default exists. + std::filesystem::path pathToKey; + if(keyFile[user]) + { + pathToKey = std::filesystem::path(keyFile[user].as()); + } + else + { + pathToKey = std::filesystem::path(keyFile["default"].as()); + } + + return read_key(std::filesystem::absolute(pathToKey).string()); +} diff --git a/src/j7s-plugin.cpp b/src/j7s-plugin.cpp index 32cbd53..5acd108 100644 --- a/src/j7s-plugin.cpp +++ b/src/j7s-plugin.cpp @@ -45,36 +45,26 @@ int mosquitto_plugin_init( { plugin_id = identifier; - if (option_count < 3) + if (option_count < 2) { mosquitto_log_printf(MOSQ_LOG_ERR, "Missing an option. Found: %d", option_count); return MOSQ_ERR_INVAL; } - std::string public_key; - std::string issuer; + std::filesystem::path keyFilePath; std::filesystem::path aclFilePath; for (int index = 0; index < option_count; index++) { const auto key = std::string(options[index].key); - if (key == "public_key") + if (key == "key_file") { - const auto key = read_key(std::string(options[index].value)); - if (not key or key->empty()) + std::string key_file_string = std::string(options[index].value); + if (key_file_string.empty()) { - mosquitto_log_printf(MOSQ_LOG_ERR, "Could not read public key."); - return MOSQ_ERR_INVAL; - } - public_key = *key; - } - else if (key == "issuer") - { - issuer = std::string(options[index].value); - if (issuer.empty()) - { - mosquitto_log_printf(MOSQ_LOG_ERR, "issuer not set."); + mosquitto_log_printf(MOSQ_LOG_ERR, "key_file not set."); return MOSQ_ERR_INVAL; } + keyFilePath = std::filesystem::path(key_file_string); } else if (key == "acl_file") { @@ -89,7 +79,8 @@ int mosquitto_plugin_init( } authorizer = std::make_unique( - public_key, issuer, std::filesystem::absolute(aclFilePath).string()); + std::filesystem::absolute(keyFilePath).string(), + std::filesystem::absolute(aclFilePath).string()); // Register the callbacks. mosquitto_callback_register( @@ -187,6 +178,7 @@ int j7s_disconnect_callback(int event, void * event_data, void * userdata) { struct mosquitto_evt_disconnect * disconnect_data = static_cast(event_data); + const std::string username = std::string(mosquitto_client_username(disconnect_data->client)); authorizer->logout(username); diff --git a/src/utils.cpp b/src/utils.cpp index 7874c02..c63230c 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -35,14 +35,13 @@ std::optional read_key(const std::string & key_file) std::tuple> validate( const std::string & token, const std::string & username, - const std::string & issuer, const std::string & pub_key) { const auto decoded_token = jwt::decode(token); // Is the token valid? const auto verifier = - jwt::verify().with_issuer(issuer).allow_algorithm(jwt::algorithm::rs256(pub_key)); + jwt::verify().allow_algorithm(jwt::algorithm::rs256(pub_key)); try { verifier.verify(decoded_token);