diff --git a/.cmake-format b/.cmake-format new file mode 100644 index 000000000..e2a32f9be --- /dev/null +++ b/.cmake-format @@ -0,0 +1,208 @@ +# ---------------------------------- +# Options affecting listfile parsing +# ---------------------------------- +with section("parse"): + + # Specify structure for custom cmake functions + additional_commands = { 'foo': { 'flags': ['BAR', 'BAZ'], + 'kwargs': {'DEPENDS': '*', 'HEADERS': '*', 'SOURCES': '*'}}} + +# ----------------------------- +# Options effecting formatting. +# ----------------------------- +with section("format"): + + # How wide to allow formatted cmake files + line_width = 120 + + # How many spaces to tab for indent + tab_size = 2 + + # If an argument group contains more than this many sub-groups (parg or kwarg + # groups) then force it to a vertical layout. + max_subgroups_hwrap = 12 + + # If a positional argument group contains more than this many arguments, then + # force it to a vertical layout. + max_pargs_hwrap = 24 + + # If true, separate flow control names from their parentheses with a space + separate_ctrl_name_with_space = False + + # If true, separate function names from parentheses with a space + separate_fn_name_with_space = False + + # If a statement is wrapped to more than one line, than dangle the closing + # parenthesis on its own line. + dangle_parens = False + + # If the trailing parenthesis must be 'dangled' on its on line, then align it + # to this reference: `prefix`: the start of the statement, `prefix-indent`: + # the start of the statement, plus one indentation level, `child`: align to + # the column of the arguments + dangle_align = 'prefix' + + # If the statement spelling length (including space and parenthesis) is + # smaller than this amount, then force reject nested layouts. + min_prefix_chars = 4 + + # If the statement spelling length (including space and parenthesis) is larger + # than the tab width by more than this amount, then force reject un-nested + # layouts. + max_prefix_chars = 10 + + # If a candidate layout is wrapped horizontally but it exceeds this many + # lines, then reject the layout. + max_lines_hwrap = 12 + + # What style line endings to use in the output. + line_ending = 'unix' + + # Format command names consistently as 'lower' or 'upper' case + command_case = 'lower' + + # Format keywords consistently as 'lower' or 'upper' case + keyword_case = 'upper' + + # A list of command names which should always be wrapped + always_wrap = [] + + # If true, the argument lists which are known to be sortable will be sorted + # lexicographicall + enable_sort = True + + # If true, the parsers may infer whether or not an argument list is sortable + # (without annotation). + autosort = False + + # By default, if cmake-format cannot successfully fit everything into the + # desired linewidth it will apply the last, most agressive attempt that it + # made. If this flag is True, however, cmake-format will print error, exit + # with non-zero status code, and write-out nothing + require_valid_layout = False + + # A dictionary mapping layout nodes to a list of wrap decisions. See the + # documentation for more information. + layout_passes = {} + +# ------------------------------------------------ +# Options affecting comment reflow and formatting. +# ------------------------------------------------ +with section("markup"): + + # What character to use for bulleted lists + bullet_char = '*' + + # What character to use as punctuation after numerals in an enumerated list + enum_char = '.' + + # If comment markup is enabled, don't reflow the first comment block in each + # listfile. Use this to preserve formatting of your copyright/license + # statements. + first_comment_is_literal = False + + # If comment markup is enabled, don't reflow any comment block which matches + # this (regex) pattern. Default is `None` (disabled). + literal_comment_pattern = None + + # Regular expression to match preformat fences in comments + # default=r'^\s*([`~]{3}[`~]*)(.*)$' + fence_pattern = '^\\s*([`~]{3}[`~]*)(.*)$' + + # Regular expression to match rulers in comments + # default=r'^\s*[^\w\s]{3}.*[^\w\s]{3}$' + ruler_pattern = '^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$' + + # If a comment line matches starts with this pattern then it is explicitly a + # trailing comment for the preceeding argument. Default is '#<' + explicit_trailing_pattern = '#<' + + # If a comment line starts with at least this many consecutive hash + # characters, then don't lstrip() them off. This allows for lazy hash rulers + # where the first hash char is not separated by space + hashruler_min_length = 10 + + # If true, then insert a space between the first hash char and remaining hash + # chars in a hash ruler, and normalize its length to fill the column + canonicalize_hashrulers = True + + # enable comment markup parsing and reflow + enable_markup = True + +# ---------------------------- +# Options affecting the linter +# ---------------------------- +with section("lint"): + + # a list of lint codes to disable + disabled_codes = [] + + # regular expression pattern describing valid function names + function_pattern = '[0-9a-z_]+' + + # regular expression pattern describing valid macro names + macro_pattern = '[0-9A-Z_]+' + + # regular expression pattern describing valid names for variables with global + # scope + global_var_pattern = '[0-9A-Z][0-9A-Z_]+' + + # regular expression pattern describing valid names for variables with global + # scope (but internal semantic) + internal_var_pattern = '_[0-9A-Z][0-9A-Z_]+' + + # regular expression pattern describing valid names for variables with local + # scope + local_var_pattern = '[0-9a-z_]+' + + # regular expression pattern describing valid names for privatedirectory + # variables + private_var_pattern = '_[0-9a-z_]+' + + # regular expression pattern describing valid names for publicdirectory + # variables + public_var_pattern = '[0-9A-Z][0-9A-Z_]+' + + # regular expression pattern describing valid names for keywords used in + # functions or macros + keyword_pattern = '[0-9A-Z_]+' + + # In the heuristic for C0201, how many conditionals to match within a loop in + # before considering the loop a parser. + max_conditionals_custom_parser = 2 + + # Require at least this many newlines between statements + min_statement_spacing = 1 + + # Require no more than this many newlines between statements + max_statement_spacing = 1 + max_returns = 6 + max_branches = 12 + max_arguments = 5 + max_localvars = 15 + max_statements = 50 + +# ------------------------------- +# Options effecting file encoding +# ------------------------------- +with section("encode"): + + # If true, emit the unicode byte-order mark (BOM) at the start of the file + emit_byteorder_mark = False + + # Specify the encoding of the input file. Defaults to utf-8 + input_encoding = 'utf-8' + + # Specify the encoding of the output file. Defaults to utf-8. Note that cmake + # only claims to support utf-8 so be careful when using anything else + output_encoding = 'utf-8' + +# ------------------------------------- +# Miscellaneous configurations options. +# ------------------------------------- +with section("misc"): + + # A dictionary containing any per-command configuration overrides. Currently + # only `command_case` is supported. + per_command = {} + diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 000000000..5582d9746 --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,77 @@ +name: CMake Integration CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + paths: + - 'CMakeLists.txt' + - 'include/jwt-cpp/**' + - 'tests/cmake/**' + - '.github/workflows/cmake.yml' + +jobs: + min-req: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: install cmake + run: | + wget https://cmake.org/files/v3.8/cmake-3.8.2.tar.gz + tar -zxf cmake-3.8.2.tar.gz + cd cmake-3.8.2 + ./bootstrap && make -j $(nproc) && sudo make install + + - name: setup + run: | + mkdir build + cd build + cmake .. -DJWT_BUILD_EXAMPLES=OFF + sudo make install + + - name: test + run: | + cd tests/cmake + cmake . -DCMAKE_PREFIX_PATH=/usr/local/cmake -DTEST:STRING="defaults-enabled" + cmake --build . + + no-pico: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: install cmake + uses: lukka/get-cmake@latest + + - name: setup + run: | + mkdir build + cd build + cmake .. -DJWT_DISABLE_PICOJSON=ON -DJWT_BUILD_EXAMPLES=OFF + sudo make install + + - name: test + run: | + cd tests/cmake + cmake . -DCMAKE_PREFIX_PATH=/usr/local/cmake -DTEST:STRING="picojson-is-disabled" + cmake --build . + + no-base64: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: install cmake + uses: lukka/get-cmake@latest + + - name: setup + run: | + mkdir build + cd build + cmake .. -DJWT_DISABLE_BASE64=ON -DJWT_BUILD_EXAMPLES=OFF + sudo make install + + - name: test + run: | + cd tests/cmake + cmake . -DCMAKE_PREFIX_PATH=/usr/local/cmake -DTEST:STRING="base64-is-disabled" + cmake --build . diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 0f3f30b3a..c063fe9ac 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,4 +1,4 @@ -name: C/C++ CI +name: Coverage CI on: push: @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v2 - name: install cmake - uses: lukka/get-cmake@v3.17.2 + uses: lukka/get-cmake@latest - name: configure run: | @@ -27,7 +27,7 @@ jobs: sudo ln -s /usr/src/gtest/libgtest_main.a /usr/lib/libgtest_main.a mkdir build cd build - cmake .. -DBUILD_TESTS=ON -DCOVERAGE=ON -DCMAKE_BUILD_TYPE=Debug + cmake .. -DJWT_BUILD_TESTS=ON -DJWT_ENABLE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug - name: make run: cd build && make jwt-cpp-test coverage diff --git a/.gitignore b/.gitignore index d49487191..4a2feba6b 100644 --- a/.gitignore +++ b/.gitignore @@ -310,4 +310,4 @@ test docs -build/* \ No newline at end of file +build/* diff --git a/.travis.yml b/.travis.yml index d9fab93b9..4068d769e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,6 @@ install: - sudo ln -s /usr/src/gtest/libgtest_main.a /usr/lib/libgtest_main.a script: - - cmake . + - cmake . -DJWT_BUILD_TESTS=ON - make - ./tests/jwt-cpp-test diff --git a/CMakeLists.txt b/CMakeLists.txt index efc49a2f0..32156c3aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,96 +1,69 @@ cmake_minimum_required(VERSION 3.8) -option(BUILD_TESTS "Configure CMake to build tests (or not)" ON) -option(BUILD_EXAMPLES "Configure CMake to build examples (or not)" ON) -option(COVERAGE "Enable code coverage testing" OFF) -option(EXTERNAL_PICOJSON "Use find_package() to locate the picojson header" OFF) -option(DISABLE_JWT_CPP_BASE64 - "Do not include the base64 implementation from this library" OFF) -option(DISABLE_JWT_CPP_PICOJSON - "Do not provide the picojson template specialiaze" OFF) - project(jwt-cpp) -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) +option(JWT_BUILD_EXAMPLES "Configure CMake to build examples (or not)" ON) +option(JWT_BUILD_TESTS "Configure CMake to build tests (or not)" OFF) +option(JWT_ENABLE_COVERAGE "Enable code coverage testing" OFF) + +option(JWT_EXTERNAL_PICOJSON "Use find_package() to locate picojson, provided to integrate with package managers" OFF) +option(JWT_DISABLE_BASE64 "Do not include the base64 implementation from this library" OFF) +option(JWT_DISABLE_PICOJSON "Do not provide the picojson template specialiaze" OFF) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") find_package(OpenSSL 1.0.2 REQUIRED) -if(EXTERNAL_PICOJSON) - find_package(picojson REQUIRED) +if(JWT_EXTERNAL_PICOJSON) + find_package(picojson 1.3.0 REQUIRED) endif() -add_library(jwt-cpp INTERFACE) -add_library(jwt-cpp::jwt-cpp ALIAS jwt-cpp) +set(JWT_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(JWT_HEADER_FILES ${JWT_INCLUDE_PATH}/jwt-cpp/jwt.h) +if(NOT JWT_DISABLE_BASE64) + list(APPEND JWT_HEADER_FILES ${JWT_INCLUDE_PATH}/jwt-cpp/base.h) +endif() +add_library(jwt-cpp INTERFACE) +add_library(jwt-cpp::jwt-cpp ALIAS jwt-cpp) # To match export target_compile_features(jwt-cpp INTERFACE cxx_std_11) - -if(DISABLE_JWT_CPP_BASE64) - target_compile_options(jwt-cpp INTERFACE DISABLE_BASE64) +if(JWT_DISABLE_BASE64) + target_compile_definitions(jwt-cpp INTERFACE JWT_DISABLE_BASE64) endif() - -if(DISABLE_JWT_CPP_PICOJSON) - target_compile_options(jwt-cpp INTERFACE DISABLE_PICOJSON) +if(JWT_DISABLE_PICOJSON) + target_compile_definitions(jwt-cpp INTERFACE JWT_DISABLE_PICOJSON) endif() - -set(JWT_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_include_directories( - jwt-cpp INTERFACE $ - $) - +target_include_directories(jwt-cpp INTERFACE $ + $) target_link_libraries(jwt-cpp INTERFACE OpenSSL::SSL OpenSSL::Crypto) -set(JWT_HEADERS ${JWT_INCLUDE_PATH}/jwt-cpp/base.h) -if(NOT EXTERNAL_PICOJSON AND NOT DISABLE_JWT_CPP_PICOJSON) - set(PICO_HEADER ${JWT_INCLUDE_PATH}/picojson/picojson.h) +if(JWT_EXTERNAL_PICOJSON) + target_link_libraries(jwt-cpp INTERFACE picojson::picojson>) endif() +include(GNUInstallDirs) include(CMakePackageConfigHelpers) -set(INCLUDE_INSTALL_DIR include) - +set(JWT_CMAKE_FILES_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/cmake/jwt-cpp) configure_package_config_file( - ${CMAKE_CURRENT_LIST_DIR}/cmake/jwt-cpp-config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-config.cmake - INSTALL_DESTINATION - ${CMAKE_INSTALL_PREFIX}/jwt-cpp/cmake - PATH_VARS - INCLUDE_INSTALL_DIR - EXTERNAL_PICOJSON) - -write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-config-version.cmake - VERSION 0.5.0-rc.0 - COMPATIBILITY ExactVersion) - -install(FILES "${JWT_INCLUDE_PATH}/jwt-cpp/jwt.h" DESTINATION include/jwt-cpp) - -if(NOT DISABLE_JWT_CPP_BASE64) - install(FILES "${JWT_INCLUDE_PATH}/jwt-cpp/base.h" - DESTINATION include/jwt-cpp) + ${CMAKE_CURRENT_LIST_DIR}/cmake/jwt-cpp-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-config.cmake + INSTALL_DESTINATION ${JWT_CMAKE_FILES_INSTALL_DIR} PATH_VARS JWT_EXTERNAL_PICOJSON) +write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-config-version.cmake VERSION 0.5.0 + COMPATIBILITY ExactVersion) + +install(TARGETS jwt-cpp EXPORT jwt-cpp-targets PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(EXPORT jwt-cpp-targets NAMESPACE jwt-cpp:: FILE jwt-cpp-targets.cmake + DESTINATION ${JWT_CMAKE_FILES_INSTALL_DIR}) +install(FILES ${JWT_HEADER_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/jwt-cpp) +if(NOT JWT_EXTERNAL_PICOJSON AND NOT JWT_DISABLE_PICOJSON) + install(FILES ${JWT_INCLUDE_PATH}/picojson/picojson.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/picojson) endif() +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-config-version.cmake + DESTINATION ${JWT_CMAKE_FILES_INSTALL_DIR}) -if(NOT EXTERNAL_PICOJSON AND NOT DISABLE_JWT_CPP_PICOJSON) - install(FILES ${PICO_HEADER} DESTINATION include/picojson) +if(JWT_BUILD_EXAMPLES) + add_subdirectory(example) endif() -install( - TARGETS jwt-cpp - EXPORT jwt-cpp - DESTINATION jwt-cpp) -install( - EXPORT jwt-cpp - NAMESPACE jwt-cpp:: - DESTINATION jwt-cpp) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-config.cmake - ${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-config-version.cmake - DESTINATION ${CMAKE_INSTALL_PREFIX}/jwt-cpp) - -if(BUILD_TESTS) +if(JWT_BUILD_TESTS) add_subdirectory(tests) endif() - -if(BUILD_EXAMPLES) - add_subdirectory(example) -endif() diff --git a/README.md b/README.md index 6db094327..9a93ee536 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ For the sake of completeness, here is a list of all supported algorithms: * PS256 * PS384 * PS512 -* EdDSA (Ed25519, Ed448), since 0.5.0 +* EdDSA (Ed25519, Ed448) _since 0.5.0_ ## Overview @@ -38,13 +38,13 @@ jwt::basic_claim claim(json::object({{"json", t This allows for complete freedom when picking which libraries you want to use. For more information, [see below](#providing-your-own-json-traits-your-traits) -In order to maintain compatibility, [picojson](https://github.com/kazuho/picojson) is still used to provide a specialized `jwt::claim` along with all helpers. Defining `DISABLE_PICOJSON` will remove this optional dependency. +In order to maintain compatibility, [picojson](https://github.com/kazuho/picojson) is still used to provide a specialized `jwt::claim` along with all helpers. Defining `JWT_DISABLE_PICOJSON` will remove this optional dependency. -As for the base64 requirements of JWTs, this libary provides `base.h` with all the required implentation; However base64 implementations are very common, with varying degrees of performance. When providing your own base64 implementation, you can define `DISABLE_BASE64` to remove the jwt-cpp implementation. +As for the base64 requirements of JWTs, this libary provides `base.h` with all the required implentation; However base64 implementations are very common, with varying degrees of performance. When providing your own base64 implementation, you can define `JWT_DISABLE_BASE64` to remove the jwt-cpp implementation. ### Getting Started -Simple example of decoding a token and printing all claims: +Simple example of decoding a token and printing all claims ([try it out](https://github.com/Thalhammer/jwt-cpp/tree/master/example/print-claims.cpp)): ```cpp #include @@ -71,7 +71,7 @@ verifier.verify(decoded_token); The created verifier is stateless so you can reuse it for different tokens. -Creating a token (and signing) is equally easy. +Creating a token (and signing) is equally as easy. ```cpp auto token = jwt::create() @@ -98,7 +98,7 @@ auto token = jwt::create() There are several key items that need to be provided to a `jwt::basic_claim` in order for it to be interoptable with you JSON library of choice. * type specifications -* conversion from generic "value type" to specific +* conversion from generic "value type" to a specific type * serialization and parsing If ever you are not sure, the traits are heavily checked against static asserts to make sure you provide everything that's required. diff --git a/cmake/jwt-cpp-config.cmake.in b/cmake/jwt-cpp-config.cmake.in index 949f580b2..b47a30386 100644 --- a/cmake/jwt-cpp-config.cmake.in +++ b/cmake/jwt-cpp-config.cmake.in @@ -1,12 +1,12 @@ @PACKAGE_INIT@ -set(USE_EXTERNAL_PICOJSON @EXTERNAL_PICOJSON@) +set(JWT_EXTERNAL_PICOJSON @JWT_EXTERNAL_PICOJSON@) include(CMakeFindDependencyMacro) find_dependency(OpenSSL REQUIRED) -if(USE_EXTERNAL_PICOJSON) +if(JWT_EXTERNAL_PICOJSON) find_dependency(picojson REQUIRED) endif() -include(${CMAKE_INSTALL_PREFIX}/jwt-cpp/jwt-cpp.cmake) +include("${CMAKE_CURRENT_LIST_DIR}/jwt-cpp-targets.cmake") diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 01b779e09..345a84ffe 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -5,6 +5,10 @@ if(NOT TARGET jwt-cpp) find_package(jwt-cpp CONFIG REQUIRED) endif() +if(JWT_DISABLE_PICOJSON) + message(FATAL_ERROR "examples require picojson to be available!") +endif() + add_executable(print-claims print-claims.cpp) target_link_libraries(print-claims jwt-cpp::jwt-cpp) diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h index 660e285e1..41d60d2b6 100644 --- a/include/jwt-cpp/jwt.h +++ b/include/jwt-cpp/jwt.h @@ -1,14 +1,14 @@ #ifndef JWT_CPP_JWT_H #define JWT_CPP_JWT_H -#ifndef DISABLE_PICOJSON +#ifndef JWT_DISABLE_PICOJSON #ifndef PICOJSON_USE_INT64 #define PICOJSON_USE_INT64 #endif #include "picojson/picojson.h" #endif -#ifndef DISABLE_BASE64 +#ifndef JWT_DISABLE_BASE64 #include "base.h" #endif @@ -53,7 +53,7 @@ * * A namespace to contain everything related to handling JSON Web Tokens, JWT for short, * as a part of [RFC7519](https://tools.ietf.org/html/rfc7519), or alternatively for - * JWS (JSON Web Signature)from [RFC7515](https://tools.ietf.org/html/rfc7515) + * JWS (JSON Web Signature) from [RFC7515](https://tools.ietf.org/html/rfc7515) */ namespace jwt { using date = std::chrono::system_clock::time_point; @@ -78,7 +78,7 @@ namespace jwt { using system_error::system_error; }; /** - * \brief Error related to processing of RSA signatures + * \brief Errors related to processing of RSA signatures */ enum class rsa_error { ok = 0, @@ -93,7 +93,7 @@ namespace jwt { no_key_provided }; /** - * \brief Errorcategory for RSA errors + * \brief Error category for RSA errors */ inline std::error_category& rsa_error_category() { class rsa_error_cat : public std::error_category @@ -124,7 +124,7 @@ namespace jwt { return {static_cast(e), rsa_error_category()}; } /** - * \brief Error related to processing of RSA signatures + * \brief Errors related to processing of RSA signatures */ enum class ecdsa_error { ok = 0, @@ -136,7 +136,7 @@ namespace jwt { invalid_key }; /** - * \brief Errorcategory for RSA errors + * \brief Error category for ECDSA errors */ inline std::error_category& ecdsa_error_category() { class ecdsa_error_cat : public std::error_category @@ -177,7 +177,7 @@ namespace jwt { get_key_failed }; /** - * \brief Errorcategory for verification errors + * \brief Error category for verification errors */ inline std::error_category& signature_verification_error_category() { class verification_error_cat : public std::error_category @@ -206,7 +206,7 @@ namespace jwt { } /** - * \brief Error enum for signature generation errors + * \brief Errors related to signature generation errors */ enum class signature_generation_error { ok = 0, @@ -224,7 +224,7 @@ namespace jwt { get_key_failed }; /** - * \brief Errorcategory for signature generation errors + * \brief Error category for signature generation errors */ inline std::error_category& signature_generation_error_category() { class signature_generation_error_cat : public std::error_category @@ -258,6 +258,9 @@ namespace jwt { return {static_cast(e), signature_generation_error_category()}; } + /** + * \brief Errors related to token verification errors + */ enum class token_verification_error { ok = 0, wrong_algorithm = 10, @@ -267,6 +270,9 @@ namespace jwt { token_expired, audience_missmatch }; + /** + * \brief Error category for token verification errors + */ inline std::error_category& token_verification_error_category() { class token_verification_error_cat : public std::error_category { @@ -379,8 +385,7 @@ namespace jwt { ec = error::rsa_error::convert_to_pem_failed; return {}; } - std::string res(ptr, len); - return res; + return {ptr, static_cast(len)}; } /** @@ -388,7 +393,7 @@ namespace jwt { * * \param certstr String containing the certificate encoded as pem * \param pw Password used to decrypt certificate (leave empty if not encrypted) - * \throws rsa_exception if an error occurred + * \throw rsa_exception if an error occurred */ inline std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw = "") { @@ -404,16 +409,21 @@ namespace jwt { * This is useful when using with JWKs as x5c claim is encoded as base64 DER. More info * (here)[https://tools.ietf.org/html/rfc7517#section-4.7] * - * \param cert_base64_der_str String containing the certificate encoded as base64 DER - * \param ec error_code for error_detection (gets cleared if no error occures) + * \tparam Decode is callabled, taking a string_type and returns a string_type. + * It should ensure the padding of the input and then base64 decode and return + * the results. + * + * \param cert_base64_der_str String containing the certificate encoded as base64 DER + * \param decode The function to decode the cert + * \param ec error_code for error_detection (gets cleared if no error occures) */ - inline - std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str, std::error_code& ec) { + template + std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str, Decode decode, std::error_code& ec) { ec.clear(); - const auto decodedStr = base::decode(base::pad(cert_base64_der_str)); + const auto decodedStr = decode(cert_base64_der_str); auto c_str = reinterpret_cast (decodedStr.c_str()); - std::unique_ptr cert(d2i_X509(NULL, & c_str, decodedStr.size()), X509_free); + std::unique_ptr cert(d2i_X509(NULL, &c_str, decodedStr.size()), X509_free); std::unique_ptr certbio(BIO_new(BIO_s_mem()), BIO_free_all); if(!cert || !certbio) { ec = error::rsa_error::create_mem_bio_failed; @@ -433,7 +443,7 @@ namespace jwt { return {}; } - return std::string {ptr, static_cast(len)}; + return {ptr, static_cast(len)}; } /** @@ -442,8 +452,45 @@ namespace jwt { * This is useful when using with JWKs as x5c claim is encoded as base64 DER. More info * (here)[https://tools.ietf.org/html/rfc7517#section-4.7] * - * \param cert_base64_der_str String containing the certificate encoded as base64 DER - * \throws rsa_exception if an error occurred + * \tparam Decode is callabled, taking a string_type and returns a string_type. + * It should ensure the padding of the input and then base64 decode and return + * the results. + * + * \param cert_base64_der_str String containing the certificate encoded as base64 DER + * \param decode The function to decode the cert + * \throw rsa_exception if an error occurred + */ + template + std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str, Decode decode) { + std::error_code ec; + auto res = convert_base64_der_to_pem(cert_base64_der_str, std::move(decode), ec); + error::throw_if_error(ec); + return res; + } +#ifndef JWT_DISABLE_BASE64 + /** + * \brief Convert the certificate provided as base64 DER to PEM. + * + * This is useful when using with JWKs as x5c claim is encoded as base64 DER. More info + * (here)[https://tools.ietf.org/html/rfc7517#section-4.7] + * + * \param cert_base64_der_str String containing the certificate encoded as base64 DER + * \param ec error_code for error_detection (gets cleared if no error occures) + */ + inline + std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str, std::error_code& ec) { + auto decode = [](const std::string& token){return base::decode(base::pad(token));}; + return convert_base64_der_to_pem(cert_base64_der_str, std::move(decode), ec); + } + + /** + * \brief Convert the certificate provided as base64 DER to PEM. + * + * This is useful when using with JWKs as x5c claim is encoded as base64 DER. More info + * (here)[https://tools.ietf.org/html/rfc7517#section-4.7] + * + * \param cert_base64_der_str String containing the certificate encoded as base64 DER + * \throw rsa_exception if an error occurred */ inline std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str) { @@ -452,7 +499,7 @@ namespace jwt { error::throw_if_error(ec); return res; } - +#endif /** * \brief Load a public key from a string. * @@ -501,7 +548,7 @@ namespace jwt { * * \param certstr String containing the certificate or key encoded as pem * \param pw Password used to decrypt certificate or key (leave empty if not encrypted) - * \throws rsa_exception if an error occurred + * \throw rsa_exception if an error occurred */ inline std::shared_ptr load_public_key_from_string(const std::string& key, const std::string& password = "") { @@ -543,7 +590,7 @@ namespace jwt { * * \param key String containing a private key as pem * \param pw Password used to decrypt key (leave empty if not encrypted) - * \throws rsa_exception if an error occurred + * \throw rsa_exception if an error occurred */ inline std::shared_ptr load_private_key_from_string(const std::string& key, const std::string& password = "") { @@ -2065,11 +2112,12 @@ namespace jwt { /// Unmodified signature part in base64 typename json_traits::string_type signature_base64; public: - #ifndef DISABLE_BASE64 + #ifndef JWT_DISABLE_BASE64 /** - * Constructor - * Parses a given token - * Decodes using the jwt::base64url which supports an std::string + * \brief Parses a given token + * + * \note Decodes using the jwt::base64url which supports an std::string + * * \param token The token to parse * \throw std::invalid_argument Token is not in correct format * \throw std::runtime_error Base64 decoding failed or invalid json @@ -2081,13 +2129,13 @@ namespace jwt { {} #endif /** - * Constructor - * Parses a given token + * \brief Parses a given token + * * \tparam Decode is callabled, taking a string_type and returns a string_type. * It should ensure the padding of the input and then base64url decode and * return the results. * \param token The token to parse - * \param decode The token to parse + * \param decode The function to decode the token * \throw std::invalid_argument Token is not in correct format * \throw std::runtime_error Base64 decoding failed or invalid json */ @@ -2313,7 +2361,7 @@ namespace jwt { error::throw_if_error(ec); return res; } - #ifndef DISABLE_BASE64 + #ifndef JWT_DISABLE_BASE64 /** * Sign token and return result * @@ -2358,7 +2406,7 @@ namespace jwt { if(ec) return {}; return token + "." + encode(signature); } - #ifndef DISABLE_BASE64 + #ifndef JWT_DISABLE_BASE64 /** * Sign token and return result * @@ -2673,7 +2721,7 @@ namespace jwt { return decoded_jwt(token); } -#ifndef DISABLE_PICOJSON +#ifndef JWT_DISABLE_PICOJSON struct picojson_traits { using value_type = picojson::value; using object_type = picojson::object; @@ -2763,7 +2811,7 @@ namespace jwt { builder create() { return builder(); } - +#ifndef JWT_DISABLE_BASE64 /** * Decode a token * \param token Token to decode @@ -2775,6 +2823,22 @@ namespace jwt { decoded_jwt decode(const std::string& token) { return decoded_jwt(token); } +#endif + /** + * Decode a token + * \tparam Decode is callabled, taking a string_type and returns a string_type. + * 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); + } #endif } // namespace jwt diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4a565f1b2..e16f68f02 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,8 +1,8 @@ -if(DISABLE_JWT_CPP_BASE64) +if(JWT_DISABLE_BASE64) message(FATAL_ERROR "Tests requires the base64 support to be enabled!") endif() -if(DISABLE_JWT_CPP_PICOJSON) +if(JWT_DISABLE_PICOJSON) message(FATAL_ERROR "Tests requires the picojson support to be enabled!") endif() @@ -12,42 +12,29 @@ include(GoogleTest) find_package(GTest REQUIRED) set(TEST_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/BaseTest.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ClaimTest.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Keys.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/HelperTest.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/TestMain.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/TokenFormatTest.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/TokenTest.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/NlohmannTest.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/BaseTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ClaimTest.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Keys.cpp ${CMAKE_CURRENT_SOURCE_DIR}/HelperTest.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TestMain.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TokenFormatTest.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TokenTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/NlohmannTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/OpenSSLErrorTest.cpp) add_executable(jwt-cpp-test ${TEST_SOURCES}) + +# NOTE: Don't use space inside a generator expression here, because the function prematurely breaks the expression into +# multiple lines. https://cmake.org/pipermail/cmake/2018-April/067422.html +set(JWT_TESTER_GCC_FLAGS -Wall -Wextra -Wpedantic) +set(JWT_TESTER_CLANG_FLAGS -Weverything -Wno-c++98-compat -Wno-global-constructors -Wno-weak-vtables) target_compile_options( - jwt-cpp-test - PRIVATE $<$:/W4> - $<$:-Wall - -Wextra - -Wpedantic> - $<$:-Weverything - -Wno-c++98-compat - -Wno-global-constructors - -Wno-weak-vtables>) - -# NOTE: Don't use space inside a generator expression here, because the function -# prematurely breaks the expression into multiple lines. -target_link_libraries(jwt-cpp-test PRIVATE jwt-cpp::jwt-cpp - GTest::GTest GTest::Main - $<$>:pthread> - $<$>:${CMAKE_DL_LIBS}> ) + jwt-cpp-test PRIVATE $<$:/W4> $<$:${JWT_TESTER_GCC_FLAGS}> + $<$:${JWT_TESTER_CLANG_FLAGS}>) +target_link_libraries(jwt-cpp-test PRIVATE jwt-cpp GTest::GTest GTest::Main + $<$>:${CMAKE_DL_LIBS}>) gtest_add_tests(TARGET jwt-cpp-test) -if(${COVERAGE}) +if(JWT_ENABLE_COVERAGE) include("code-coverage") setup_coverage(jwt-cpp-test) - set(COVERAGE_EXCLUDES "/usr/**" "/home/*/.conan/**" "*test*" "*build*" "*json*") - setup_target_for_coverage_lcov(NAME coverage EXECUTABLE - ${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-test) + setup_target_for_coverage_lcov(NAME coverage EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-test) endif() diff --git a/tests/HelperTest.cpp b/tests/HelperTest.cpp index 913e88089..bc5e4335f 100644 --- a/tests/HelperTest.cpp +++ b/tests/HelperTest.cpp @@ -13,7 +13,7 @@ TEST(HelperTest, Cert2Pubkey) { } TEST(HelperTest, Base64DER2PemCert) { - auto cert_pem = jwt::helper::convert_base64_der_to_pem (google_cert_base64_der); + auto cert_pem = jwt::helper::convert_base64_der_to_pem(google_cert_base64_der); ASSERT_EQ(google_cert, cert_pem); } diff --git a/tests/NlohmannTest.cpp b/tests/NlohmannTest.cpp index 1c7ca0a61..2e20abe5e 100644 --- a/tests/NlohmannTest.cpp +++ b/tests/NlohmannTest.cpp @@ -1,4 +1,4 @@ -#define DISABLE_PICOJSON // Make sure JWT compiles with this flag +#define JWT_DISABLE_PICOJSON // Make sure JWT compiles with this flag #include #include "jwt-cpp/jwt.h" diff --git a/tests/cmake/CMakeLists.txt b/tests/cmake/CMakeLists.txt new file mode 100644 index 000000000..9834acb64 --- /dev/null +++ b/tests/cmake/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.8) +project(jwt-cpp-installation-tests) + +set(TEST CACHE STRING "The test source file to be used") + +find_package(jwt-cpp 0.5.0 EXACT REQUIRED CONFIG) + +add_executable(test-project ${TEST}.cpp) +target_link_libraries(test-project jwt-cpp::jwt-cpp) diff --git a/tests/cmake/base64-is-disabled.cpp b/tests/cmake/base64-is-disabled.cpp new file mode 100644 index 000000000..696bb6656 --- /dev/null +++ b/tests/cmake/base64-is-disabled.cpp @@ -0,0 +1,10 @@ +#ifndef JWT_DISABLE_BASE64 + #error "This test expects 'JWT_DISABLE_BASE64' to be defined!" +#endif + +#include "jwt-cpp/jwt.h" + +int main() { + jwt::date date; + return 0; +} diff --git a/tests/cmake/defaults-enabled.cpp b/tests/cmake/defaults-enabled.cpp new file mode 100644 index 000000000..ce326ceaa --- /dev/null +++ b/tests/cmake/defaults-enabled.cpp @@ -0,0 +1,6 @@ +#include "jwt-cpp/jwt.h" + +int main() { + jwt::claim claim; + return 0; +} diff --git a/tests/cmake/picojson-is-disabled.cpp b/tests/cmake/picojson-is-disabled.cpp new file mode 100644 index 000000000..afe739d91 --- /dev/null +++ b/tests/cmake/picojson-is-disabled.cpp @@ -0,0 +1,10 @@ +#ifndef JWT_DISABLE_PICOJSON + #error "This test expects 'JWT_DISABLE_PICOJSON' to be defined!" +#endif + +#include "jwt-cpp/jwt.h" + +int main() { + jwt::date date; + return 0; +}