j7s-jwt-mosquitto-auth/src/j7s-plugin.cpp

179 lines
5.3 KiB
C++

// Copyright 2021 James Pace
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <j7s-plugin/j7s-plugin.h>
#include <j7s-plugin/Authorizer.hpp>
#include <memory>
#include <string>
// Mosquitto Globals
static mosquitto_plugin_id_t * plugin_id = nullptr;
static std::unique_ptr<Authorizer> authorizer = nullptr;
int mosquitto_plugin_version(int supported_version_count, const int * supported_versions)
{
for (int index = 0; index < supported_version_count; index++)
{
if (supported_versions[index] == 5)
{
return 5;
}
}
return -1;
}
int mosquitto_plugin_init(
mosquitto_plugin_id_t * identifier,
void ** userdata,
struct mosquitto_opt * options,
int option_count)
{
plugin_id = identifier;
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;
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));
if (not key or key->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.");
return MOSQ_ERR_INVAL;
}
}
}
authorizer = std::make_unique<Authorizer>(public_key, issuer);
// Register the callbacks.
mosquitto_callback_register(
plugin_id, MOSQ_EVT_BASIC_AUTH, j7s_auth_basic_auth_callback, NULL, NULL);
mosquitto_callback_register(plugin_id, MOSQ_EVT_ACL_CHECK, j7s_acl_check_callback, NULL, NULL);
mosquitto_callback_register(
plugin_id, MOSQ_EVT_DISCONNECT, j7s_disconnect_callback, NULL, NULL);
// May want MOSQ_EVT_RELOAD as well.
return MOSQ_ERR_SUCCESS;
}
int mosquitto_plugin_cleanup(void * userdata, struct mosquitto_opt * options, int option_count)
{
if (plugin_id)
{
mosquitto_callback_unregister(
plugin_id, MOSQ_EVT_BASIC_AUTH, j7s_auth_basic_auth_callback, NULL);
mosquitto_callback_unregister(plugin_id, MOSQ_EVT_ACL_CHECK, j7s_acl_check_callback, NULL);
mosquitto_callback_unregister(
plugin_id, MOSQ_EVT_DISCONNECT, j7s_disconnect_callback, NULL);
}
return MOSQ_ERR_SUCCESS;
}
int j7s_auth_basic_auth_callback(int event, void * event_data, void * userdata)
{
if (not authorizer)
{
// Not sure this is possible.
return MOSQ_ERR_AUTH;
}
struct mosquitto_evt_basic_auth * auth_data =
static_cast<struct mosquitto_evt_basic_auth *>(event_data);
if (!auth_data->username)
{
// We need a username to do anything.
return MOSQ_ERR_PLUGIN_DEFER;
}
if (!auth_data->password)
{
authorizer->add_unknown(std::string(auth_data->username));
return MOSQ_ERR_PLUGIN_DEFER;
}
bool is_authed =
authorizer->add(std::string(auth_data->password), std::string(auth_data->username));
if (is_authed)
{
return MOSQ_ERR_SUCCESS;
}
else
{
return MOSQ_ERR_AUTH;
}
}
int j7s_acl_check_callback(int event, void * event_data, void * userdata)
{
if (not authorizer)
{
return MOSQ_ERR_ACL_DENIED;
}
struct mosquitto_evt_acl_check * acl_data =
static_cast<struct mosquitto_evt_acl_check *>(event_data);
const std::string username = std::string(mosquitto_client_username(acl_data->client));
if (authorizer->is_unknown(username))
{
return MOSQ_ERR_PLUGIN_DEFER;
}
switch (acl_data->access)
{
case MOSQ_ACL_SUBSCRIBE:
return MOSQ_ERR_PLUGIN_DEFER;
case MOSQ_ACL_UNSUBSCRIBE:
return MOSQ_ERR_PLUGIN_DEFER;
case MOSQ_ACL_WRITE:
return (authorizer->can_write(username) ? MOSQ_ERR_SUCCESS : MOSQ_ERR_ACL_DENIED);
case MOSQ_ACL_READ:
return (authorizer->can_read(username) ? MOSQ_ERR_SUCCESS : MOSQ_ERR_ACL_DENIED);
default:
return MOSQ_ERR_ACL_DENIED;
}
}
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);
return MOSQ_ERR_SUCCESS;
}