More refactoring. Implement an ACL file. Care about token timeouts.
This commit is contained in:
1 parent
deb1cef30f
commit
4f60efa87a
12 files changed
+236
-50
No files matched your search
+20
-18
@@ -14,36 +14,38 @@
|
||||
#include <algorithm>
|
||||
#include <j7s-plugin/AuthList.hpp>
|
||||
|
||||
AuthList::AuthList() : _allowedUsernames{} {}
|
||||
AuthList::AuthList() : _map{} {}
|
||||
|
||||
void AuthList::add(const std::string & username)
|
||||
void AuthList::add(const std::string & username, const time_T& expr_time)
|
||||
{
|
||||
// Is the username already in the list?
|
||||
// If not add it.
|
||||
if (not confirm(username))
|
||||
{
|
||||
_allowedUsernames.emplace_front(username);
|
||||
}
|
||||
// Add the user to the list or update it's expr time if
|
||||
// it's already there.
|
||||
_map[username] = expr_time;
|
||||
}
|
||||
|
||||
void AuthList::remove(const std::string & username)
|
||||
{
|
||||
// Is the user in the list?
|
||||
// Is so, remove it,
|
||||
if (confirm(username))
|
||||
{
|
||||
_allowedUsernames.remove(username);
|
||||
}
|
||||
// Remove the user
|
||||
_map.erase(username);
|
||||
}
|
||||
|
||||
bool AuthList::confirm(const std::string & username)
|
||||
{
|
||||
// Is the user in the list?
|
||||
const auto found =
|
||||
std::find(std::begin(_allowedUsernames), std::end(_allowedUsernames), username);
|
||||
if (found != std::end(_allowedUsernames))
|
||||
// Is the user in the map?
|
||||
const auto iter = _map.find(username);
|
||||
|
||||
if(iter == _map.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Has the token expired?
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const auto expr_time = std::get<1>(*iter);
|
||||
if(now < expr_time)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
+46
-7
@@ -17,14 +17,21 @@
|
||||
#include <j7s-plugin/AuthList.hpp>
|
||||
#include <j7s-plugin/Authorizer.hpp>
|
||||
|
||||
Authorizer::Authorizer(const std::string & pub_key, const std::string & issuer) :
|
||||
_pub_key{pub_key}, _issuer{issuer}
|
||||
#include <tuple>
|
||||
|
||||
// Util.
|
||||
std::tuple<bool, bool> checkACL(const YAML::Node& user);
|
||||
|
||||
// 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}
|
||||
{
|
||||
}
|
||||
|
||||
void Authorizer::add_unknown(const std::string & username)
|
||||
{
|
||||
_unknownList.add(username);
|
||||
_unknownList.add(username, time_T::max());
|
||||
}
|
||||
|
||||
bool Authorizer::is_unknown(const std::string & username)
|
||||
@@ -34,20 +41,35 @@ bool Authorizer::is_unknown(const std::string & username)
|
||||
|
||||
bool Authorizer::add(const std::string & token, const std::string & username)
|
||||
{
|
||||
const auto validated = validate(token, username, _issuer, _pub_key);
|
||||
const auto [validated, expr_time] = validate(token, username, _issuer, _pub_key);
|
||||
if (not validated)
|
||||
{
|
||||
std::cerr << "Not validated." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Check ACL file to see which one.
|
||||
_writeList.add(username);
|
||||
_readList.add(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]);
|
||||
|
||||
if (can_read)
|
||||
{
|
||||
_readList.add(username, expr_time);
|
||||
}
|
||||
if (can_write)
|
||||
{
|
||||
_writeList.add(username, expr_time);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Authorizer::can_read(const std::string & username)
|
||||
{
|
||||
return _readList.confirm(username);
|
||||
@@ -64,3 +86,20 @@ void Authorizer::logout(const std::string & username)
|
||||
_readList.remove(username);
|
||||
_unknownList.remove(username);
|
||||
}
|
||||
|
||||
// Util.
|
||||
std::tuple<bool, bool> checkACL(const YAML::Node& user)
|
||||
{
|
||||
bool can_read = false;
|
||||
bool can_write = false;
|
||||
if(user["can_read"] and user["can_read"].as<bool>())
|
||||
{
|
||||
can_read = true;
|
||||
}
|
||||
if(user["can_write"] and user["can_write"].as<bool>())
|
||||
{
|
||||
can_write = true;
|
||||
}
|
||||
|
||||
return std::make_tuple(can_read, can_write);
|
||||
}
|
||||
+19
-3
@@ -14,8 +14,12 @@
|
||||
#include <j7s-plugin/j7s-plugin.h>
|
||||
|
||||
#include <j7s-plugin/Authorizer.hpp>
|
||||
|
||||
#include <j7s-plugin/utils.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
// Mosquitto Globals
|
||||
static mosquitto_plugin_id_t * plugin_id = nullptr;
|
||||
@@ -41,7 +45,7 @@ int mosquitto_plugin_init(
|
||||
{
|
||||
plugin_id = identifier;
|
||||
|
||||
if (option_count != 2)
|
||||
if (option_count < 3)
|
||||
{
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Missing an option. Found: %d", option_count);
|
||||
return MOSQ_ERR_INVAL;
|
||||
@@ -49,12 +53,13 @@ int mosquitto_plugin_init(
|
||||
|
||||
std::string public_key;
|
||||
std::string issuer;
|
||||
std::filesystem::path aclFilePath;
|
||||
for (int index = 0; index < option_count; index++)
|
||||
{
|
||||
const auto key = std::string(options[index].key);
|
||||
if (key == "public_key")
|
||||
{
|
||||
const auto key = Authorizer::read_key(std::string(options[index].value));
|
||||
const auto key = read_key(std::string(options[index].value));
|
||||
if (not key or key->empty())
|
||||
{
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Could not read public key.");
|
||||
@@ -71,9 +76,20 @@ int mosquitto_plugin_init(
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
}
|
||||
else if (key == "acl_file")
|
||||
{
|
||||
std::string acl_file_string = std::string(options[index].value);
|
||||
if (acl_file_string.empty())
|
||||
{
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "acl_file not set.");
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
aclFilePath = std::filesystem::path(acl_file_string);
|
||||
}
|
||||
}
|
||||
|
||||
authorizer = std::make_unique<Authorizer>(public_key, issuer);
|
||||
authorizer = std::make_unique<Authorizer>(
|
||||
public_key, issuer, std::filesystem::absolute(aclFilePath).string());
|
||||
|
||||
// Register the callbacks.
|
||||
mosquitto_callback_register(
|
||||
|
||||
+18
-6
@@ -32,7 +32,7 @@ std::optional<std::string> read_key(const std::string & key_file)
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool validate(
|
||||
std::tuple<bool, std::chrono::time_point<std::chrono::system_clock>> validate(
|
||||
const std::string & token,
|
||||
const std::string & username,
|
||||
const std::string & issuer,
|
||||
@@ -50,7 +50,7 @@ bool validate(
|
||||
catch (jwt::error::token_verification_exception & exception)
|
||||
{
|
||||
std::cerr << exception.what() << std::endl;
|
||||
return false;
|
||||
return std::make_tuple(false, std::chrono::system_clock::now());
|
||||
}
|
||||
auto claims = decoded_token.get_payload_claims();
|
||||
|
||||
@@ -58,22 +58,34 @@ bool validate(
|
||||
if (not claims.contains("upn"))
|
||||
{
|
||||
std::cerr << "Missing upn." << std::endl;
|
||||
return false;
|
||||
return std::make_tuple(false, std::chrono::system_clock::now());
|
||||
}
|
||||
if (claims["upn"].as_string() != username)
|
||||
{
|
||||
std::cerr << "Wrong username." << std::endl;
|
||||
return false;
|
||||
return std::make_tuple(false, std::chrono::system_clock::now());
|
||||
}
|
||||
|
||||
// Check for mqtt-write claim value.
|
||||
if (not claims.contains("mqtt"))
|
||||
{
|
||||
std::cerr << "Missing mqtt claim." << std::endl;
|
||||
return false;
|
||||
return std::make_tuple(false, std::chrono::system_clock::now());
|
||||
}
|
||||
if(not claims["mqtt"].as_bool())
|
||||
{
|
||||
std::cerr << "Not claiming can do mqtt." << std::endl;
|
||||
return std::make_tuple(false, std::chrono::system_clock::now());
|
||||
}
|
||||
|
||||
return claims["mqtt"].as_bool();
|
||||
// Do we have an expiration time?
|
||||
if(not claims.contains("exp"))
|
||||
{
|
||||
std::cerr << "Missing expiration time claim." << std::endl;
|
||||
return std::make_tuple(false, std::chrono::system_clock::now());
|
||||
}
|
||||
|
||||
return std::make_tuple(true, claims["exp"].as_date());
|
||||
}
|
||||
|
||||
std::string gen_token(
|
||||
|
||||
Reference in new issue
Block a user