Add key_file.

This commit is contained in:
James Pace 2022-03-25 00:57:21 +00:00
parent 4f60efa87a
commit 5ef2e0b929
7 changed files with 75 additions and 52 deletions

1
examples/keys.yaml Normal file
View File

@ -0,0 +1 @@
default: /home/jimmy/Develop/mosquitto-plugin/examples/key.pem

View File

@ -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

View File

@ -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<std::string> 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;

View File

@ -22,7 +22,6 @@ std::optional<std::string> read_key(const std::string & key_file);
std::tuple<bool, std::chrono::time_point<std::chrono::system_clock>> validate(
const std::string & token,
const std::string & username,
const std::string & issuer,
const std::string & pub_key);
std::string gen_token(

View File

@ -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 <j7s-plugin/Authorizer.hpp>
#include <tuple>
#include <filesystem>
// Util.
std::tuple<bool, bool> checkACL(const YAML::Node& user);
std::tuple<bool, bool> checkACL(const std::string& user, const YAML::Node& aclFile);
std::optional<std::string> 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<bool, bool> 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<bool, bool> 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<bool>())
if(userDict["can_read"] and userDict["can_read"].as<bool>())
{
can_read = true;
}
if(user["can_write"] and user["can_write"].as<bool>())
if(userDict["can_write"] and userDict["can_write"].as<bool>())
{
can_write = true;
}
return std::make_tuple(can_read, can_write);
}
std::optional<std::string> 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<std::string>());
}
else
{
pathToKey = std::filesystem::path(keyFile["default"].as<std::string>());
}
return read_key(std::filesystem::absolute(pathToKey).string());
}

View File

@ -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<Authorizer>(
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<struct mosquitto_evt_disconnect *>(event_data);
const std::string username = std::string(mosquitto_client_username(disconnect_data->client));
authorizer->logout(username);

View File

@ -35,14 +35,13 @@ std::optional<std::string> read_key(const std::string & key_file)
std::tuple<bool, std::chrono::time_point<std::chrono::system_clock>> 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);