From 12920e06de0149b0953322260d6cc354e7c594a6 Mon Sep 17 00:00:00 2001 From: Daniel Nicoletti Date: Tue, 5 Nov 2024 23:48:05 -0300 Subject: [PATCH 1/2] JWT QtJson traits --- src/CMakeLists.txt | 2 + src/googlecloudoauth2.cpp | 3 +- src/jwt/defaults.h | 86 +++++++++++++++++ src/jwt/traits.h | 196 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 src/jwt/defaults.h create mode 100644 src/jwt/traits.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 69563b1..3befc91 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -74,6 +74,8 @@ add_library(${target} ${firebase_admin_SRC} ${firebase_admin_HEADERS} ${firebase_admin_HEADERS_PRIVATE} + jwt/defaults.h + jwt/traits.h ) set_compiler_flags(${target}) diff --git a/src/googlecloudoauth2.cpp b/src/googlecloudoauth2.cpp index 81999d2..5324f6b 100644 --- a/src/googlecloudoauth2.cpp +++ b/src/googlecloudoauth2.cpp @@ -10,7 +10,8 @@ #include -#include "jwt-cpp/jwt.h" +#define JWT_DISABLE_PICOJSON +#include "jwt/defaults.h" Q_LOGGING_CATEGORY(GC_OAUTH, "gc.oauth", QtInfoMsg) diff --git a/src/jwt/defaults.h b/src/jwt/defaults.h new file mode 100644 index 0000000..03ecacc --- /dev/null +++ b/src/jwt/defaults.h @@ -0,0 +1,86 @@ +#ifndef JWT_CPP_QT_JSON_DEFAULTS_H +#define JWT_CPP_QT_JSON_DEFAULTS_H + +#include "traits.h" + +namespace jwt { +/** + * \brief a class to store a generic Qt JSON value as claim + * * This type is the specialization of the \ref basic_claim class which + * uses the qt_json. + */ +using claim = basic_claim; + +/** + * Create a verifier using the default clock + * \return verifier instance + */ +inline verifier verify() { + return verify(default_clock{}); +} + +/** + * Create a builder using the default clock + * \return builder instance to create a new token + */ +inline builder create() { + return builder(default_clock{}); +} + +#ifndef JWT_DISABLE_BASE64 +/** + * Decode a token + * \param token Token to decode + * \return Decoded token + * \throw std::invalid_argument Token is not in correct format + * \throw std::runtime_error Base64 decoding failed or invalid json + */ +inline decoded_jwt decode(const std::string &token) { + return decoded_jwt(token); +} +#endif + +/** + * Decode a token + * \tparam Decode is callable, taking a QString and returns a QString. + * It should ensure the padding of the input and then base64url decode and + * return the results. + * \param token Token to decode + * \param decode The token to parse + * \return Decoded token + * \throw std::invalid_argument Token is not in correct format + * \throw std::runtime_error Base64 decoding failed or invalid json + */ +template +decoded_jwt decode(const std::string& token, Decode decode) { + return decoded_jwt(token, decode); +} + +/** + * Parse a jwk + * \param token JWK Token to parse + * \return Parsed JWK + * \throw std::runtime_error Token is not in correct format + */ +inline jwk parse_jwk(const traits::qt_json::string_type& token) { + return jwk(token); +} + +/** + * Parse a jwks + * \param token JWKs Token to parse + * \return Parsed JWKs + * \throw std::runtime_error Token is not in correct format + */ +inline jwks parse_jwks(const traits::qt_json::string_type& token) { + return jwks(token); +} + +/** + * This type is the specialization of the \ref verify_ops::verify_context class which + * uses the qt_json. + */ +using verify_context = verify_ops::verify_context; +} // namespace jwt + +#endif // JWT_CPP_QT_JSON_DEFAULTS_H diff --git a/src/jwt/traits.h b/src/jwt/traits.h new file mode 100644 index 0000000..b6ff9a9 --- /dev/null +++ b/src/jwt/traits.h @@ -0,0 +1,196 @@ +#ifndef JWT_CPP_QT_JSON_TRAITS_H +#define JWT_CPP_QT_JSON_TRAITS_H + +#include +#include +#include +#include +#include +#include +#include + +#define JWT_DISABLE_PICOJSON +#include + +namespace jwt { +/** + * \brief Namespace containing all the json_trait implementations for a jwt::basic_claim. + */ +namespace traits { + +struct qt_json { + // Type Specifications + using string_type = std::string; // current limitation of traits implementation + using number_type = double; + using integer_type = qint64; + using boolean_type = bool; + + struct value_type : QJsonValue { + using QJsonValue::QJsonValue; // Inherit constructors from QJsonValue + + value_type(const std::string &value) : QJsonValue{QString::fromStdString(value)} {} + }; + + struct object_type : QJsonObject { + using QJsonObject::QJsonObject; // Inherit constructors from QJsonObject + + // Add missing C++11 member types + // using value_type = key_value_type; // Enable optional jwt-cpp methods + // using mapped_type = key_value_type::value_type; + using size_type = size_t; // for implementing count + + // object_type() = default; + // object_type(const object_type&) = default; + explicit object_type(const QJsonObject& o) : QJsonObject(o) {} + // object_type(object_type&&) = default; + explicit object_type(QJsonObject&& o) : QJsonObject(o) {} + // ~object_type() = default; + // object_type& operator=(const object_type& o) = default; + // object_type& operator=(object_type&& o) noexcept = default; + + // Add missing C++11 subscription operator + QJsonValue operator[](const std::string& key) { + QJsonValueRef ref = QJsonObject::operator[](QString::fromStdString(key)); + return ref; + } + + // Add missing C++11 element access + QJsonValue at(const std::string& key) const { + auto it = constFind(QString::fromStdString(key)); + if (it != constEnd()) { + return it.value(); + } + + throw std::out_of_range("invalid key"); + } + + // Add missing C++11 lookup method + size_type count(const std::string & key) const { + return contains(QString::fromStdString(key)) ? 0 : 1; + } + }; + + struct array_type : QJsonArray { + using QJsonArray::QJsonArray; // Inherit constructors from QJsonArray + + explicit array_type(const QJsonArray& o) : QJsonArray(o) {} + explicit array_type(QJsonArray&& o) : QJsonArray(o) {} + }; + + // Translation between Qt's JSON type and jwt::json::type equivalent + static jwt::json::type get_type(const value_type &val) { + using jwt::json::type; + + switch (val.type()) { + case QJsonValue::Object: + return type::object; + case QJsonValue::Array: + return type::array; + case QJsonValue::String: + return type::string; + case QJsonValue::Double: + return (std::trunc(val.toDouble()) == val.toDouble()) ? type::integer : type::number; + case QJsonValue::Bool: + return type::boolean; + default: + throw std::logic_error("invalid type"); + } + } + + // Define conversions to and from std::string to satisfy the requirement + // static std::string to_std_string(const QString &str) { + // return str.toStdString(); + // } + + // static QString from_std_string(const std::string &str) { + // return QString::fromStdString(str); + // } + + // // Helper function for substring to replace std::string::substr + // static QString substring(const QString &str, integer_type pos, integer_type len) { + // return str.mid(pos, len); // Equivalent to std::string::substr for QString + // } + + // // Helper function to concatenate QStrings + // static QString concat_strings(const QString &lhs, const QString &rhs) { + // return lhs + rhs; + // } + + // Helper function that returns the actual string type to be used in templates + // where `std::string` is required. + // static std::string get_compatible_string(const string_type &qtStr) { + // return to_std_string(qtStr); + // } + + // Conversion from generic value to specific type + static object_type as_object(const value_type &val) { + if (!val.isObject()) + throw std::logic_error("Not an object type"); + return object_type(val.toObject()); + } + + static array_type as_array(const value_type &val) { + if (!val.isArray()) + throw std::logic_error("Not an array type"); + return array_type(val.toArray()); + } + + static string_type as_string(const value_type &val) { + if (!val.isString()) + throw std::logic_error("Not a string type"); + return val.toString().toStdString(); + } + + static number_type as_number(const value_type &val) { + if (!val.isDouble()) + throw std::logic_error("Not a number type"); + return val.toDouble(); + } + + static integer_type as_integer(const value_type &val) { + if (!val.isDouble() || std::trunc(val.toDouble()) != val.toDouble()) + throw std::logic_error("Not an integer type"); + return static_cast(val.toDouble()); + } + + static boolean_type as_boolean(const value_type &val) { + if (!val.isBool()) + throw std::logic_error("Not a boolean type"); + return val.toBool(); + } + + // Serialization and parsing + static bool parse(value_type &val, const string_type &str) { + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(QByteArray::fromStdString(str), &error); + if (error.error != QJsonParseError::NoError) + return false; + + if (doc.isObject()) { + val = value_type(doc.object()); + } else if (doc.isArray()) { + val = value_type(doc.array()); + } else { + return false; + } + return true; + } + + static string_type serialize(const value_type &val) { + QJsonDocument doc; + if (val.isObject()) { + doc = QJsonDocument(val.toObject()); + } else if (val.isArray()) { + doc = QJsonDocument(val.toArray()); + } else { + throw std::logic_error("Only objects and arrays can be serialized"); + } + return doc.toJson(QJsonDocument::Compact).toStdString(); + } +}; + +} + +} + +#endif // JWT_CPP_QT_JSON_TRAITS_H From bec9e84f78f85fb1ca16b00642292d441999f629 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:29:06 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/jwt/defaults.h | 26 ++-- src/jwt/traits.h | 335 +++++++++++++++++++++++---------------------- 2 files changed, 185 insertions(+), 176 deletions(-) diff --git a/src/jwt/defaults.h b/src/jwt/defaults.h index 03ecacc..c4e713d 100644 --- a/src/jwt/defaults.h +++ b/src/jwt/defaults.h @@ -16,7 +16,7 @@ using claim = basic_claim; * \return verifier instance */ inline verifier verify() { - return verify(default_clock{}); + return verify(default_clock{}); } /** @@ -24,7 +24,7 @@ inline verifier verify() { * \return builder instance to create a new token */ inline builder create() { - return builder(default_clock{}); + return builder(default_clock{}); } #ifndef JWT_DISABLE_BASE64 @@ -36,7 +36,7 @@ inline builder create() { * \throw std::runtime_error Base64 decoding failed or invalid json */ inline decoded_jwt decode(const std::string &token) { - return decoded_jwt(token); + return decoded_jwt(token); } #endif @@ -51,9 +51,9 @@ inline decoded_jwt decode(const std::string &token) { * \throw std::invalid_argument Token is not in correct format * \throw std::runtime_error Base64 decoding failed or invalid json */ -template -decoded_jwt decode(const std::string& token, Decode decode) { - return decoded_jwt(token, decode); +template +decoded_jwt decode(const std::string &token, Decode decode) { + return decoded_jwt(token, decode); } /** @@ -62,8 +62,9 @@ decoded_jwt decode(const std::string& token, Decode decode) { * \return Parsed JWK * \throw std::runtime_error Token is not in correct format */ -inline jwk parse_jwk(const traits::qt_json::string_type& token) { - return jwk(token); +inline jwk +parse_jwk(const traits::qt_json::string_type &token) { + return jwk(token); } /** @@ -72,13 +73,14 @@ inline jwk parse_jwk(const traits::qt_json::string_type& token) * \return Parsed JWKs * \throw std::runtime_error Token is not in correct format */ -inline jwks parse_jwks(const traits::qt_json::string_type& token) { - return jwks(token); +inline jwks +parse_jwks(const traits::qt_json::string_type &token) { + return jwks(token); } /** - * This type is the specialization of the \ref verify_ops::verify_context class which - * uses the qt_json. + * This type is the specialization of the \ref verify_ops::verify_context class + * which uses the qt_json. */ using verify_context = verify_ops::verify_context; } // namespace jwt diff --git a/src/jwt/traits.h b/src/jwt/traits.h index b6ff9a9..8ec3226 100644 --- a/src/jwt/traits.h +++ b/src/jwt/traits.h @@ -1,10 +1,10 @@ #ifndef JWT_CPP_QT_JSON_TRAITS_H #define JWT_CPP_QT_JSON_TRAITS_H -#include -#include #include #include +#include +#include #include #include #include @@ -14,183 +14,190 @@ namespace jwt { /** - * \brief Namespace containing all the json_trait implementations for a jwt::basic_claim. + * \brief Namespace containing all the json_trait implementations for a + * jwt::basic_claim. */ namespace traits { struct qt_json { - // Type Specifications - using string_type = std::string; // current limitation of traits implementation - using number_type = double; - using integer_type = qint64; - using boolean_type = bool; - - struct value_type : QJsonValue { - using QJsonValue::QJsonValue; // Inherit constructors from QJsonValue - - value_type(const std::string &value) : QJsonValue{QString::fromStdString(value)} {} - }; - - struct object_type : QJsonObject { - using QJsonObject::QJsonObject; // Inherit constructors from QJsonObject - - // Add missing C++11 member types - // using value_type = key_value_type; // Enable optional jwt-cpp methods - // using mapped_type = key_value_type::value_type; - using size_type = size_t; // for implementing count - - // object_type() = default; - // object_type(const object_type&) = default; - explicit object_type(const QJsonObject& o) : QJsonObject(o) {} - // object_type(object_type&&) = default; - explicit object_type(QJsonObject&& o) : QJsonObject(o) {} - // ~object_type() = default; - // object_type& operator=(const object_type& o) = default; - // object_type& operator=(object_type&& o) noexcept = default; - - // Add missing C++11 subscription operator - QJsonValue operator[](const std::string& key) { - QJsonValueRef ref = QJsonObject::operator[](QString::fromStdString(key)); - return ref; - } - - // Add missing C++11 element access - QJsonValue at(const std::string& key) const { - auto it = constFind(QString::fromStdString(key)); - if (it != constEnd()) { - return it.value(); - } - - throw std::out_of_range("invalid key"); - } - - // Add missing C++11 lookup method - size_type count(const std::string & key) const { - return contains(QString::fromStdString(key)) ? 0 : 1; - } - }; - - struct array_type : QJsonArray { - using QJsonArray::QJsonArray; // Inherit constructors from QJsonArray - - explicit array_type(const QJsonArray& o) : QJsonArray(o) {} - explicit array_type(QJsonArray&& o) : QJsonArray(o) {} - }; - - // Translation between Qt's JSON type and jwt::json::type equivalent - static jwt::json::type get_type(const value_type &val) { - using jwt::json::type; - - switch (val.type()) { - case QJsonValue::Object: - return type::object; - case QJsonValue::Array: - return type::array; - case QJsonValue::String: - return type::string; - case QJsonValue::Double: - return (std::trunc(val.toDouble()) == val.toDouble()) ? type::integer : type::number; - case QJsonValue::Bool: - return type::boolean; - default: - throw std::logic_error("invalid type"); - } - } - - // Define conversions to and from std::string to satisfy the requirement - // static std::string to_std_string(const QString &str) { - // return str.toStdString(); - // } - - // static QString from_std_string(const std::string &str) { - // return QString::fromStdString(str); - // } - - // // Helper function for substring to replace std::string::substr - // static QString substring(const QString &str, integer_type pos, integer_type len) { - // return str.mid(pos, len); // Equivalent to std::string::substr for QString - // } - - // // Helper function to concatenate QStrings - // static QString concat_strings(const QString &lhs, const QString &rhs) { - // return lhs + rhs; - // } - - // Helper function that returns the actual string type to be used in templates - // where `std::string` is required. - // static std::string get_compatible_string(const string_type &qtStr) { - // return to_std_string(qtStr); - // } - - // Conversion from generic value to specific type - static object_type as_object(const value_type &val) { - if (!val.isObject()) - throw std::logic_error("Not an object type"); - return object_type(val.toObject()); + // Type Specifications + using string_type = + std::string; // current limitation of traits implementation + using number_type = double; + using integer_type = qint64; + using boolean_type = bool; + + struct value_type : QJsonValue { + using QJsonValue::QJsonValue; // Inherit constructors from QJsonValue + + value_type(const std::string &value) + : QJsonValue{QString::fromStdString(value)} {} + }; + + struct object_type : QJsonObject { + using QJsonObject::QJsonObject; // Inherit constructors from QJsonObject + + // Add missing C++11 member types + // using value_type = key_value_type; // Enable optional jwt-cpp methods + // using mapped_type = key_value_type::value_type; + using size_type = size_t; // for implementing count + + // object_type() = default; + // object_type(const object_type&) = default; + explicit object_type(const QJsonObject &o) : QJsonObject(o) {} + // object_type(object_type&&) = default; + explicit object_type(QJsonObject &&o) : QJsonObject(o) {} + // ~object_type() = default; + // object_type& operator=(const object_type& o) = default; + // object_type& operator=(object_type&& o) noexcept = default; + + // Add missing C++11 subscription operator + QJsonValue operator[](const std::string &key) { + QJsonValueRef ref = QJsonObject::operator[](QString::fromStdString(key)); + return ref; } - static array_type as_array(const value_type &val) { - if (!val.isArray()) - throw std::logic_error("Not an array type"); - return array_type(val.toArray()); - } - - static string_type as_string(const value_type &val) { - if (!val.isString()) - throw std::logic_error("Not a string type"); - return val.toString().toStdString(); - } + // Add missing C++11 element access + QJsonValue at(const std::string &key) const { + auto it = constFind(QString::fromStdString(key)); + if (it != constEnd()) { + return it.value(); + } - static number_type as_number(const value_type &val) { - if (!val.isDouble()) - throw std::logic_error("Not a number type"); - return val.toDouble(); + throw std::out_of_range("invalid key"); } - static integer_type as_integer(const value_type &val) { - if (!val.isDouble() || std::trunc(val.toDouble()) != val.toDouble()) - throw std::logic_error("Not an integer type"); - return static_cast(val.toDouble()); + // Add missing C++11 lookup method + size_type count(const std::string &key) const { + return contains(QString::fromStdString(key)) ? 0 : 1; } - - static boolean_type as_boolean(const value_type &val) { - if (!val.isBool()) - throw std::logic_error("Not a boolean type"); - return val.toBool(); + }; + + struct array_type : QJsonArray { + using QJsonArray::QJsonArray; // Inherit constructors from QJsonArray + + explicit array_type(const QJsonArray &o) : QJsonArray(o) {} + explicit array_type(QJsonArray &&o) : QJsonArray(o) {} + }; + + // Translation between Qt's JSON type and jwt::json::type equivalent + static jwt::json::type get_type(const value_type &val) { + using jwt::json::type; + + switch (val.type()) { + case QJsonValue::Object: + return type::object; + case QJsonValue::Array: + return type::array; + case QJsonValue::String: + return type::string; + case QJsonValue::Double: + return (std::trunc(val.toDouble()) == val.toDouble()) ? type::integer + : type::number; + case QJsonValue::Bool: + return type::boolean; + default: + throw std::logic_error("invalid type"); } - - // Serialization and parsing - static bool parse(value_type &val, const string_type &str) { - QJsonParseError error; - QJsonDocument doc = QJsonDocument::fromJson(QByteArray::fromStdString(str), &error); - if (error.error != QJsonParseError::NoError) - return false; - - if (doc.isObject()) { - val = value_type(doc.object()); - } else if (doc.isArray()) { - val = value_type(doc.array()); - } else { - return false; - } - return true; + } + + // Define conversions to and from std::string to satisfy the requirement + // static std::string to_std_string(const QString &str) { + // return str.toStdString(); + // } + + // static QString from_std_string(const std::string &str) { + // return QString::fromStdString(str); + // } + + // // Helper function for substring to replace std::string::substr + // static QString substring(const QString &str, integer_type pos, integer_type + // len) { + // return str.mid(pos, len); // Equivalent to std::string::substr for + // QString + // } + + // // Helper function to concatenate QStrings + // static QString concat_strings(const QString &lhs, const QString &rhs) { + // return lhs + rhs; + // } + + // Helper function that returns the actual string type to be used in templates + // where `std::string` is required. + // static std::string get_compatible_string(const string_type &qtStr) { + // return to_std_string(qtStr); + // } + + // Conversion from generic value to specific type + static object_type as_object(const value_type &val) { + if (!val.isObject()) + throw std::logic_error("Not an object type"); + return object_type(val.toObject()); + } + + static array_type as_array(const value_type &val) { + if (!val.isArray()) + throw std::logic_error("Not an array type"); + return array_type(val.toArray()); + } + + static string_type as_string(const value_type &val) { + if (!val.isString()) + throw std::logic_error("Not a string type"); + return val.toString().toStdString(); + } + + static number_type as_number(const value_type &val) { + if (!val.isDouble()) + throw std::logic_error("Not a number type"); + return val.toDouble(); + } + + static integer_type as_integer(const value_type &val) { + if (!val.isDouble() || std::trunc(val.toDouble()) != val.toDouble()) + throw std::logic_error("Not an integer type"); + return static_cast(val.toDouble()); + } + + static boolean_type as_boolean(const value_type &val) { + if (!val.isBool()) + throw std::logic_error("Not a boolean type"); + return val.toBool(); + } + + // Serialization and parsing + static bool parse(value_type &val, const string_type &str) { + QJsonParseError error; + QJsonDocument doc = + QJsonDocument::fromJson(QByteArray::fromStdString(str), &error); + if (error.error != QJsonParseError::NoError) + return false; + + if (doc.isObject()) { + val = value_type(doc.object()); + } else if (doc.isArray()) { + val = value_type(doc.array()); + } else { + return false; } - - static string_type serialize(const value_type &val) { - QJsonDocument doc; - if (val.isObject()) { - doc = QJsonDocument(val.toObject()); - } else if (val.isArray()) { - doc = QJsonDocument(val.toArray()); - } else { - throw std::logic_error("Only objects and arrays can be serialized"); - } - return doc.toJson(QJsonDocument::Compact).toStdString(); + return true; + } + + static string_type serialize(const value_type &val) { + QJsonDocument doc; + if (val.isObject()) { + doc = QJsonDocument(val.toObject()); + } else if (val.isArray()) { + doc = QJsonDocument(val.toArray()); + } else { + throw std::logic_error("Only objects and arrays can be serialized"); } + return doc.toJson(QJsonDocument::Compact).toStdString(); + } }; -} +} // namespace traits -} +} // namespace jwt #endif // JWT_CPP_QT_JSON_TRAITS_H