Mass refactor. Clang-format. Start adding tests. Use FetchContent.

This commit is contained in:
jimmy committed 2022-03-24 00:20:21 +00:00
1 parent c76ff553cc
commit 3309b19c2e
19 files changed
+310 -223

No files matched your search

+21
View File
@@ -0,0 +1,21 @@
---
Language: Cpp
BasedOnStyle: Google
AccessModifierOffset: -4
AlignAfterOpenBracket: AlwaysBreak
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces: Allman
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterColon
ColumnLimit: 100
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
DerivePointerAlignment: false
IndentWidth: 4
PointerAlignment: Middle
ReflowComments: false
...
+21 -16
View File
@@ -1,34 +1,39 @@
cmake_minimum_required(VERSION 3.16)
project(j7s-mosquitto-plugin)
# https://github.com/Sarcasm/cmake-superbuild/
option (USE_SUPERBUILD "Whether or not a superbuild should be invoked" ON)
include(external-deps.cmake)
if (USE_SUPERBUILD)
project(j7s-mosquitto-plugin-super)
include(external-deps.cmake)
return()
else()
project(j7s-mosquitto-plugin)
endif()
find_package(jwt-cpp)
find_package(argparse)
find_package(OpenSSL)
set(CMAKE_CXX_STANDARD 20)
add_library(utils SHARED src/utils.cpp)
target_include_directories(utils PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(utils OpenSSL::Crypto jwt-cpp)
add_library(j7s-plugin SHARED src/j7s-plugin.cpp src/AuthList.cpp src/Authorizer.cpp)
target_include_directories(j7s-plugin PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
${jwt-cpp_INCLUDE_DIR}
)
target_link_libraries(j7s-plugin OpenSSL::Crypto)
target_link_libraries(j7s-plugin utils)
add_executable(gen-token src/gen-token.cpp)
target_include_directories(gen-token PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
${jwt-cpp_INCLUDE_DIR}
)
target_link_libraries(gen-token OpenSSL::Crypto argparse::argparse)
target_link_libraries(gen-token utils argparse::argparse)
if(BUILD_TESTING)
include(GoogleTest)
enable_testing()
add_executable(authorizer_test test/authorizer_test.cpp)
target_link_libraries(authorizer_test GTest::gtest_main)
gtest_discover_tests(authorizer_test)
endif()
+1 -1
View File
@@ -4,7 +4,7 @@ Authentication using JWTs for the mosquitto mqtt broker.
## Dependencies
```
sudo apt install mosquitto-dev g++ cmake libmosquitto-dev mosquitto-clients openssl libssl-dev
sudo apt install mosquitto-dev g++ cmake libmosquitto-dev mosquitto-clients openssl libssl-dev googletest
```
## Generating offline keys
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
+11 -28
View File
@@ -1,39 +1,22 @@
include (ExternalProject)
include(FetchContent)
set (DEPENDENCIES)
set (EXTRA_CMAKE_ARGS)
FetchContent_Declare(googletest
GIT_REPOSITORY "https://github.com/google/googletest.git"
GIT_TAG "release-1.11.0"
GIT_SHALLOW "True"
)
FetchContent_MakeAvailable(googletest)
list(APPEND DEPENDENCIES ep_jwt-cpp)
ExternalProject_Add(ep_jwt-cpp
PREFIX ep_jwt-cpp
FetchContent_Declare(jwt-cpp
GIT_REPOSITORY "https://github.com/Thalhammer/jwt-cpp.git"
GIT_TAG "v0.5.2"
GIT_SHALLOW "True"
CMAKE_ARGS -DJWT_CMAKE_FILES_INSTALL_DIR=${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp
)
list (APPEND EXTRA_CMAKE_ARGS
-Djwt-cpp_DIR=${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp -Djwt-cpp_INCLUDE_DIR=${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp/include
)
FetchContent_MakeAvailable(jwt-cpp)
list(APPEND DEPENDENCIES ep_argparse)
ExternalProject_Add(ep_argparse
PREFIX ep_argparse
FetchContent_Declare(argparse
GIT_REPOSITORY "https://github.com/p-ranav/argparse.git"
GIT_TAG "v2.2"
GIT_SHALLOW "True"
CMAKE_ARGS -DARGPARSE_CMAKE_FILES_INSTALL_DIR=${CMAKE_CURRENT_BINARY_DIR}/argparse -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/argparse
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/argparse
)
list (APPEND EXTRA_CMAKE_ARGS
-Dargparse_DIR=${CMAKE_CURRENT_BINARY_DIR}/argparse
)
ExternalProject_Add (ep_j7s-mosquitto-plugin
PREFIX ep_j7s-mosquitto-plugin
DEPENDS ${DEPENDENCIES}
SOURCE_DIR "${PROJECT_SOURCE_DIR}"
CMAKE_ARGS -DUSE_SUPERBUILD=OFF ${EXTRA_CMAKE_ARGS}
INSTALL_COMMAND ""
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}
)
FetchContent_MakeAvailable(argparse)
+17 -11
View File
@@ -15,19 +15,25 @@
// Mosquitto authentication plugin that using Authorizer to authorize
// users using jwts.
extern "C" {
#include "mosquitto.h"
#include "mosquitto_broker.h"
#include "mosquitto_plugin.h"
extern "C"
{
#include "mosquitto.h"
#include "mosquitto_broker.h"
#include "mosquitto_plugin.h"
}
// Stuff we're "exporting" for the dynamic loading.
extern "C" {
int mosquitto_plugin_version(int supported_version_count, const int *supported_versions);
int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **userdata, struct mosquitto_opt *options, int option_count);
int mosquitto_plugin_cleanup(void *userdata, struct mosquitto_opt *options, int option_count);
extern "C"
{
int mosquitto_plugin_version(int supported_version_count, const int * supported_versions);
int mosquitto_plugin_init(
mosquitto_plugin_id_t * identifier,
void ** userdata,
struct mosquitto_opt * options,
int option_count);
int mosquitto_plugin_cleanup(void * userdata, struct mosquitto_opt * options, int option_count);
}
// My functions
int j7s_auth_basic_auth_callback(int event, void *event_data, void *userdata);
int j7s_acl_check_callback(int event, void *event_data, void *userdata);
int j7s_disconnect_callback(int event, void *event_data, void *userdata);
int j7s_auth_basic_auth_callback(int event, void * event_data, void * userdata);
int j7s_acl_check_callback(int event, void * event_data, void * userdata);
int j7s_disconnect_callback(int event, void * event_data, void * userdata);
+36
View File
@@ -0,0 +1,36 @@
// Copyright 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.
// 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 <chrono>
#include <optional>
#include <string>
#include <tuple>
std::optional<std::string> read_key(const std::string & key_file);
std::tuple<bool, bool> validate(
const std::string & token,
const std::string & username,
const std::string & issuer,
const std::string & pub_key);
std::string gen_token(
const std::string & issuer,
const std::string & username,
const std::string & can_read,
const std::string & can_write,
const std::string & pub_key,
const std::string & priv_key,
const std::chrono::time_point<std::chrono::system_clock> & expr_time);
+10 -12
View File
@@ -11,39 +11,37 @@
// 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/AuthList.hpp>
#include <algorithm>
#include <j7s-plugin/AuthList.hpp>
AuthList::AuthList():
_allowedUsernames{}
{
}
AuthList::AuthList() : _allowedUsernames{} {}
void AuthList::add(const std::string& username)
void AuthList::add(const std::string & username)
{
// Is the username already in the list?
// If not add it.
if(not confirm(username))
if (not confirm(username))
{
_allowedUsernames.emplace_front(username);
}
}
void AuthList::remove(const std::string& username)
void AuthList::remove(const std::string & username)
{
// Is the user in the list?
// Is so, remove it,
if(confirm(username))
if (confirm(username))
{
_allowedUsernames.remove(username);
}
}
bool AuthList::confirm(const std::string& 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))
const auto found =
std::find(std::begin(_allowedUsernames), std::end(_allowedUsernames), username);
if (found != std::end(_allowedUsernames))
{
return true;
}
+15 -70
View File
@@ -11,95 +11,41 @@
// 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/Authorizer.hpp>
#include <j7s-plugin/AuthList.hpp>
#include <jwt-cpp/jwt.h>
#include <j7s-plugin/utils.h>
#include <iostream>
#include <fstream>
#include <sstream>
#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}
Authorizer::Authorizer(const std::string & pub_key, const std::string & issuer) :
_pub_key{pub_key}, _issuer{issuer}
{
}
std::optional<std::string> Authorizer::read_key(const std::string& key_file)
{
// Read key from file.
std::ifstream key_stream(key_file, std::ios::binary);
if(not key_stream)
{
return std::nullopt;
}
std::stringstream ss;
ss << key_stream.rdbuf();
return ss.str();
}
void Authorizer::add_unknown(const std::string& username)
void Authorizer::add_unknown(const std::string & username)
{
_unknownList.add(username);
}
bool Authorizer::is_unknown(const std::string& username)
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)
bool Authorizer::add(const std::string & token, const std::string & username)
{
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));
try
{
verifier.verify(decoded_token);
}
catch(jwt::error::token_verification_exception& exception)
{
std::cerr << exception.what() << std::endl;
return false;
}
auto claims = decoded_token.get_payload_claims();
// Check username matches.
if(not claims.contains("upn"))
{
std::cerr << "Missing upn." << std::endl;
return false;
}
if(claims["upn"].as_string() != username)
{
std::cerr << "Wrong username." << std::endl;
return false;
}
// Check for mqtt-write claim value.
if(not (claims.contains("mqtt-write") and claims.contains("mqtt-read")))
{
std::cerr << "Missing mqtt-write or mqtt-read." << std::endl;
return false;
}
bool can_write = claims["mqtt-write"].as_bool();
bool can_read = claims["mqtt-read"].as_bool();
if(not (can_write or can_read))
const auto [can_read, can_write] = validate(token, username, _issuer, _pub_key);
if (not(can_write or can_read))
{
std::cerr << "Can't write or can't read." << std::endl;
return false;
}
if(can_write)
if (can_write)
{
_writeList.add(username);
}
if(can_read)
if (can_read)
{
_readList.add(username);
}
@@ -107,20 +53,19 @@ bool Authorizer::add(const std::string& token, const std::string& username)
return true;
}
bool Authorizer::can_read(const std::string& username)
bool Authorizer::can_read(const std::string & username)
{
return _readList.confirm(username);
}
bool Authorizer::can_write(const std::string& username)
bool Authorizer::can_write(const std::string & username)
{
return _writeList.confirm(username);
}
void Authorizer::logout(const std::string& username)
void Authorizer::logout(const std::string & username)
{
_writeList.remove(username);
_readList.remove(username);
_unknownList.remove(username);
}
+16 -44
View File
@@ -12,35 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <argparse/argparse.hpp>
#include <jwt-cpp/jwt.h>
#include <j7s-plugin/utils.h>
#include <iostream>
#include <argparse/argparse.hpp>
#include <chrono>
#include <optional>
#include <string>
#include <fstream>
#include <sstream>
#include <filesystem>
std::optional<std::string> read_key(const std::string& key_file);
int main(int argc, char *argv[])
int main(int argc, char * argv[])
{
argparse::ArgumentParser program("gen-token", "0.0.0");
program.add_argument("--pub-key")
.required()
.help("Pub key of signer.");
program.add_argument("--priv-key")
.required()
.help("Private key of signer.");
program.add_argument("--issuer")
.required()
.help("Issuer to assign to signed key.");
program.add_argument("--username")
.required()
.help("Username assigned to key.");
program.add_argument("--pub-key").required().help("Pub key of signer.");
program.add_argument("--priv-key").required().help("Private key of signer.");
program.add_argument("--issuer").required().help("Issuer to assign to signed key.");
program.add_argument("--username").required().help("Username assigned to key.");
program.add_argument("--valid-days")
.required()
.help("Days from now until the token will be valid.");
@@ -68,7 +53,7 @@ int main(int argc, char *argv[])
const auto pub_key_file = program.get<std::string>("--pub-key");
const auto pub_key = read_key(std::filesystem::absolute(pub_key_file));
if(not pub_key or not priv_key)
if (not pub_key or not priv_key)
{
std::cerr << "Could not open key!" << std::endl;
return -2;
@@ -80,29 +65,16 @@ int main(int argc, char *argv[])
const auto expr_time = std::chrono::system_clock::now() +
std::chrono::days(std::stoi(program.get<std::string>("--valid-days")));
const auto token = jwt::create()
.set_type("JWT")
.set_issuer(program.get<std::string>("--issuer"))
.set_payload_claim("upn", jwt::claim(program.get<std::string>("--username")))
.set_payload_claim("mqtt-write", jwt::claim(can_read))
.set_payload_claim("mqtt-read", jwt::claim(can_write))
.set_expires_at(expr_time)
.sign(jwt::algorithm::rs256(pub_key.value(), priv_key.value(), "", ""));
const auto token = gen_token(
program.get<std::string>("--issuer"),
program.get<std::string>("--username"),
can_read,
can_write,
pub_key.value(),
priv_key.value(),
expr_time);
std::cout << token;
return 0;
}
std::optional<std::string> read_key(const std::string& key_file)
{
// Read key from file.
std::ifstream key_stream(key_file, std::ios::binary);
if(not key_stream)
{
return std::nullopt;
}
std::stringstream ss;
ss << key_stream.rdbuf();
return ss.str();
}
+51 -41
View File
@@ -12,32 +12,36 @@
// 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 <string>
#include <memory>
#include <string>
// Mosquitto Globals
static mosquitto_plugin_id_t *plugin_id = nullptr;
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)
int mosquitto_plugin_version(int supported_version_count, const int * supported_versions)
{
for(int index = 0; index < supported_version_count; index++)
for (int index = 0; index < supported_version_count; index++)
{
if(supported_versions[index] == 5)
if (supported_versions[index] == 5)
{
return 5;
}
}
return -1;
return 5;
}
}
return -1;
}
int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **userdata, struct mosquitto_opt *options, int option_count)
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)
if (option_count != 2)
{
mosquitto_log_printf(MOSQ_LOG_ERR, "Missing an option. Found: %d", option_count);
return MOSQ_ERR_INVAL;
@@ -45,23 +49,23 @@ int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **userdata, st
std::string public_key;
std::string issuer;
for(int index = 0; index < option_count; index++)
for (int index = 0; index < option_count; index++)
{
const auto key = std::string(options[index].key);
if(key == "public_key")
if (key == "public_key")
{
const auto key = Authorizer::read_key(std::string(options[index].value));
if(not key or key->empty())
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")
else if (key == "issuer")
{
issuer = std::string(options[index].value);
if(issuer.empty())
if (issuer.empty())
{
mosquitto_log_printf(MOSQ_LOG_ERR, "issuer not set.");
return MOSQ_ERR_INVAL;
@@ -72,50 +76,56 @@ int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **userdata, st
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);
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)
int mosquitto_plugin_cleanup(void * userdata, struct mosquitto_opt * options, int option_count)
{
if(plugin_id)
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);
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)
int j7s_auth_basic_auth_callback(int event, void * event_data, void * userdata)
{
if(not authorizer)
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);
struct mosquitto_evt_basic_auth * auth_data =
static_cast<struct mosquitto_evt_basic_auth *>(event_data);
if(!auth_data->username)
if (!auth_data->username)
{
// We need a username to do anything.
return MOSQ_ERR_PLUGIN_DEFER;
}
if(!auth_data->password)
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));
bool is_authed =
authorizer->add(std::string(auth_data->password), std::string(auth_data->username));
if(is_authed)
if (is_authed)
{
return MOSQ_ERR_SUCCESS;
}
@@ -125,23 +135,24 @@ int j7s_auth_basic_auth_callback(int event, void *event_data, void *userdata)
}
}
int j7s_acl_check_callback(int event, void *event_data, void *userdata)
int j7s_acl_check_callback(int event, void * event_data, void * userdata)
{
if(not authorizer)
if (not authorizer)
{
return MOSQ_ERR_ACL_DENIED;
}
struct mosquitto_evt_acl_check *acl_data = static_cast<struct mosquitto_evt_acl_check *>(event_data);
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))
if (authorizer->is_unknown(username))
{
return MOSQ_ERR_PLUGIN_DEFER;
}
switch(acl_data->access)
switch (acl_data->access)
{
case MOSQ_ACL_SUBSCRIBE:
return MOSQ_ERR_PLUGIN_DEFER;
@@ -156,13 +167,12 @@ int j7s_acl_check_callback(int event, void *event_data, void *userdata)
}
}
int j7s_disconnect_callback(int event, void *event_data, void *userdata)
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);
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;
}
+101
View File
@@ -0,0 +1,101 @@
// Copyright 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.
// 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/utils.h>
#include <jwt-cpp/jwt.h>
#include <fstream>
#include <iostream>
#include <sstream>
std::optional<std::string> read_key(const std::string & key_file)
{
// Read key from file.
std::ifstream key_stream(key_file, std::ios::binary);
if (not key_stream)
{
return std::nullopt;
}
std::stringstream ss;
ss << key_stream.rdbuf();
return ss.str();
}
std::tuple<bool, bool> 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));
try
{
verifier.verify(decoded_token);
}
catch (jwt::error::token_verification_exception & exception)
{
std::cerr << exception.what() << std::endl;
return std::make_tuple(false, false);
}
auto claims = decoded_token.get_payload_claims();
// Check username matches.
if (not claims.contains("upn"))
{
std::cerr << "Missing upn." << std::endl;
return std::make_tuple(false, false);
}
if (claims["upn"].as_string() != username)
{
std::cerr << "Wrong username." << std::endl;
return std::make_tuple(false, false);
}
// Check for mqtt-write claim value.
if (not(claims.contains("mqtt-write") and claims.contains("mqtt-read")))
{
std::cerr << "Missing mqtt-write or mqtt-read." << std::endl;
return std::make_tuple(false, false);
}
bool can_read = claims["mqtt-read"].as_bool();
bool can_write = claims["mqtt-write"].as_bool();
return std::make_tuple(can_read, can_write);
}
std::string gen_token(
const std::string & issuer,
const std::string & username,
const std::string & can_read,
const std::string & can_write,
const std::string & pub_key,
const std::string & priv_key,
const std::chrono::time_point<std::chrono::system_clock> & expr_time)
{
const auto token = jwt::create()
.set_type("JWT")
.set_issuer(issuer)
.set_payload_claim("upn", jwt::claim(username))
.set_payload_claim("mqtt-read", jwt::claim(can_read))
.set_payload_claim("mqtt-write", jwt::claim(can_write))
.set_expires_at(expr_time)
.sign(jwt::algorithm::rs256(pub_key, priv_key, "", ""));
return token;
}
+10
View File
@@ -0,0 +1,10 @@
#include <j7s-plugin/Authorizer.hpp>
#include "gtest/gtest.h"
// Demonstrate some basic assertions.
TEST(AuthorizerTest, BasicAssertions) {
// Expect two strings not to be equal.
EXPECT_STRNE("hello", "world");
// Expect equality.
EXPECT_EQ(7 * 6, 42);
}