diff --git a/include/jwt/detail/meta.hpp b/include/jwt/detail/meta.hpp index 752a66d..76c6f91 100644 --- a/include/jwt/detail/meta.hpp +++ b/include/jwt/detail/meta.hpp @@ -2,11 +2,15 @@ #define CPP_JWT_META_HPP #include +#include "jwt/string_view.hpp" namespace jwt { namespace detail { namespace meta { +/** + * The famous void_t trick. + */ template struct make_void { @@ -16,7 +20,16 @@ struct make_void template using void_t = typename make_void::type; +/** + * A type tag representing an empty tag. + * To be used to represent a `result-not-found` + * situation. + */ +struct empty_type {}; + +/** + */ template struct has_create_json_obj_member: std::false_type { @@ -34,6 +47,91 @@ struct has_create_json_obj_member +struct is_mapping_concept: std::false_type +{ +}; + +template +struct is_mapping_concept::value, + void + >::type, + + typename std::enable_if< + std::is_constructible::value, + void + >::type, + + decltype( + std::declval().operator[](std::declval()), + std::declval().begin(), + std::declval().end(), + (void)0 + ) + + > + >: std::true_type +{ +}; + + +/** + * Checks if the type `T` models the ParameterConcept. + * + * Requirements on type `T` for matching the requirements: + * a. The type must have a `get` method. + */ +template +struct is_parameter_concept: std::false_type +{ +}; + +template +struct is_parameter_concept().get(), + (void)0 + ) + > + >: std::true_type +{ +}; + +/** + */ +template +struct bool_pack {}; + +/** + */ +template +using all_true = std::is_same, bool_pack>; + +/** + */ +template +using are_all_params = all_true{}...>; + } // END namespace meta } // END namespace detail diff --git a/include/jwt/impl/jwt.ipp b/include/jwt/impl/jwt.ipp index 730f8a5..2613b19 100644 --- a/include/jwt/impl/jwt.ipp +++ b/include/jwt/impl/jwt.ipp @@ -197,6 +197,51 @@ jwt_signature::get_verify_algorithm_impl(const jwt_header& hdr) const noexcept } +// +template +jwt_object::jwt_object(Args&&... args) +{ + static_assert (detail::meta::are_all_params::value, + "All constructor argument types must model ParameterConcept"); + + set_parameters(std::forward(args)...); +} + +template +void jwt_object::set_parameters( + params::detail::payload_param&& payload, Rest&&... rargs) +{ + set_parameters(std::forward(rargs)...); +} + +template +void jwt_object::set_parameters( + params::detail::secret_param secret, Rest&&... rargs) +{ + set_parameters(std::forward(rargs)...); +} + +template +void jwt_object::set_parameters( + params::detail::headers_param&& header, Rest&&... rargs) +{ + set_parameters(std::forward(rargs)...); +} + +void jwt_object::set_parameters() +{ + //setinel call + return; +} + +template +jwt_payload& jwt_object::add_payload(const std::string& name, T&& value) +{ + payload_.add_claim(name, std::forward(value)); + return payload_; +} + + //==================================================================== void jwt_decode(const string_view encoded_str, const string_view key, bool validate) diff --git a/include/jwt/jwt.hpp b/include/jwt/jwt.hpp index 0886fd7..cebf2ed 100644 --- a/include/jwt/jwt.hpp +++ b/include/jwt/jwt.hpp @@ -10,6 +10,7 @@ #include "jwt/base64.hpp" #include "jwt/algorithm.hpp" #include "jwt/string_view.hpp" +#include "jwt/parameters.hpp" #include "jwt/json/json.hpp" // For convenience @@ -442,6 +443,7 @@ private: // Private implementation verify_func_t get_verify_algorithm_impl(const jwt_header& hdr) const noexcept; private: // Data members; + /// The key for creating the JWS std::string key_; }; @@ -452,16 +454,84 @@ private: // Data members; class jwt_object { public: // 'tors + /** + */ jwt_object() = default; + /** + */ + template + jwt_object(Args&&... args); + public: // Exposed APIs + /** + */ + jwt_payload& payload() noexcept + { + return payload_; + } + + /** + */ + const jwt_payload& payload() const noexcept + { + return payload_; + } + + /** + */ + jwt_header& header() noexcept + { + return header_; + } + + /** + */ + const jwt_header& header() const noexcept + { + return header_; + } + + /** + */ + template + jwt_payload& add_payload(const std::string& name, T&& value); + +private: // private APIs + /** + */ + template + void set_parameters(Args&&... args); + + /** + */ + template + void set_parameters(params::detail::payload_param&&, Rest&&...); + + /** + */ + template + void set_parameters(params::detail::secret_param, Rest&&...); + + /** + */ + template + void set_parameters(params::detail::headers_param&&, Rest&&...); + + /** + */ + void set_parameters(); private: // Data Members /// JWT header section jwt_header header_; + /// JWT payload section jwt_payload payload_; + + /// The secret key + std::string secret_; }; /*! diff --git a/include/jwt/parameters.hpp b/include/jwt/parameters.hpp new file mode 100644 index 0000000..672bb3e --- /dev/null +++ b/include/jwt/parameters.hpp @@ -0,0 +1,143 @@ +#ifndef CPP_JWT_PARAMETERS_HPP +#define CPP_JWT_PARAMETERS_HPP + +#include +#include +#include + +#include "jwt/detail/meta.hpp" +#include "jwt/string_view.hpp" + +namespace jwt { +namespace params { + + +namespace detail { +/** + * Parameter for providing the payload. + * Takes a Mapping concept representing + * key-value pairs. + * + * NOTE: MappingConcept allows only strings + * for both keys and values. Use `add_header` + * API of `jwt_object` otherwise. + * + * Modeled as ParameterConcept. + */ +template +struct payload_param +{ + payload_param(MappingConcept&& mc) + : payload_(std::forward(mc)) + {} + + MappingConcept get() && { return std::move(payload_); } + const MappingConcept& get() const& { return payload_; } + + MappingConcept payload_; +}; + +/** + * Parameter for providing the secret key. + * Stores only the view of the provided string + * as string_view. Later the implementation may or + * may-not copy it. + * + * Modeled as ParameterConcept. + */ +struct secret_param +{ + secret_param(string_view sv) + : secret_(sv) + {} + + string_view get() { return secret_; } + string_view secret_; +}; + +/** + * Parameter for providing additional headers. + * Takes a mapping concept representing + * key-value pairs. + * + * Modeled as ParameterConcept. + */ +template +struct headers_param +{ + headers_param(MappingConcept&& mc) + : headers_(std::forward(mc)) + {} + + MappingConcept get() && { return std::move(headers_); } + const MappingConcept& get() const& { return headers_; } + + MappingConcept headers_; +}; + +} // END namespace detail + +// Useful typedef +using param_init_list_t = std::initializer_list>; + + +/** + */ +template ::type> +detail::payload_param +payload(MappingConcept&& mc) +{ + return { std::forward(mc) }; +} + +/** + */ +detail::payload_param> +payload(const param_init_list_t& kvs) +{ + std::unordered_map m; + + for (const auto& elem : kvs) { + m.emplace(elem.first.data(), elem.second.data()); + } + + return { std::move(m) }; +} + + +/** + */ +detail::secret_param secret(string_view sv) +{ + return { sv }; +} + +/** + */ +template ::type> +detail::headers_param +headers(MappingConcept&& mc) +{ + return { std::forward(mc) }; +} + +/** + */ + detail::headers_param> +headers(const param_init_list_t& kvs) +{ + std::map m; + + for (const auto& elem : kvs) { + m.emplace(elem.first.data(), elem.second.data()); + } + + return { std::move(m) }; +} + +} // END namespace params +} // END namespace jwt + +#endif diff --git a/include/jwt/test/test_jwt_object b/include/jwt/test/test_jwt_object new file mode 100755 index 0000000..aa50cbf Binary files /dev/null and b/include/jwt/test/test_jwt_object differ diff --git a/include/jwt/test/test_jwt_object.cc b/include/jwt/test/test_jwt_object.cc new file mode 100644 index 0000000..34f4ee0 --- /dev/null +++ b/include/jwt/test/test_jwt_object.cc @@ -0,0 +1,16 @@ +#include +#include "jwt/jwt.hpp" + +void basic_jwt_object_test() +{ + using namespace jwt::params; + jwt::jwt_object obj(payload({ + {"a", "b"}, + {"c", "d"} + })); +} + +int main() { + basic_jwt_object_test(); + return 0; +}