Add tests. Cleanup formatting.

This commit is contained in:
James Pace 2022-03-25 02:23:28 +00:00
parent 5ef2e0b929
commit 0b4a73e2b7
10 changed files with 159 additions and 105 deletions

View File

@ -16,6 +16,6 @@ ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4 ContinuationIndentWidth: 4
DerivePointerAlignment: false DerivePointerAlignment: false
IndentWidth: 4 IndentWidth: 4
PointerAlignment: Middle PointerAlignment: Right
ReflowComments: false ReflowComments: false
... ...

View File

@ -41,7 +41,11 @@ if(BUILD_TESTING)
enable_testing() enable_testing()
add_executable(authorizer_test test/authorizer_test.cpp) add_executable(token_test test/token_test.cpp)
target_link_libraries(authorizer_test GTest::gtest_main) target_include_directories(token_test PUBLIC
gtest_discover_tests(authorizer_test) $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(token_test utils GTest::gtest_main)
gtest_discover_tests(token_test)
endif() endif()

View File

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

View File

@ -13,27 +13,30 @@
// limitations under the License. // limitations under the License.
#include <j7s-plugin/utils.h> #include <j7s-plugin/utils.h>
#include <filesystem>
#include <iostream> #include <iostream>
#include <j7s-plugin/AuthList.hpp> #include <j7s-plugin/AuthList.hpp>
#include <j7s-plugin/Authorizer.hpp> #include <j7s-plugin/Authorizer.hpp>
#include <tuple> #include <tuple>
#include <filesystem>
// Util. // Util.
std::tuple<bool, bool> checkACL(const std::string &user, const YAML::Node &aclFile); 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); std::optional<std::string> getKey(const std::string &user, const YAML::Node &keyFile);
// Class implementation. // Class implementation.
Authorizer::Authorizer( Authorizer::Authorizer(const std::string &keyFilePath, const std::string &aclFilePath) :
const std::string & keyFilePath, const std::string & aclFilePath) :
_keyFile{keyFilePath}, _aclFile{aclFilePath} _keyFile{keyFilePath}, _aclFile{aclFilePath}
{ {
} }
bool Authorizer::add(const std::string &token, const std::string &username) bool Authorizer::add(const std::string &token, const std::string &username)
{ {
// We should protect from this already.
if (token.empty() or username.empty())
{
return false;
}
const auto key = getKey(username, _keyFile); const auto key = getKey(username, _keyFile);
if (not key) if (not key)
@ -64,7 +67,6 @@ bool Authorizer::add(const std::string & token, const std::string & username)
return true; return true;
} }
bool Authorizer::can_read(const std::string &username) bool Authorizer::can_read(const std::string &username)
{ {
return _readList.confirm(username); return _readList.confirm(username);
@ -92,7 +94,6 @@ bool Authorizer::is_unknown(const std::string & username)
return (username.empty() or _unknownList.confirm(username)); return (username.empty() or _unknownList.confirm(username));
} }
// Util. // Util.
std::tuple<bool, bool> checkACL(const std::string &user, const YAML::Node &aclFile) std::tuple<bool, bool> checkACL(const std::string &user, const YAML::Node &aclFile)
{ {

View File

@ -24,7 +24,6 @@ int main(int argc, char * argv[])
program.add_argument("--pub-key").required().help("Pub key of signer."); 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("--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("--username").required().help("Username assigned to key.");
program.add_argument("--valid-days") program.add_argument("--valid-days")
.required() .required()
@ -67,12 +66,7 @@ int main(int argc, char * argv[])
now + std::chrono::days(std::stoi(program.get<std::string>("--valid-days"))); now + std::chrono::days(std::stoi(program.get<std::string>("--valid-days")));
const auto token = gen_token( const auto token = gen_token(
program.get<std::string>("--issuer"), program.get<std::string>("--username"), pub_key.value(), priv_key.value(), now, expr_time);
program.get<std::string>("--username"),
pub_key.value(),
priv_key.value(),
now,
expr_time);
std::cout << token; std::cout << token;

View File

@ -12,14 +12,12 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include <j7s-plugin/j7s-plugin.h> #include <j7s-plugin/j7s-plugin.h>
#include <j7s-plugin/Authorizer.hpp>
#include <j7s-plugin/utils.h> #include <j7s-plugin/utils.h>
#include <filesystem>
#include <j7s-plugin/Authorizer.hpp>
#include <memory> #include <memory>
#include <string> #include <string>
#include <filesystem>
// Mosquitto Globals // Mosquitto Globals
static mosquitto_plugin_id_t *plugin_id = nullptr; static mosquitto_plugin_id_t *plugin_id = nullptr;

View File

@ -18,6 +18,7 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <system_error>
std::optional<std::string> read_key(const std::string &key_file) std::optional<std::string> read_key(const std::string &key_file)
{ {
@ -29,26 +30,34 @@ std::optional<std::string> read_key(const std::string & key_file)
} }
std::stringstream ss; std::stringstream ss;
ss << key_stream.rdbuf(); ss << key_stream.rdbuf();
return ss.str(); const std::string key(ss.str());
if (key.empty())
{
return std::nullopt;
}
return key;
} }
std::tuple<bool, std::chrono::time_point<std::chrono::system_clock>> validate( std::tuple<bool, std::chrono::time_point<std::chrono::system_clock>> validate(
const std::string & token, const std::string &token, const std::string &username, const std::string &pub_key)
const std::string & username,
const std::string & pub_key)
{ {
if (token.empty() or username.empty() or pub_key.empty())
{
return std::make_tuple(false, std::chrono::system_clock::now());
}
const auto decoded_token = jwt::decode(token); const auto decoded_token = jwt::decode(token);
// Is the token valid?
const auto verifier =
jwt::verify().allow_algorithm(jwt::algorithm::rs256(pub_key));
try try
{ {
// Is the token valid?
const auto verifier = jwt::verify().allow_algorithm(jwt::algorithm::rs256(pub_key));
verifier.verify(decoded_token); verifier.verify(decoded_token);
} }
catch (jwt::error::token_verification_exception & exception) catch (std::system_error &exception)
{ {
std::cerr << exception.what() << std::endl; std::cerr << "Token Verification Failed: " << exception.what() << std::endl;
return std::make_tuple(false, std::chrono::system_clock::now()); return std::make_tuple(false, std::chrono::system_clock::now());
} }
auto claims = decoded_token.get_payload_claims(); auto claims = decoded_token.get_payload_claims();
@ -71,7 +80,7 @@ std::tuple<bool, std::chrono::time_point<std::chrono::system_clock>> validate(
std::cerr << "Missing mqtt claim." << std::endl; std::cerr << "Missing mqtt claim." << std::endl;
return std::make_tuple(false, std::chrono::system_clock::now()); return std::make_tuple(false, std::chrono::system_clock::now());
} }
if(not claims["mqtt"].as_bool()) if (not(claims["mqtt"].as_string() == "true"))
{ {
std::cerr << "Not claiming can do mqtt." << std::endl; std::cerr << "Not claiming can do mqtt." << std::endl;
return std::make_tuple(false, std::chrono::system_clock::now()); return std::make_tuple(false, std::chrono::system_clock::now());
@ -88,7 +97,6 @@ std::tuple<bool, std::chrono::time_point<std::chrono::system_clock>> validate(
} }
std::string gen_token( std::string gen_token(
const std::string & issuer,
const std::string &username, const std::string &username,
const std::string &pub_key, const std::string &pub_key,
const std::string &priv_key, const std::string &priv_key,
@ -97,7 +105,6 @@ std::string gen_token(
{ {
const auto token = jwt::create() const auto token = jwt::create()
.set_type("JWT") .set_type("JWT")
.set_issuer(issuer)
.set_payload_claim("upn", jwt::claim(username)) .set_payload_claim("upn", jwt::claim(username))
.set_payload_claim("mqtt", jwt::claim(std::string("true"))) .set_payload_claim("mqtt", jwt::claim(std::string("true")))
.set_issued_at(issue_time) .set_issued_at(issue_time)

View File

@ -12,9 +12,13 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include <j7s-plugin/utils.h> #include <j7s-plugin/utils.h>
#include <ctime>
#include <iostream>
#include "gtest/gtest.h" #include "gtest/gtest.h"
constexpr std::string priv_key_a = const std::string priv_key_a =
R"(-----BEGIN PRIVATE KEY----- R"(-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC+ouwDpYOWDEyM MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC+ouwDpYOWDEyM
nJhwejOn+boDxw4ntiOR3kRzIANuJrbEPf3UJFL+SPPzzY7NU1A6XPz/NAccbvfn nJhwejOn+boDxw4ntiOR3kRzIANuJrbEPf3UJFL+SPPzzY7NU1A6XPz/NAccbvfn
@ -42,9 +46,8 @@ mnjcxB3ZtRoyFWvfYx9wD3/rV4sMtiIoorNgtJMCgYABDGH571InLE9HMO1+Czmp
zyvcbTAq8GiN0G4Rok95+THfa726N6BcmkZUK1xWaleO6xNGrDsBghfmgw629Ujk zyvcbTAq8GiN0G4Rok95+THfa726N6BcmkZUK1xWaleO6xNGrDsBghfmgw629Ujk
UJ73ERYyATbA4GHM9f3dbje8pd2SFa4xF+0Xp09qY380aJrZSWsklBZPUmYiU6+W UJ73ERYyATbA4GHM9f3dbje8pd2SFa4xF+0Xp09qY380aJrZSWsklBZPUmYiU6+W
i2MlHfF+44rBO9igkUjQKA== i2MlHfF+44rBO9igkUjQKA==
-----END PRIVATE KEY-----)" -----END PRIVATE KEY-----)";
const std::string pub_key_a =
constexpr std::string pub_key_a =
R"(-----BEGIN PUBLIC KEY----- R"(-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvqLsA6WDlgxMjJyYcHoz MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvqLsA6WDlgxMjJyYcHoz
p/m6A8cOJ7Yjkd5EcyADbia2xD391CRS/kjz882OzVNQOlz8/zQHHG7353O/HY9d p/m6A8cOJ7Yjkd5EcyADbia2xD391CRS/kjz882OzVNQOlz8/zQHHG7353O/HY9d
@ -54,7 +57,8 @@ m0MrwLnIGkwyp1O9r4jTsaoPL+WxhflrU4ysHs5mLckPGe3aRkGzC9bZExEME6uZ
9Hfn9zmE+Y53zD0qSiYmDZS6sQiTxozXEkcY880bf3EWM4QD364jv35GBrUHCzQe 9Hfn9zmE+Y53zD0qSiYmDZS6sQiTxozXEkcY880bf3EWM4QD364jv35GBrUHCzQe
XQIDAQAB XQIDAQAB
-----END PUBLIC KEY-----)"; -----END PUBLIC KEY-----)";
constexpr std::string priv_key_b =
const std::string priv_key_b =
R"(-----BEGIN PRIVATE KEY----- R"(-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCYq8QNOXZRoAid MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCYq8QNOXZRoAid
R7cKE9byr+9WekPMNDNkaKTjRUoXj8lUgno3y5tIDEIqhcv4thTLAxzQD4N+bVA3 R7cKE9byr+9WekPMNDNkaKTjRUoXj8lUgno3y5tIDEIqhcv4thTLAxzQD4N+bVA3
@ -83,7 +87,7 @@ t4/SVFXBRLzse8AB3V6qSEwgCaUfeuj0Qq93nrkTIodHFWXuFoQTgQrA29VWbK6x
PSwjdVNwYES+Hg+LbXP8Fo+u5sGhcWLzWdmFp3UdUm5Mv76Oo+MriZNnS4RQiX0+ PSwjdVNwYES+Hg+LbXP8Fo+u5sGhcWLzWdmFp3UdUm5Mv76Oo+MriZNnS4RQiX0+
Y8PiIt3YYCsowmchtEggaQ== Y8PiIt3YYCsowmchtEggaQ==
-----END PRIVATE KEY-----)"; -----END PRIVATE KEY-----)";
constexpr std::string pub_key_b = const std::string pub_key_b =
R"(-----BEGIN PUBLIC KEY----- R"(-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmKvEDTl2UaAInUe3ChPW MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmKvEDTl2UaAInUe3ChPW
8q/vVnpDzDQzZGik40VKF4/JVIJ6N8ubSAxCKoXL+LYUywMc0A+Dfm1QN1xdWTH5 8q/vVnpDzDQzZGik40VKF4/JVIJ6N8ubSAxCKoXL+LYUywMc0A+Dfm1QN1xdWTH5
@ -94,11 +98,60 @@ wmwOQRQS2h5xR1Z/o6HBIGnzllI67mHMK1HGbgq/DsvCi6s+sOV7Wvwe8dC8sd87
xwIDAQAB xwIDAQAB
-----END PUBLIC KEY-----)"; -----END PUBLIC KEY-----)";
using time_T = std::chrono::time_point<std::chrono::system_clock>;
// Demonstrate some basic assertions. TEST(TokenTest, SimpleTwoWay)
TEST(TokenTest, TwoWay) { {
constexpr std::string issuer = "james-keycloak"; const std::string username = "james";
constexpr std::string username = "james"; const time_T now = std::chrono::system_clock::now();
constexpr const time_T expire = now + std::chrono::seconds(1);
const auto token = gen_token(
const auto token = gen_token(username, pub_key_a, priv_key_a, now, expire);
auto [valid, end] = validate(token, username, pub_key_a);
std::time_t expire_time = std::chrono::system_clock::to_time_t(expire);
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
EXPECT_TRUE(valid);
EXPECT_EQ(end_time, expire_time);
}
TEST(TokenTest, InvalidUsername)
{
const std::string username = "james";
const time_T now = std::chrono::system_clock::now();
const time_T expire = now + std::chrono::seconds(1);
const auto token = gen_token(username, pub_key_a, priv_key_a, now, expire);
const std::string notjames = "not_james";
const auto [valid, end] = validate(token, notjames, pub_key_a);
EXPECT_FALSE(valid);
}
TEST(TokenTest, WrongKey)
{
const std::string username = "james";
const time_T now = std::chrono::system_clock::now();
const time_T expire = now + std::chrono::seconds(1);
const auto token = gen_token(username, pub_key_a, priv_key_a, now, expire);
const auto [valid, end] = validate(token, username, pub_key_b);
EXPECT_FALSE(valid);
}
TEST(TokenTest, NonsenseKey)
{
const std::string username = "james";
const time_T now = std::chrono::system_clock::now();
const time_T expire = now + std::chrono::seconds(1);
const auto token = gen_token(username, pub_key_a, priv_key_a, now, expire);
const std::string nonsenseKey = "lslslslsl";
const auto [valid, end] = validate(token, username, nonsenseKey);
EXPECT_FALSE(valid);
} }