pax_global_header00006660000000000000000000000064151146671770014531gustar00rootroot0000000000000052 comment=24a9196fff89771b5811d45bcb5ff06174e75878 scitokens-cpp-1.2.0/000077500000000000000000000000001511466717700143135ustar00rootroot00000000000000scitokens-cpp-1.2.0/.clang-format000066400000000000000000000000411511466717700166610ustar00rootroot00000000000000BasedOnStyle: LLVM IndentWidth: 4scitokens-cpp-1.2.0/.github/000077500000000000000000000000001511466717700156535ustar00rootroot00000000000000scitokens-cpp-1.2.0/.github/workflows/000077500000000000000000000000001511466717700177105ustar00rootroot00000000000000scitokens-cpp-1.2.0/.github/workflows/build-rpm.yml000066400000000000000000000006631511466717700223330ustar00rootroot00000000000000name: RPM Build on: pull_request: branches: - master push: branches: - master jobs: build-rpm: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 with: submodules: recursive # Custom rpm building from Derek to build RPM - uses: djw8605/docker-mock-rpmbuilder@master with: spec-file: rpm/scitokens-cpp.spec mock-config: epel-7-x86_64 scitokens-cpp-1.2.0/.github/workflows/ccpp.yml000066400000000000000000000043021511466717700213570ustar00rootroot00000000000000name: C/C++ CI on: pull_request: branches: - master push: branches: - master env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release jobs: build: strategy: matrix: external-gtest: [ YES, NO ] os: [ ubuntu-latest, ubuntu-22.04 ] asan: [ YES, NO ] runs-on: ${{ matrix.os }} name: Build with external_gtest=${{ matrix.external-gtest }} on ${{ matrix.os }} and asan=${{ matrix.asan }} steps: - uses: actions/checkout@v1 with: submodules: recursive - name: install deps run: | sudo apt update && sudo apt-get install libssl-dev sqlite3 libsqlite3-dev cmake libcurl4 libcurl4-openssl-dev uuid-dev libgtest-dev - name: Create Build Environment # Some projects don't allow in-source building, so create a separate build directory # We'll use this as our working directory for all subsequent commands run: cmake -E make_directory ${{runner.workspace}}/build - name: Configure CMake # Use a bash shell so we can use the same syntax for environment variable # access regardless of the host operating system shell: bash working-directory: ${{runner.workspace}}/build # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSCITOKENS_BUILD_UNITTESTS=ON -DSCITOKENS_EXTERNAL_GTEST=${{ matrix.external-gtest }} -DSCITOKENS_WITH_ASAN=${{ matrix.asan }} - name: Build working-directory: ${{runner.workspace}}/build shell: bash # Execute the build. You can specify a specific target with "--target " run: cmake --build . --config $BUILD_TYPE - name: Test working-directory: ${{runner.workspace}}/build shell: bash # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C $BUILD_TYPE --verbose scitokens-cpp-1.2.0/.github/workflows/linter.yml000066400000000000000000000046131511466717700217340ustar00rootroot00000000000000name: Lint # Linter Action documentation at https://github.com/marketplace/actions/lint-action # One thing to note is that this action is currently configured automatically fix and re-push the linted code to the repo on a pull request. # Because the github token used for authenticating this commit comes from the upstream repo (ie scitokens/scitokens-cpp), those linter changes will not be pushed # to the fork that is providing the pull request. A manual git fetch will have to be run by the fork after the PR is merged to update the fork to the linted code. # The linter does not have authorization to lint any code in the repo's .github/workflows/ directory. # If the linter fails, the PR can still be completed, but none of the linter changes will be made. on: # push: # Can specify more circumstances under which to run the linter. # branches: # Not specifying input to "branches:" causes the action to run on push for all branches. push: branches: - master # Trigger the workflow on pull request, # but only for master pull_request_target: branches: - master permissions: checks: write contents: read pull-requests: write jobs: run-linters: name: Run linters runs-on: ubuntu-latest steps: - name: Check out repository (push) if: ${{ github.event_name == 'push' }} uses: actions/checkout@v3 - name: Check out repository (pull_request_target) if: ${{ github.event_name == 'pull_request_target' }} uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} - name: Install ClangFormat run: sudo apt-get install -y clang-format - name: Run linters uses: wearerequired/lint-action@v2 with: github_token: ${{ secrets.github_token }} # For providing the commit authorization for the auto_fix feature clang_format: true clang_format_auto_fix: true auto_fix: true commit: false continue_on_error: false git_email: github.event.commits[0].author.name # Uses the author's git email instead of the default git email associated with the action ("lint-action@samuelmeuli.com") clang_format_args: -style=file # Any additional arguments for clang_format - name: suggester / lint uses: reviewdog/action-suggester@v1 with: tool_name: lint scitokens-cpp-1.2.0/.gitignore000066400000000000000000000000061511466717700162770ustar00rootroot00000000000000build scitokens-cpp-1.2.0/.gitmodules000066400000000000000000000003271511466717700164720ustar00rootroot00000000000000[submodule "vendor/jwt-cpp"] path = vendor/jwt-cpp url = https://github.com/Thalhammer/jwt-cpp.git [submodule "vendor/gtest"] path = vendor/gtest url = https://github.com/google/googletest.git branch = v1.8.x scitokens-cpp-1.2.0/CMakeLists.txt000066400000000000000000000065261511466717700170640ustar00rootroot00000000000000 cmake_minimum_required( VERSION 3.10) project( scitokens-cpp DESCRIPTION "A C++ Library to interface to scitokens" VERSION 1.0.2 LANGUAGES CXX) option( SCITOKENS_BUILD_UNITTESTS "Build the scitokens-cpp unit tests" OFF ) option( SCITOKENS_EXTERNAL_GTEST "Use an external/pre-installed copy of GTest" OFF ) option( SCITOKENS_WITH_ASAN "Build with the address sanitizer" OFF ) set( CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}" ) set( CMAKE_BUILD_TYPE RelWithDebInfo) # -g -O2 set( CMAKE_UNITY_BUILD False) if( SCITOKENS_WITH_ASAN ) add_compile_options(-fsanitize=address -fno-omit-frame-pointer) add_link_options(-fsanitize=address) endif() include(GNUInstallDirs) find_package( jwt-cpp REQUIRED ) find_package( CURL REQUIRED ) find_package( UUID REQUIRED ) if( APPLE ) find_package( OpenSSL REQUIRED ) find_package( Sqlite3 REQUIRED ) set(LIBCRYPTO_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR}) set(LIBCRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) set(CMAKE_MACOSX_RPATH ON) elseif( UNIX ) include (FindPkgConfig) pkg_check_modules(LIBCRYPTO REQUIRED libcrypto) pkg_check_modules(OPENSSL REQUIRED openssl) pkg_check_modules(SQLITE REQUIRED sqlite3) endif() add_library(SciTokens SHARED src/scitokens.cpp src/scitokens_internal.cpp src/scitokens_cache.cpp) target_compile_features(SciTokens PUBLIC cxx_std_11) # Use at least C++11 for building and when linking to scitokens target_include_directories(SciTokens PUBLIC ${JWT_CPP_INCLUDES} "${PROJECT_SOURCE_DIR}/src" PRIVATE ${CURL_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} ${LIBCRYPTO_INCLUDE_DIRS} ${SQLITE_INCLUDE_DIRS} ${UUID_INCLUDE_DIRS}) target_link_libraries(SciTokens PUBLIC ${OPENSSL_LIBRARIES} ${LIBCRYPTO_LIBRARIES} ${CURL_LIBRARIES} ${SQLITE_LIBRARIES} ${UUID_LIBRARIES}) if (UNIX) # pkg_check_modules fails to return an absolute path on RHEL7. Set the # link directories accordingly. target_link_directories(SciTokens PUBLIC ${OPENSSL_LIBRARY_DIRS} ${LIBCRYPTO_LIBRARY_DIRS}) endif() if ( NOT APPLE AND UNIX ) set_target_properties(SciTokens PROPERTIES LINK_FLAGS "-Wl,--version-script=${PROJECT_SOURCE_DIR}/configs/export-symbols") endif() add_executable(scitokens-test src/test.cpp) target_include_directories(scitokens-test PRIVATE "${PROJECT_SOURCE_DIR}" ${JWT_CPP_INCLUDES} ${LIBCRYPTO_INCLUDE_DIRS}) target_link_libraries(scitokens-test SciTokens) add_executable(scitokens-verify src/verify.cpp) target_link_libraries(scitokens-verify SciTokens) add_executable(scitokens-test-access src/test_access.cpp) target_link_libraries(scitokens-test-access SciTokens) add_executable(scitokens-list-access src/list_access.cpp) target_link_libraries(scitokens-list-access SciTokens) add_executable(scitokens-create src/create.cpp) target_link_libraries(scitokens-create SciTokens) get_directory_property(TARGETS BUILDSYSTEM_TARGETS) install( TARGETS ${TARGETS} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) install( FILES src/scitokens.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/scitokens ) set_target_properties( SciTokens PROPERTIES VERSION "0.0.2" SOVERSION "0" ) if( SCITOKENS_BUILD_UNITTESTS ) if( NOT SCITOKENS_EXTERNAL_GTEST ) include(ExternalProject) ExternalProject_Add(gtest PREFIX external/gtest URL ${CMAKE_CURRENT_SOURCE_DIR}/vendor/gtest INSTALL_COMMAND : ) endif() enable_testing() add_subdirectory(test) endif() scitokens-cpp-1.2.0/LICENSE000066400000000000000000000261351511466717700153270ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} 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. scitokens-cpp-1.2.0/README.md000066400000000000000000000047471511466717700156060ustar00rootroot00000000000000 SciTokens C++ Library ===================== This repository implements a minimal library for creating and using SciTokens from C or C++. [SciTokens](https://scitokens.org) provide a token format for distributed authorization. The tokens are self-describing, can be verified in a distributed fashion (no need to contact the issuer to determine if the token is valid). This is convenient for a federated environment where several otherwise-independent storage endpoints want to delegate trust for an issuer for managing a storage allocation. Building -------- To build the `scitokens-cpp` library, the following dependencies are needed: - [jwt-cpp] v0.5.0 or later (https://github.com/Thalhammer/jwt-cpp): A header-only C++ library for manipulating JWTs. - OpenSSL 1.0 or later. - `sqlite3` CMake is used for the build system. To build, from the source directory: ``` mkdir build cd build JWT_CPP_DIR=~/path/to/jwt-cpp cmake .. make ``` Testing ------- The easiest way to test `scitokens-cpp` is to head to the [SciTokens Demo app](https://demo.scitokens.org) and copy the generated token. Then, from the build directory: ``` echo "" | ./scitokens-verify ``` Replace the given token above with the fresh one you just generated; using the above token should give an expired token error. The token must be provided via standard input (stdin). Instructions for Generating a Release ------------------------------------- SciTokens-cpp includes a submodule, jwt-cpp. Therefore, to create a release, you have to include the submodule into the release. VER=0.3.3 # for example git archive --prefix "scitokens-cpp-$VER/" -o "scitokens-cpp-$VER.tar" v$VER git submodule update --init git submodule foreach --recursive "git archive --prefix=scitokens-cpp-$VER/\$path/ --output=\$sha1.tar HEAD && tar --concatenate --file=$(pwd)/scitokens-cpp-$VER.tar \$sha1.tar && rm \$sha1.tar" gzip "scitokens-cpp-$VER.tar" Before tagging a new release, make sure that the RPM spec file has an updated version number and an associated changelog entry. Also, make sure that the ``debian/changelog`` has an entry that matches the RPM changelog entry. This package is built on the [cvmfs-config OpenSUSE Build Service](https://build.opensuse.org/project/show/home:cvmfs:contrib). In order to support that run `debian/obsupdate.sh` whenever the version or release number is changed in `rpm/scitokens-cpp.spec`, and commit the generated `debian/scitokens-cpp.dsc` before tagging the release. scitokens-cpp-1.2.0/cmake/000077500000000000000000000000001511466717700153735ustar00rootroot00000000000000scitokens-cpp-1.2.0/cmake/FindSqlite3.cmake000066400000000000000000000022441511466717700205240ustar00rootroot00000000000000# - Try to find SQLITE3 # # SQLITE3_FOUND - system has SQLITE3 # SQLITE3_INCLUDE_DIRS - the SQLITE3 include directory # SQLITE3_LIBRARIES - Link these to use SQLITE3 # include(CheckSymbolExists) FIND_LIBRARY(SQLITE3_LIBRARY NAMES sqlite) find_path(SQLITE3_INCLUDE_DIR NAMES sqlite3.h PATHS ${SQLITE3_DIR} $ENV{SQLITE3_DIR} ~/Library/Frameworks /Library/Frameworks /usr/local/include /usr/include ) find_library(SQLITE3_LIBRARY NAMES sqlite3 PATHS ${SQLITE_DIR} $ENV{SQLITE_DIR} ~/Library/Frameworks /Library/Frameworks ) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(SQLITE3 DEFAULT_MSG SQLITE3_LIBRARY SQLITE3_INCLUDE_DIR) IF(SQLITE3_FOUND) SET(SQLITE3_LIBRARIES ${SQLITE3_LIBRARY}) SET(SQLITE_LIBRARIES ${SQLITE3_LIBRARY}) SET(SQLITE3_INCLUDE_DIRS ${SQLITE3_INCLUDE_DIR}) SET(SQLITE_INCLUDE_DIRS ${SQLITE3_INCLUDE_DIR}) ELSE() SET(SQLITE_LIBRARIES) SET(SQLITE3_LIBRARIES) SET(SQLITE_INCLUDE_DIRS) SET(SQLITE3_INCLUDE_DIRS) endif() mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES SQLITE_LIBRARIES SQLITE_INCLUDE_DIRS) scitokens-cpp-1.2.0/cmake/FindUUID.cmake000066400000000000000000000056301511466717700177500ustar00rootroot00000000000000# - Try to find UUID # Once done this will define # # UUID_FOUND - system has UUID # UUID_INCLUDE_DIRS - the UUID include directory # UUID_LIBRARIES - Link these to use UUID # UUID_DEFINITIONS - Compiler switches required for using UUID # # Copyright (c) 2006 Andreas Schneider # # Redistribution and use is allowed according to the terms of the New # BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # if (APPLE) include(CheckSymbolExists) # On mac, it can't find uuid library # So, just check for the functions with the default include CHECK_SYMBOL_EXISTS("uuid_generate" "uuid/uuid.h" UUID_SYMBOL) endif (APPLE) if (UUID_SYMBOL) set(UUID_FOUND TRUE) elseif (UUID_LIBRARIES AND UUID_INCLUDE_DIRS) # in cache already set(UUID_FOUND TRUE) else (UUID_LIBRARIES AND UUID_INCLUDE_DIRS) find_path(UUID_INCLUDE_DIR NAMES uuid/uuid.h PATHS ${UUID_DIR}/include $ENV{UUID_DIR}/include $ENV{UUID_DIR} ${DELTA3D_EXT_DIR}/inc $ENV{DELTA_ROOT}/ext/inc $ENV{DELTA_ROOT} ~/Library/Frameworks /Library/Frameworks /usr/local/include /usr/include /usr/include/gdal /sw/include # Fink /opt/local/include # DarwinPorts /opt/csw/include # Blastwave /opt/include [HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session\ Manager\\Environment;OSG_ROOT]/include /usr/freeware/include ) find_library(UUID_LIBRARY NAMES uuid PATHS ${UUID_DIR}/lib $ENV{UUID_DIR}/lib $ENV{UUID_DIR} ${DELTA3D_EXT_DIR}/lib $ENV{DELTA_ROOT}/ext/lib $ENV{DELTA_ROOT} $ENV{OSG_ROOT}/lib ~/Library/Frameworks /Library/Frameworks /usr/local/lib /usr/lib /sw/lib /opt/local/lib /opt/csw/lib /opt/lib /usr/freeware/lib64 ) find_library(UUID_LIBRARY_DEBUG NAMES uuidd PATHS ${UUID_DIR}/lib $ENV{UUID_DIR}/lib $ENV{UUID_DIR} ${DELTA3D_EXT_DIR}/lib $ENV{DELTA_ROOT}/ext/lib $ENV{DELTA_ROOT} $ENV{OSG_ROOT}/lib ~/Library/Frameworks /Library/Frameworks /usr/local/lib /usr/lib /sw/lib /opt/local/lib /opt/csw/lib /opt/lib /usr/freeware/lib64 ) set(UUID_INCLUDE_DIRS ${UUID_INCLUDE_DIR}) set(UUID_LIBRARIES ${UUID_LIBRARY}) if (UUID_INCLUDE_DIRS AND UUID_LIBRARIES) set(UUID_FOUND TRUE) endif (UUID_INCLUDE_DIRS AND UUID_LIBRARIES) if (UUID_FOUND) if (NOT UUID_FIND_QUIETLY) message(STATUS "Found UUID : ${UUID_LIBRARIES}") endif (NOT UUID_FIND_QUIETLY) else (UUID_FOUND) if (UUID_FIND_REQUIRED) message(FATAL_ERROR "Could not find UUID") endif (UUID_FIND_REQUIRED) endif (UUID_FOUND) # show the UUID_INCLUDE_DIRS and UUID_LIBRARIES variables only in the advanced view mark_as_advanced(UUID_INCLUDE_DIRS UUID_LIBRARIES) endif (UUID_SYMBOL) scitokens-cpp-1.2.0/cmake/Findjwt-cpp.cmake000066400000000000000000000002371511466717700205640ustar00rootroot00000000000000 FIND_PATH(JWT_CPP_INCLUDES jwt-cpp/jwt.h HINTS ${JWT_CPP_DIR} $ENV{JWT_CPP_DIR} /usr ${PROJECT_SOURCE_DIR}/vendor/jwt-cpp PATH_SUFFIXES include ) scitokens-cpp-1.2.0/configs/000077500000000000000000000000001511466717700157435ustar00rootroot00000000000000scitokens-cpp-1.2.0/configs/export-symbols000066400000000000000000000001341511466717700206730ustar00rootroot00000000000000{ global: scitoken*; validator*; enforcer*; keycache*; config*; local: *; }; scitokens-cpp-1.2.0/debian/000077500000000000000000000000001511466717700155355ustar00rootroot00000000000000scitokens-cpp-1.2.0/debian/README.obs000066400000000000000000000005001511466717700171720ustar00rootroot00000000000000This package is built by cvmfs-contrib on the OpenSUSE Build System https://build.opensuse.org/project/show/home:cvmfs:contrib OBS requires a little help from the package in order to build on Debian. Each time there's a new release, run obsupdate.sh in this directory and commit the newly updated scitokens-cpp.dsc. scitokens-cpp-1.2.0/debian/changelog000066400000000000000000000117141511466717700174130ustar00rootroot00000000000000scitokens-cpp (1.2.0-1) unstable; urgency=medium * Fix segfault if the JSON parser cannot parse the JWKS * Fix float time claims issue and improve error handling * Fix security issue with malicious issuer handling in error messages * Improve JWTVerificationException message to include the invalid issuer * Update usage on verify command to make the TOKENFILE explicit * Read token for scitokens-verify from stdin * Set CURLOPT_NOSIGNAL option in SimpleCurlGet to prevent signal interruptions * Add asan value to the job name * Turn off building unit tests by default * Add cmake option SCITOKENS_WITH_ASAN which enables memory checking with the address sanitizer. Also enable this in CI so tests fail if they hit a memory leak or other memory problem * Fix memory leak in store_public_ec_key * Fix memory leaks in the unit tests * Fix memory leak in rs256_from_coords * Fix memory leak in scitokens_verify -- Derek Weitzel Fri, 05 Dec 2025 00:00:00 +0000 scitokens-cpp (1.1.3-1) stable; urgency=medium * Include cstdint import for jwt library to support newer compilers -- Derek Weitzel Mon, 24 Feb 2025 12:00:00 -0600 scitokens-cpp (1.1.2-1) stable; urgency=medium * Turn off CMAKE unity builds * Add a mutex around requesting public keys to stop overloading issuers -- Derek Weitzel Wed, 30 Oct 2024 12:00:00 -0600 scitokens-cpp (1.1.1-1) stable; urgency=medium * Improve error handling around the sqlite3 library * Fix test failures and compiler warnings -- Derek Weitzel Wed, 28 Feb 2024 12:00:00 -0600 scitokens-cpp (1.1.0-1) stable; urgency=medium * Allow the scitokens library user to setup a custom CA file * Fix typecast errors in scitoken_status_get_*() that caused async queries to fail * Fix logic error in deserialize_continue() that caused async deserialization to fail -- Tim Theisen Tue, 07 Nov 2023 15:46:00 -0600 scitokens-cpp (1.0.2-1) stable; urgency=medium * Add support for API-configurable cache home * Fix enforcer_acl_free logic * scitokens_internal: catch matching exception type after jwt-cpp update -- Derek Weitzel Thu, 15 Jun 2023 12:00:00 -0500 scitokens-cpp (1.0.1-1) stable; urgency=medium * Fix bug in generate acls which would cause a timeout -- Derek Weitzel Wed, 26 Apr 2023 12:00:00 -0500 scitokens-cpp (1.0.0-1) stable; urgency=medium * Add async API for parsing and verifying tokens * Add configuration API * Make nbf claim optional for non-scitokens tokens * Update to OpenSSL 3.0 -- Derek Weitzel Tue, 21 Mar 2023 10:18:59 -0500 scitokens-cpp (0.7.3-1) stable; urgency=medium * Retry failed key renewal every 5 minutes -- Derek Weitzel Tue, 01 Nov 2022 08:29:22 -0500 scitokens-cpp (0.7.2-1) stable; urgency=medium * Add curl timeout of 4 seconds for update, and 30 for expired keys -- Derek Weitzel Mon, 31 Oct 2022 15:35:17 -0500 scitokens-cpp (0.7.1-1) stable; urgency=medium * Add scitokens-* binaries to the package * Bug: close sqlite db handle on return -- Derek Weitzel Wed, 22 Jun 2022 11:26:18 -0500 scitokens-cpp (0.7.0-1) stable; urgency=medium * Changes from static analysis * If only one key is available, do not error on no kid * Support at+jwt profile -- Derek Weitzel Fri, 18 Feb 2022 13:16:18 -0600 scitokens-cpp (0.6.3-1) stable; urgency=medium * Add support for building Debian packages on the OpenSUSE Build System * Add patch to jwt-cpp to update its picojson dependency in order to enable it to compile on Debian 11 and Ubuntu 21.04 * Fix el7 build by requiring epel-rpm-macros -- Dave Dykstra Fri, 03 Sep 2021 12:00:00 -0500 scitokens-cpp (0.6.2-2) stable; urgency=medium * Make the build require cmake3 instead of cmake -- Dave Dykstra Thu, 26 Aug 2021 12:00:00 -0500 scitokens-cpp (0.6.2-1) stable; urgency=medium * Correct WLCG compat for condor read permissions -- Dave Dykstra Thu, 26 Aug 2021 12:00:00 -0500 scitokens-cpp (0.6.1-1) stable; urgency=medium * Fix vector resize for el8+ builds -- Derek Weitzel Thu, 20 May 2021 12:00:00 -0500 scitokens-cpp (0.6.0-2) unstable; urgency=medium * Fix empty libscitokens0 package -- Tim Theisen Fri, 26 Mar 2021 08:11:00 -0500 scitokens-cpp (0.6.0-1) unstable; urgency=medium * Fix compilation errors on c++11 * Update to jwt-cpp-0.4.0 vendor * Change scitoken profile name to match spec, scitoken:2.0 -- Derek Weitzel Tue, 09 Mar 2021 13:45:00 -0600 scitokens-cpp (0.5.1-3) unstable; urgency=low * Updated packaging for Debian -- Tim Theisen Sun, 28 Feb 2021 16:02:24 -0600 scitokens-cpp (0.5.1-1) unstable; urgency=low * Initial release. -- Tim Theisen Fri, 04 Dec 2020 10:54:24 -0600 scitokens-cpp-1.2.0/debian/compat000066400000000000000000000000031511466717700167340ustar00rootroot0000000000000012 scitokens-cpp-1.2.0/debian/control000066400000000000000000000027671511466717700171540ustar00rootroot00000000000000Source: scitokens-cpp Section: science Priority: optional Maintainer: Tim Theisen Build-Depends: cmake (>=2.6), debhelper (>=9), libcurl4-openssl-dev | libcurl4-gnutls-dev, libsqlite3-dev, libssl-dev, pkg-config, uuid-dev Standards-Version: 3.9.8 Homepage: https://github.com/scitokens/scitokens-cpp Package: libscitokens0 Section: libs Architecture: any Multi-Arch: same Pre-Depends: ${misc:Pre-Depends} Depends: ${misc:Depends}, ${shlibs:Depends} Description: C++ Implementation of the SciTokens Library SciTokens provide a token format for distributed authorization The tokens are self-describing, can be verified in a distributed fashion (no need to contact the issuer to determine if the token is valid). This is convenient for a federated environment where several otherwise-independent storage endpoints want to delegate trust for an issuer for managing a storage allocation. Package: libscitokens-dev Section: libdevel Architecture: any Multi-Arch: same Depends: libscitokens0 (= ${binary:Version}), ${misc:Depends} Description: Header files for the libscitokens public interfaces SciTokens provide a token format for distributed authorization. The tokens are self-describing, can be verified in a distributed fashion (no need to contact the issuer to determine if the token is valid). This is convenient for a federated environment where several otherwise-independent storage endpoints want to delegate trust for an issuer for managing a storage allocation. scitokens-cpp-1.2.0/debian/copyright000066400000000000000000000422151511466717700174740ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: scitokens-cpp Upstream-Contact: discuss@scitokens.org Source: https://github.com/scitokens/scitokens-cpp Files: * Copyright: NONE License: Apache-2.0 Files: ./vendor/gtest/BUILD.bazel ./vendor/gtest/LICENSE ./vendor/gtest/ci/build-linux-bazel.sh ./vendor/gtest/ci/env-linux.sh ./vendor/gtest/ci/env-osx.sh ./vendor/gtest/ci/get-nprocessors.sh ./vendor/gtest/ci/install-linux.sh ./vendor/gtest/ci/install-osx.sh ./vendor/gtest/ci/log-config.sh ./vendor/gtest/googlemock/LICENSE ./vendor/gtest/googlemock/include/gmock/gmock-actions.h ./vendor/gtest/googlemock/include/gmock/gmock-cardinalities.h ./vendor/gtest/googlemock/include/gmock/gmock-function-mocker.h ./vendor/gtest/googlemock/include/gmock/gmock-generated-actions.h ./vendor/gtest/googlemock/include/gmock/gmock-generated-actions.h.pump ./vendor/gtest/googlemock/include/gmock/gmock-generated-function-mockers.h ./vendor/gtest/googlemock/include/gmock/gmock-generated-function-mockers.h.pump ./vendor/gtest/googlemock/include/gmock/gmock-generated-matchers.h ./vendor/gtest/googlemock/include/gmock/gmock-generated-matchers.h.pump ./vendor/gtest/googlemock/include/gmock/gmock-matchers.h ./vendor/gtest/googlemock/include/gmock/gmock-more-actions.h ./vendor/gtest/googlemock/include/gmock/gmock-more-matchers.h ./vendor/gtest/googlemock/include/gmock/gmock-nice-strict.h ./vendor/gtest/googlemock/include/gmock/gmock-spec-builders.h ./vendor/gtest/googlemock/include/gmock/gmock.h ./vendor/gtest/googlemock/include/gmock/internal/custom/gmock-matchers.h ./vendor/gtest/googlemock/include/gmock/internal/custom/gmock-port.h ./vendor/gtest/googlemock/include/gmock/internal/gmock-internal-utils.h ./vendor/gtest/googlemock/include/gmock/internal/gmock-port.h ./vendor/gtest/googlemock/scripts/fuse_gmock_files.py ./vendor/gtest/googlemock/scripts/pump.py ./vendor/gtest/googlemock/src/gmock-all.cc ./vendor/gtest/googlemock/src/gmock-cardinalities.cc ./vendor/gtest/googlemock/src/gmock-internal-utils.cc ./vendor/gtest/googlemock/src/gmock-matchers.cc ./vendor/gtest/googlemock/src/gmock-spec-builders.cc ./vendor/gtest/googlemock/src/gmock.cc ./vendor/gtest/googlemock/src/gmock_main.cc ./vendor/gtest/googlemock/test/BUILD.bazel ./vendor/gtest/googlemock/test/gmock-actions_test.cc ./vendor/gtest/googlemock/test/gmock-cardinalities_test.cc ./vendor/gtest/googlemock/test/gmock-function-mocker_test.cc ./vendor/gtest/googlemock/test/gmock-generated-actions_test.cc ./vendor/gtest/googlemock/test/gmock-generated-function-mockers_test.cc ./vendor/gtest/googlemock/test/gmock-generated-matchers_test.cc ./vendor/gtest/googlemock/test/gmock-internal-utils_test.cc ./vendor/gtest/googlemock/test/gmock-matchers_test.cc ./vendor/gtest/googlemock/test/gmock-more-actions_test.cc ./vendor/gtest/googlemock/test/gmock-nice-strict_test.cc ./vendor/gtest/googlemock/test/gmock-port_test.cc ./vendor/gtest/googlemock/test/gmock-pp-string_test.cc ./vendor/gtest/googlemock/test/gmock-spec-builders_test.cc ./vendor/gtest/googlemock/test/gmock_all_test.cc ./vendor/gtest/googlemock/test/gmock_ex_test.cc ./vendor/gtest/googlemock/test/gmock_leak_test.py ./vendor/gtest/googlemock/test/gmock_leak_test_.cc ./vendor/gtest/googlemock/test/gmock_link2_test.cc ./vendor/gtest/googlemock/test/gmock_link_test.cc ./vendor/gtest/googlemock/test/gmock_link_test.h ./vendor/gtest/googlemock/test/gmock_output_test.py ./vendor/gtest/googlemock/test/gmock_output_test_.cc ./vendor/gtest/googlemock/test/gmock_stress_test.cc ./vendor/gtest/googlemock/test/gmock_test.cc ./vendor/gtest/googlemock/test/gmock_test_utils.py ./vendor/gtest/googlemock/test/pump_test.py ./vendor/gtest/googletest/LICENSE ./vendor/gtest/googletest/include/gtest/gtest-death-test.h ./vendor/gtest/googletest/include/gtest/gtest-matchers.h ./vendor/gtest/googletest/include/gtest/gtest-message.h ./vendor/gtest/googletest/include/gtest/gtest-param-test.h ./vendor/gtest/googletest/include/gtest/gtest-printers.h ./vendor/gtest/googletest/include/gtest/gtest-spi.h ./vendor/gtest/googletest/include/gtest/gtest-test-part.h ./vendor/gtest/googletest/include/gtest/gtest-typed-test.h ./vendor/gtest/googletest/include/gtest/gtest.h ./vendor/gtest/googletest/include/gtest/gtest_pred_impl.h ./vendor/gtest/googletest/include/gtest/gtest_prod.h ./vendor/gtest/googletest/include/gtest/internal/custom/gtest-port.h ./vendor/gtest/googletest/include/gtest/internal/custom/gtest-printers.h ./vendor/gtest/googletest/include/gtest/internal/custom/gtest.h ./vendor/gtest/googletest/include/gtest/internal/gtest-death-test-internal.h ./vendor/gtest/googletest/include/gtest/internal/gtest-filepath.h ./vendor/gtest/googletest/include/gtest/internal/gtest-internal.h ./vendor/gtest/googletest/include/gtest/internal/gtest-param-util.h ./vendor/gtest/googletest/include/gtest/internal/gtest-port-arch.h ./vendor/gtest/googletest/include/gtest/internal/gtest-port.h ./vendor/gtest/googletest/include/gtest/internal/gtest-string.h ./vendor/gtest/googletest/include/gtest/internal/gtest-type-util.h ./vendor/gtest/googletest/samples/prime_tables.h ./vendor/gtest/googletest/samples/sample1.cc ./vendor/gtest/googletest/samples/sample1.h ./vendor/gtest/googletest/samples/sample10_unittest.cc ./vendor/gtest/googletest/samples/sample1_unittest.cc ./vendor/gtest/googletest/samples/sample2.cc ./vendor/gtest/googletest/samples/sample2.h ./vendor/gtest/googletest/samples/sample2_unittest.cc ./vendor/gtest/googletest/samples/sample3-inl.h ./vendor/gtest/googletest/samples/sample3_unittest.cc ./vendor/gtest/googletest/samples/sample4.cc ./vendor/gtest/googletest/samples/sample4.h ./vendor/gtest/googletest/samples/sample4_unittest.cc ./vendor/gtest/googletest/samples/sample5_unittest.cc ./vendor/gtest/googletest/samples/sample6_unittest.cc ./vendor/gtest/googletest/samples/sample7_unittest.cc ./vendor/gtest/googletest/samples/sample8_unittest.cc ./vendor/gtest/googletest/samples/sample9_unittest.cc ./vendor/gtest/googletest/scripts/common.py ./vendor/gtest/googletest/scripts/fuse_gtest_files.py ./vendor/gtest/googletest/scripts/gen_gtest_pred_impl.py ./vendor/gtest/googletest/scripts/release_docs.py ./vendor/gtest/googletest/scripts/run_with_path.py ./vendor/gtest/googletest/scripts/upload.py ./vendor/gtest/googletest/scripts/upload_gtest.py ./vendor/gtest/googletest/src/gtest-all.cc ./vendor/gtest/googletest/src/gtest-death-test.cc ./vendor/gtest/googletest/src/gtest-filepath.cc ./vendor/gtest/googletest/src/gtest-internal-inl.h ./vendor/gtest/googletest/src/gtest-matchers.cc ./vendor/gtest/googletest/src/gtest-port.cc ./vendor/gtest/googletest/src/gtest-printers.cc ./vendor/gtest/googletest/src/gtest-test-part.cc ./vendor/gtest/googletest/src/gtest-typed-test.cc ./vendor/gtest/googletest/src/gtest.cc ./vendor/gtest/googletest/src/gtest_main.cc ./vendor/gtest/googletest/test/BUILD.bazel ./vendor/gtest/googletest/test/googletest-break-on-failure-unittest.py ./vendor/gtest/googletest/test/googletest-break-on-failure-unittest_.cc ./vendor/gtest/googletest/test/googletest-catch-exceptions-test.py ./vendor/gtest/googletest/test/googletest-catch-exceptions-test_.cc ./vendor/gtest/googletest/test/googletest-color-test.py ./vendor/gtest/googletest/test/googletest-color-test_.cc ./vendor/gtest/googletest/test/googletest-death-test-test.cc ./vendor/gtest/googletest/test/googletest-death-test_ex_test.cc ./vendor/gtest/googletest/test/googletest-env-var-test.py ./vendor/gtest/googletest/test/googletest-env-var-test_.cc ./vendor/gtest/googletest/test/googletest-filepath-test.cc ./vendor/gtest/googletest/test/googletest-filter-unittest.py ./vendor/gtest/googletest/test/googletest-filter-unittest_.cc ./vendor/gtest/googletest/test/googletest-json-outfiles-test.py ./vendor/gtest/googletest/test/googletest-json-output-unittest.py ./vendor/gtest/googletest/test/googletest-list-tests-unittest.py ./vendor/gtest/googletest/test/googletest-list-tests-unittest_.cc ./vendor/gtest/googletest/test/googletest-listener-test.cc ./vendor/gtest/googletest/test/googletest-message-test.cc ./vendor/gtest/googletest/test/googletest-options-test.cc ./vendor/gtest/googletest/test/googletest-output-test.py ./vendor/gtest/googletest/test/googletest-output-test_.cc ./vendor/gtest/googletest/test/googletest-param-test-invalid-name1-test.py ./vendor/gtest/googletest/test/googletest-param-test-invalid-name1-test_.cc ./vendor/gtest/googletest/test/googletest-param-test-invalid-name2-test.py ./vendor/gtest/googletest/test/googletest-param-test-invalid-name2-test_.cc ./vendor/gtest/googletest/test/googletest-param-test-test.cc ./vendor/gtest/googletest/test/googletest-param-test-test.h ./vendor/gtest/googletest/test/googletest-param-test2-test.cc ./vendor/gtest/googletest/test/googletest-port-test.cc ./vendor/gtest/googletest/test/googletest-printers-test.cc ./vendor/gtest/googletest/test/googletest-shuffle-test.py ./vendor/gtest/googletest/test/googletest-shuffle-test_.cc ./vendor/gtest/googletest/test/googletest-test-part-test.cc ./vendor/gtest/googletest/test/googletest-test2_test.cc ./vendor/gtest/googletest/test/googletest-throw-on-failure-test.py ./vendor/gtest/googletest/test/googletest-throw-on-failure-test_.cc ./vendor/gtest/googletest/test/googletest-uninitialized-test.py ./vendor/gtest/googletest/test/googletest-uninitialized-test_.cc ./vendor/gtest/googletest/test/gtest-typed-test2_test.cc ./vendor/gtest/googletest/test/gtest-typed-test_test.cc ./vendor/gtest/googletest/test/gtest-typed-test_test.h ./vendor/gtest/googletest/test/gtest-unittest-api_test.cc ./vendor/gtest/googletest/test/gtest_all_test.cc ./vendor/gtest/googletest/test/gtest_assert_by_exception_test.cc ./vendor/gtest/googletest/test/gtest_environment_test.cc ./vendor/gtest/googletest/test/gtest_help_test.py ./vendor/gtest/googletest/test/gtest_help_test_.cc ./vendor/gtest/googletest/test/gtest_json_test_utils.py ./vendor/gtest/googletest/test/gtest_list_output_unittest.py ./vendor/gtest/googletest/test/gtest_list_output_unittest_.cc ./vendor/gtest/googletest/test/gtest_main_unittest.cc ./vendor/gtest/googletest/test/gtest_no_test_unittest.cc ./vendor/gtest/googletest/test/gtest_pred_impl_unittest.cc ./vendor/gtest/googletest/test/gtest_premature_exit_test.cc ./vendor/gtest/googletest/test/gtest_prod_test.cc ./vendor/gtest/googletest/test/gtest_repeat_test.cc ./vendor/gtest/googletest/test/gtest_skip_test.cc ./vendor/gtest/googletest/test/gtest_sole_header_test.cc ./vendor/gtest/googletest/test/gtest_stress_test.cc ./vendor/gtest/googletest/test/gtest_test_macro_stack_footprint_test.cc ./vendor/gtest/googletest/test/gtest_test_utils.py ./vendor/gtest/googletest/test/gtest_throw_on_failure_ex_test.cc ./vendor/gtest/googletest/test/gtest_unittest.cc ./vendor/gtest/googletest/test/gtest_xml_outfile1_test_.cc ./vendor/gtest/googletest/test/gtest_xml_outfile2_test_.cc ./vendor/gtest/googletest/test/gtest_xml_outfiles_test.py ./vendor/gtest/googletest/test/gtest_xml_output_unittest.py ./vendor/gtest/googletest/test/gtest_xml_output_unittest_.cc ./vendor/gtest/googletest/test/gtest_xml_test_utils.py ./vendor/gtest/googletest/test/production.cc ./vendor/gtest/googletest/test/production.h Copyright: 2005, Google Inc. 2006, Google Inc. 2007, Google Inc. 2008, Google Inc. 2009, Google Inc. 2010, Google Inc. 2013, Google Inc. 2015, Google Inc. 2017, Google Inc. 2018, Google Inc. License: BSD-3-clause Files: ./vendor/gtest/googlemock/scripts/generator/cpp/ast.py ./vendor/gtest/googlemock/scripts/generator/cpp/gmock_class_test.py ./vendor/gtest/googlemock/scripts/generator/cpp/keywords.py ./vendor/gtest/googlemock/scripts/generator/cpp/tokenize.py ./vendor/gtest/googlemock/scripts/generator/cpp/utils.py Copyright: 2007, Google Inc. 2007, Neal Norwitz 2009, Google Inc. 2009, Neal Norwitz License: Apache-2.0 Files: ./vendor/gtest/googletest/test/gtest_skip_check_output_test.py ./vendor/gtest/googletest/test/gtest_skip_environment_check_output_test.py ./vendor/gtest/googletest/test/gtest_skip_in_environment_setup_test.cc ./vendor/gtest/googletest/test/gtest_testbridge_test.py ./vendor/gtest/googletest/test/gtest_testbridge_test_.cc Copyright: 2018, Google LLC. 2019, Google LLC. License: BSD-3-clause Files: ./vendor/gtest/googlemock/scripts/generator/cpp/gmock_class.py ./vendor/gtest/googlemock/scripts/generator/gmock_gen.py Copyright: 2008, Google Inc. License: Apache-2.0 Files: ./vendor/jwt-cpp/include/jwt-cpp/picojson.h Copyright: 2009-2010, Cybozu Labs, Inc. 2011-2014, Kazuho Oku License: BSD-2-clause Files: ./vendor/gtest/library.json Copyright: NONE License: BSD-3-clause Files: ./vendor/jwt-cpp/* Copyright: 2018, Dominik Thalhammer License: Expat Files: ./cmake/FindUUID.cmake Copyright: 2006, Andreas Schneider License: BSD-3-clause License: Apache-2.0 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. . On Debian systems, the full text of the Apache License, Version 2.0 can be found in the file `/usr/share/common-licenses/Apache-2.0'. License: BSD-2-clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. . 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. License: BSD-3-clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. . 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. . THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. License: Expat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. scitokens-cpp-1.2.0/debian/get-orig-source.sh000077500000000000000000000020701511466717700211060ustar00rootroot00000000000000# Generate a source tarball including submodules if [ -z "${1}" ] ; then echo No tag or branch given exit 1 fi ver=${1} # Remove initial v from tag name for use in filenames if [ ${ver:0:1} = 'v' ] ; then fver=${ver:1} else fver=${ver} fi if [ -r scitokens-cpp_${fver}.orig.tar.gz ] ; then echo scitokens-cpp_${fver}.orig.tar.gz already exists exit 1 fi curdir=$(pwd) tdir=$(mktemp -d) cd ${tdir} git clone https://github.com/scitokens/scitokens-cpp.git cd scitokens-cpp git checkout ${ver} if [ $? -ne 0 ] ; then echo No such tag or branch: ${ver} cd ${curdir} rm -rf ${tdir} exit 1 fi git archive --prefix scitokens-cpp_${fver}/ ${ver} -o ${tdir}/scitokens-cpp_${fver}.orig.tar git submodule update --init git submodule foreach --recursive "git archive --prefix scitokens-cpp_${fver}/\$path/ \$sha1 -o ${tdir}/\$sha1.tar ; tar -A -f ${tdir}/scitokens-cpp_${fver}.orig.tar ${tdir}/\$sha1.tar ; rm ${tdir}/\$sha1.tar" cd ${tdir} gzip scitokens-cpp_${fver}.orig.tar mv scitokens-cpp_${fver}.orig.tar.gz ${curdir} cd ${curdir} rm -rf ${tdir} scitokens-cpp-1.2.0/debian/libscitokens-dev.install000066400000000000000000000001231511466717700223660ustar00rootroot00000000000000debian/tmp/usr/include/scitokens usr/include/ debian/tmp/usr/lib/*/libSciTokens.so scitokens-cpp-1.2.0/debian/libscitokens0.install000066400000000000000000000001061511466717700216730ustar00rootroot00000000000000debian/tmp/usr/lib/*/libSciTokens.so.* debian/tmp/usr/bin/scitokens-* scitokens-cpp-1.2.0/debian/obsupdate.sh000077500000000000000000000023151511466717700200630ustar00rootroot00000000000000#!/bin/bash # update ../packaging/debian/packageName.dsc # That file is used by build.opensuse.org's Open Build Service # After the file is updated, it needs to be separately committed to git. HERE="`dirname $0`" ME="`basename $0`" cd $HERE PKG="`sed -n 's/^Source: //p' control`" SPECFILE="../rpm/$PKG.spec" VERSION="$(grep ^Version: $SPECFILE | awk '{print $2}')" RPMREL="$(grep '^%define release_prefix' $SPECFILE | awk '{print $3}')" if [ -z "$RPMREL" ]; then RPMREL="$(grep '^Release:' $SPECFILE | awk '{print $2}' | cut -d% -f1)" fi # if the version is current, increment the release number, else choose 1 DEBREL="`sed -n "s/^Version: ${VERSION}\.${RPMREL}-//p" $PKG.dsc 2>/dev/null`" if [ -z "$DEBREL" ]; then DEBREL=1 else let DEBREL+=1 fi ( echo "# created by $ME, do not edit by hand" # The following two lines are OBS "magic" to use the tarball from the rpm echo "Debtransform-Tar: ${PKG}-${VERSION}.tar.gz" #echo "Debtransform-Files-Tar: " echo "Format: 3.0" echo "Version: ${VERSION}.${RPMREL}-${DEBREL}" echo "Binary: $PKG" cat control echo "Files:" echo " ffffffffffffffffffffffffffffffff 99999 file1" echo " ffffffffffffffffffffffffffffffff 99999 file2" ) > $PKG.dsc # echo "Updated $PWD/$PKG.dsc" scitokens-cpp-1.2.0/debian/patches/000077500000000000000000000000001511466717700171645ustar00rootroot00000000000000scitokens-cpp-1.2.0/debian/patches/series000066400000000000000000000000001511466717700203670ustar00rootroot00000000000000scitokens-cpp-1.2.0/debian/rules000077500000000000000000000003061511466717700166140ustar00rootroot00000000000000#!/usr/bin/make -f DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) %: dh $@ override_dh_auto_configure: dh_auto_configure -- -DLIB_INSTALL_DIR=lib/$(DEB_HOST_MULTIARCH) scitokens-cpp-1.2.0/debian/scitokens-cpp.dsc000066400000000000000000000033551511466717700210200ustar00rootroot00000000000000# created by obsupdate.sh, do not edit by hand Debtransform-Tar: scitokens-cpp-1.2.0.tar.gz Format: 3.0 Version: 1.2.0.1-1 Binary: scitokens-cpp Source: scitokens-cpp Section: science Priority: optional Maintainer: Tim Theisen Build-Depends: cmake (>=2.6), debhelper (>=9), libcurl4-openssl-dev | libcurl4-gnutls-dev, libsqlite3-dev, libssl-dev, pkg-config, uuid-dev Standards-Version: 3.9.8 Homepage: https://github.com/scitokens/scitokens-cpp Package: libscitokens0 Section: libs Architecture: any Multi-Arch: same Pre-Depends: ${misc:Pre-Depends} Depends: ${misc:Depends}, ${shlibs:Depends} Description: C++ Implementation of the SciTokens Library SciTokens provide a token format for distributed authorization The tokens are self-describing, can be verified in a distributed fashion (no need to contact the issuer to determine if the token is valid). This is convenient for a federated environment where several otherwise-independent storage endpoints want to delegate trust for an issuer for managing a storage allocation. Package: libscitokens-dev Section: libdevel Architecture: any Multi-Arch: same Depends: libscitokens0 (= ${binary:Version}), ${misc:Depends} Description: Header files for the libscitokens public interfaces SciTokens provide a token format for distributed authorization. The tokens are self-describing, can be verified in a distributed fashion (no need to contact the issuer to determine if the token is valid). This is convenient for a federated environment where several otherwise-independent storage endpoints want to delegate trust for an issuer for managing a storage allocation. Files: ffffffffffffffffffffffffffffffff 99999 file1 ffffffffffffffffffffffffffffffff 99999 file2 scitokens-cpp-1.2.0/debian/source/000077500000000000000000000000001511466717700170355ustar00rootroot00000000000000scitokens-cpp-1.2.0/debian/source/format000066400000000000000000000000141511466717700202430ustar00rootroot000000000000003.0 (quilt) scitokens-cpp-1.2.0/debian/source/local-options000066400000000000000000000000541511466717700215420ustar00rootroot00000000000000#abort-on-upstream-changes #unapply-patches scitokens-cpp-1.2.0/debian/watch000066400000000000000000000000121511466717700165570ustar00rootroot00000000000000version=3 scitokens-cpp-1.2.0/rpm/000077500000000000000000000000001511466717700151115ustar00rootroot00000000000000scitokens-cpp-1.2.0/rpm/scitokens-cpp.spec000066400000000000000000000155041511466717700205540ustar00rootroot00000000000000Name: scitokens-cpp Version: 1.2.0 Release: 1%{?dist} Summary: C++ Implementation of the SciTokens Library License: ASL 2.0 URL: https://github.com/scitokens/scitokens-cpp # Directions to generate a proper release: # VER=0.3.3 # for example # git archive --prefix "scitokens-cpp-$VER/" -o "scitokens-cpp-$VER.tar" v$VER # git submodule update --init # git submodule foreach --recursive "git archive --prefix=scitokens-cpp-$VER/\$path/ --output=\$sha1.tar HEAD && tar --concatenate --file=$(pwd)/scitokens-cpp-$VER.tar \$sha1.tar && rm \$sha1.tar" # gzip "scitokens-cpp-$VER.tar" Source0: https://github.com/scitokens/scitokens-cpp/releases/download/v%{version}/%{name}-%{version}.tar.gz # Scitokens-cpp bundles jwt-cpp, a header only dependency # Since it doesn't create a library that can be used by others, it seems # inappropriate to include a "Provides", as jwt-cpp is not provided # by this package. BuildRequires: gcc-c++ BuildRequires: make BuildRequires: cmake3 BuildRequires: sqlite-devel BuildRequires: openssl-devel BuildRequires: libcurl-devel BuildRequires: libuuid-devel %if 0%{?el7} # needed for ldconfig_scriptlets BuildRequires: epel-rpm-macros %endif %description %{summary} %package devel Summary: Header files for the scitokens-cpp public interfaces Requires: %{name}%{?_isa} = %{version} %description devel %{summary} %prep %setup -q %build %cmake3 %cmake3_build %install %cmake3_install # Run the ldconfig %ldconfig_scriptlets %files %{_libdir}/libSciTokens.so.0* %{_bindir}/scitokens-* %license LICENSE %doc README.md %files devel %{_libdir}/libSciTokens.so %{_includedir}/scitokens/scitokens.h %dir %{_includedir}/scitokens %changelog * Fri Dec 05 2025 Derek Weitzel - 1.2.0-1 - Fix segfault if the JSON parser cannot parse the JWKS - Fix float time claims issue and improve error handling - Fix security issue with malicious issuer handling in error messages - Improve JWTVerificationException message to include the invalid issuer - Update usage on verify command to make the TOKENFILE explicit - Read token for scitokens-verify from stdin - Set CURLOPT_NOSIGNAL option in SimpleCurlGet to prevent signal interruptions - Adding asan value to the job name - Turn off building unit tests by default. - Add cmake option SCITOKENS_WITH_ASAN which enables memory checking with the address sanitizer. Also enable this in CI, so that tests fail if they hit a memory leak or other memory problem. - Fix memory leak in store_public_ec_key - Fix memory leaks in the unit tests - Fix memory leak in rs256_from_coords - Fix memory leak in scitokens_verify * Mon Feb 24 2025 Derek Weitzel - 1.1.3-1 - Include cstdint import for jwt library to support newer compilers * Wed Oct 30 2024 Derek Weitzel - 1.1.2-1 - Turn off CMAKE unity builds - Add a mutex around requesting public keys to stop overloading issuers * Wed Feb 28 2024 Derek Weitzel - 1.1.1-1 - Improve error handling around the sqlite3 library - Fix test failures and compiler warnings * Tue Nov 07 2023 Derek Weitzel - 1.1.0-1 - Allow the scitokens library user to setup a custom CA file - Fix typecast errors in scitoken_status_get_*() that caused async queries to fail - Fix logic error in deserialize_continue() that caused async deserialization to fail * Thu Jun 15 2023 Derek Weitzel - 1.0.2-1 - Add support for API-configurable cache home - Fix enforcer_acl_free logic - scitokens_internal: catch matching exception type after jwt-cpp update * Wed Apr 26 2023 Derek Weitzel - 1.0.1-1 - Fix bug in generate acls which would cause a timeout * Tue Mar 21 2023 Derek Weitzel - 1.0.0-1 - Add async API for parsing and verifying tokens - Add configuration API - Make nbf claim optional for non-scitokens tokens - Update to OpenSSL 3.0 * Wed Jun 22 2022 Derek Weitzel - 0.7.1-1 - Add scitokens-* binaries to the package - Bug: close sqlite db handle on return * Fri Feb 18 2022 Derek Weitzel - 0.7.0-1 - Changes from static analysis - If only one key is available, do not error on no kid - Support at+jwt profile * Fri Sep 03 2021 Dave Dykstra - 0.6.3-1 - Add support for building Debian packages on the OpenSUSE Build System - Add patch to jwt-cpp to update its picojson dependency in order to enable it to compile on Debian 11 and Ubuntu 21.04 - Fix el7 build by requiring epel-rpm-macros * Thu Aug 26 2021 Dave Dykstra - 0.6.2-2 - Make the build require cmake3 instead of cmake * Thu Jun 03 2021 Derek Weitzel - 0.6.2-1 - Correct WLCG compat for condor read permissions * Thu May 20 2021 Derek Weitzel - 0.6.1-1 - Fix vector resize for el8+ builds * Tue May 18 2021 Derek Weitzel - 0.6.0-2 - Add back paren patch * Tue Mar 09 2021 Derek Weitzel - 0.6.0-1 - Fix compilation errors on c++11 - Update to jwt-cpp-0.4.0 vendor - Change scitoken profile name to match spec, scitoken:2.0 * Wed Jun 24 2020 Derek Weitzel - 0.5.1-1 - Add storage.modify as write permission * Fri Feb 28 2020 Derek Weitzel - 0.5.0-1 - Add API for retrieving string list attributes * Fri Nov 08 2019 Derek Weitzel - 0.4.0-1 - Add support for WLCG profile * Fri Nov 08 2019 Derek Weitzel - 0.3.5-1 - Fix EC public key handling * Wed Sep 18 2019 Derek Weitzel - 0.3.4-1 - Fix bugs for support with IAM * Thu Aug 01 2019 Derek Weitzel - 0.3.3-3 - Update the packaging to bring it line with EPEL (fedora) guidelines * Tue Jul 30 2019 Derek Weitzel - 0.3.3-2 - Change the Source URL - Use make_build in the packaging * Thu Jul 25 2019 Derek Weitzel - 0.3.3-1 - Merge OSG changes - Use a newer, still supported version of devtoolset - Fix bug in verifying EC signed tokens #13 * Thu Jul 25 2019 Derek Weitzel - 0.3.2-1 - Update RPM to v0.3.2 of the packaging. - Fix downloading public key bug #12 * Thu Jun 20 2019 Brian Bockelman - 0.3.1-1 - Update RPM to v0.3.1 of the packaging. * Wed May 29 2019 Mátyás Selmeci - 0.3.0-4 - Use double layer of const for deserialize (patch from https://github.com/scitokens/scitokens-cpp/commit/ac0b2f0679488fa91c14ed781268efbcdb69ed3c) * Mon May 13 2019 Mátyás Selmeci - 0.3.0-3 - Add Force-aud-test-in-the-validator.patch from https://github.com/scitokens/scitokens-cpp/pull/8 * Fri May 03 2019 Mátyás Selmeci - 0.3.0-2 - Fix requirements * Thu May 02 2019 Mátyás Selmeci - 0.3.0-1 - Update to v0.3.0 - Add dependencies on libcurl-devel, libuuid-devel * Thu Jan 03 2019 Brian Bockelman - 0.1.0-1 - Initial version of the SciTokens C++ RPM. scitokens-cpp-1.2.0/src/000077500000000000000000000000001511466717700151025ustar00rootroot00000000000000scitokens-cpp-1.2.0/src/create.cpp000066400000000000000000000140361511466717700170550ustar00rootroot00000000000000 #include "scitokens.h" #include #include #include #include #include #include #include namespace { const char usage[] = "\n" "Syntax: %s [--cred cred_file] [--key key_file] [--keyid kid]\n" " [--claim key=val] ...\n" "\n" " Options\n" " -h | --help Display usage\n" " -c | --cred File containing signing " "credential.\n" " -k | --key File containing the signing " "private key.\n" " -K | --keyid Name of the token key.\n" " -i | --issuer Issuer for the token.\n" " -p | --profile Token profile (wlcg, scitokens1, " "scitokens2, atjwt).\n" "\n"; const struct option long_options[] = {{"help", no_argument, NULL, 'h'}, {"cred", required_argument, NULL, 'c'}, {"key", required_argument, NULL, 'k'}, {"keyid", required_argument, NULL, 'K'}, {"issuer", required_argument, NULL, 'i'}, {"claim", required_argument, NULL, 'C'}, {"profile", required_argument, NULL, 'p'}, {0, 0, 0, 0}}; const char short_options[] = "hc:k:K:i:C:p:"; std::string g_cred, g_key, g_kid, g_issuer, g_profile; std::vector g_claims; int init_arguments(int argc, char *argv[]) { int arg; while ((arg = getopt_long(argc, argv, short_options, long_options, nullptr)) != -1) { switch (arg) { case 'h': printf(usage, argv[0]); exit(0); break; case 'c': g_cred = optarg; break; case 'k': g_key = optarg; break; case 'K': g_kid = optarg; break; case 'i': g_issuer = optarg; break; case 'C': g_claims.emplace_back(optarg); break; case 'p': g_profile = optarg; break; default: fprintf(stderr, usage, argv[0]); exit(1); break; } } if (optind != argc) { fprintf(stderr, "%s: invalid option -- %s\n", argv[0], argv[optind]); fprintf(stderr, usage, argv[0]); exit(1); } if (g_cred.empty()) { fprintf(stderr, "%s: missing --cred option\n", argv[0]); fprintf(stderr, usage, argv[0]); exit(1); } if (g_key.empty()) { fprintf(stderr, "%s: missing --key option\n", argv[0]); fprintf(stderr, usage, argv[0]); exit(1); } if (g_kid.empty()) { fprintf(stderr, "%s: missing --keyid option\n", argv[0]); fprintf(stderr, usage, argv[0]); exit(1); } if (g_issuer.empty()) { fprintf(stderr, "%s: missing --issuer option\n", argv[0]); fprintf(stderr, usage, argv[0]); exit(1); } return 0; } } // namespace int main(int argc, char *argv[]) { int rv = init_arguments(argc, argv); if (rv) { return rv; } std::ifstream priv_ifs(g_key); std::string private_contents((std::istreambuf_iterator(priv_ifs)), (std::istreambuf_iterator())); std::ifstream pub_ifs(g_cred); std::string public_contents((std::istreambuf_iterator(pub_ifs)), (std::istreambuf_iterator())); char *err_msg; auto key_raw = scitoken_key_create(g_kid.c_str(), "ES256", public_contents.c_str(), private_contents.c_str(), &err_msg); std::unique_ptr key( key_raw, scitoken_key_destroy); if (key_raw == nullptr) { fprintf(stderr, "Failed to generate a key: %s\n", err_msg); free(err_msg); return 1; } std::unique_ptr token( scitoken_create(key_raw), scitoken_destroy); if (token.get() == nullptr) { fprintf(stderr, "Failed to generate a new token.\n"); return 1; } rv = scitoken_set_claim_string(token.get(), "iss", g_issuer.c_str(), &err_msg); if (rv) { fprintf(stderr, "Failed to set issuer: %s\n", err_msg); free(err_msg); return 1; } for (const auto &claim : g_claims) { auto pos = claim.find("="); if (pos == std::string::npos) { fprintf(stderr, "Claim must contain a '=' character: %s\n", claim.c_str()); return 1; } auto key = claim.substr(0, pos); auto val = claim.substr(pos + 1); rv = scitoken_set_claim_string(token.get(), key.c_str(), val.c_str(), &err_msg); if (rv) { fprintf(stderr, "Failed to set claim (%s=%s): %s\n", key.c_str(), val.c_str(), err_msg); free(err_msg); return 1; } } if (!g_profile.empty()) { SciTokenProfile profile; if (g_profile == "wlcg") { profile = SciTokenProfile::WLCG_1_0; } else if (g_profile == "scitokens1") { profile = SciTokenProfile::SCITOKENS_1_0; } else if (g_profile == "scitokens2") { profile = SciTokenProfile::SCITOKENS_2_0; } else if (g_profile == "atjwt") { profile = SciTokenProfile::AT_JWT; } else { fprintf(stderr, "Unknown token profile: %s\n", g_profile.c_str()); return 1; } scitoken_set_serialize_mode(token.get(), profile); } char *value; rv = scitoken_serialize(token.get(), &value, &err_msg); if (rv) { fprintf(stderr, "Failed to serialize the token: %s\n", err_msg); free(err_msg); return 1; } printf("%s\n", value); } scitokens-cpp-1.2.0/src/list_access.cpp000066400000000000000000000027241511466717700201070ustar00rootroot00000000000000#include #include "scitokens.h" int main(int argc, const char **argv) { if (argc < 4) { std::cerr << "Usage: " << argv[0] << " (TOKEN) (ISSUER) (AUDIENCE)" << std::endl; return 1; } std::string token(argv[1]); std::string issuer(argv[2]); std::string audience(argv[3]); const char *aud_list[2]; aud_list[0] = audience.c_str(); aud_list[1] = nullptr; SciToken scitoken; char *err_msg = nullptr; if (scitoken_deserialize(token.c_str(), &scitoken, nullptr, &err_msg)) { std::cout << "Failed to deserialize a token: " << err_msg << std::endl; return 1; } std::cout << "Token deserialization successful. Checking authorizations." << std::endl; Enforcer enf; if (!(enf = enforcer_create(issuer.c_str(), aud_list, &err_msg))) { std::cout << "Failed to create a new enforcer object: " << err_msg << std::endl; return 1; } Acl *acls; if (enforcer_generate_acls(enf, scitoken, &acls, &err_msg)) { std::cout << "ACL generation failed: " << err_msg << std::endl; return 1; } std::cout << "Start of ACLs:" << std::endl; for (int idx = 0; acls[idx].authz && acls[idx].resource; idx++) { std::cout << "ACL: " << acls[idx].authz << ":" << acls[idx].resource << std::endl; } std::cout << "End of ACLs:" << std::endl; enforcer_destroy(enf); return 0; } scitokens-cpp-1.2.0/src/scitokens.cpp000066400000000000000000000770361511466717700176250ustar00rootroot00000000000000#include #include #include #include #include "scitokens.h" #include "scitokens_internal.h" /** * GLOBALS */ // Cache timeout config std::atomic_int configurer::Configuration::m_next_update_delta{600}; std::atomic_int configurer::Configuration::m_expiry_delta{4 * 24 * 3600}; // SciTokens cache home config std::shared_ptr configurer::Configuration::m_cache_home = std::make_shared(""); std::shared_ptr configurer::Configuration::m_tls_ca_file = std::make_shared(""); SciTokenKey scitoken_key_create(const char *key_id, const char *alg, const char *public_contents, const char *private_contents, char **err_msg) { if (key_id == nullptr) { if (err_msg) { *err_msg = strdup("Key ID cannot be NULL."); } return nullptr; } if (alg == nullptr) { if (err_msg) { *err_msg = strdup("Algorithm cannot be NULL."); } return nullptr; } if (public_contents == nullptr) { if (err_msg) { *err_msg = strdup("Public key contents cannot be NULL."); } return nullptr; } if (private_contents == nullptr) { if (err_msg) { *err_msg = strdup("Private key contents cannot be NULL."); } return nullptr; } return new scitokens::SciTokenKey(key_id, alg, public_contents, private_contents); } void scitoken_key_destroy(SciTokenKey token) { scitokens::SciTokenKey *real_token = reinterpret_cast(token); delete real_token; } SciToken scitoken_create(SciTokenKey private_key) { scitokens::SciTokenKey *key = reinterpret_cast(private_key); return new scitokens::SciToken(*key); } void scitoken_destroy(SciToken token) { scitokens::SciToken *real_token = reinterpret_cast(token); delete real_token; } int scitoken_set_claim_string(SciToken token, const char *key, const char *value, char **err_msg) { scitokens::SciToken *real_token = reinterpret_cast(token); if (real_token == nullptr) { if (err_msg) { *err_msg = strdup("Token passed is not initialized."); } return -1; } if (key == nullptr) { if (err_msg) { *err_msg = strdup("Claim key passed is not initialized."); } return -1; } if (value == nullptr) { if (err_msg) { *err_msg = strdup("Claim value passed is not initialized."); } return -1; } try { real_token->set_claim(key, jwt::claim(std::string(value))); } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } return 0; } void scitoken_set_serialize_profile(SciToken token, SciTokenProfile profile) { scitoken_set_serialize_mode(token, profile); } void scitoken_set_serialize_mode(SciToken token, SciTokenProfile profile) { scitokens::SciToken *real_token = reinterpret_cast(token); if (real_token == nullptr) { return; } real_token->set_serialize_mode( static_cast(profile)); } void scitoken_set_deserialize_profile(SciToken token, SciTokenProfile profile) { scitokens::SciToken *real_token = reinterpret_cast(token); if (real_token == nullptr) { return; } real_token->set_deserialize_mode( static_cast(profile)); } int scitoken_get_claim_string(const SciToken token, const char *key, char **value, char **err_msg) { scitokens::SciToken *real_token = reinterpret_cast(token); std::string claim_str; try { claim_str = real_token->get_claim_string(key); } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } *value = strdup(claim_str.c_str()); return 0; } int scitoken_set_claim_string_list(const SciToken token, const char *key, const char **value, char **err_msg) { auto real_token = reinterpret_cast(token); if (real_token == nullptr) { if (err_msg) *err_msg = strdup( "NULL scitoken passed to scitoken_get_claim_string_list"); return -1; } std::vector claim_list; int idx = 0; while (value[idx++]) { } claim_list.reserve(idx); idx = 0; while (value[idx++]) { claim_list.emplace_back(value[idx - 1]); } real_token->set_claim_list(key, claim_list); return 0; } int scitoken_get_claim_string_list(const SciToken token, const char *key, char ***value, char **err_msg) { auto real_token = reinterpret_cast(token); if (real_token == nullptr) { if (err_msg) *err_msg = strdup( "NULL scitoken passed to scitoken_get_claim_string_list"); return -1; } std::vector claim_list; try { claim_list = real_token->get_claim_list(key); } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } auto claim_list_c = static_cast(malloc(sizeof(char *) * (claim_list.size() + 1))); claim_list_c[claim_list.size()] = nullptr; int idx = 0; for (const auto &entry : claim_list) { claim_list_c[idx] = strdup(entry.c_str()); if (!claim_list_c[idx]) { scitoken_free_string_list(claim_list_c); if (err_msg) { *err_msg = strdup("Failed to create a copy of string entry in list"); } return -1; } idx++; } *value = claim_list_c; return 0; } void scitoken_free_string_list(char **value) { int idx = 0; do { free(value[idx++]); } while (value[idx]); free(value); } int scitoken_get_expiration(const SciToken token, long long *expiry, char **err_msg) { if (!token) { if (err_msg) { *err_msg = strdup("Token cannot be NULL"); } return -1; } if (!expiry) { if (err_msg) { *err_msg = strdup("Expiry output parameter cannot be NULL"); } return -1; } scitokens::SciToken *real_token = reinterpret_cast(token); if (!real_token->has_claim("exp")) { *expiry = -1; return 0; } long long result; try { auto claim_value = real_token->get_claim("exp").to_json(); if (claim_value.is()) { // Integer value result = claim_value.get(); } else if (claim_value.is()) { // Float value - convert to integer (truncate) // Float value - convert to integer using std::floor(). // This ensures expiration is not extended by fractional seconds. result = static_cast(std::floor(claim_value.get())); } else { if (err_msg) { *err_msg = strdup("'exp' claim must be a number (integer or float)"); } return -1; } } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } *expiry = result; return 0; } void scitoken_set_lifetime(SciToken token, int lifetime) { if (token == nullptr) { return; } scitokens::SciToken *real_token = reinterpret_cast(token); real_token->set_lifetime(lifetime); } int scitoken_serialize(const SciToken token, char **value, char **err_msg) { if (value == nullptr) { if (err_msg) { *err_msg = strdup("Output variable not provided"); } return -1; } scitokens::SciToken *real_token = reinterpret_cast(token); try { std::string serialized = real_token->serialize(); *value = strdup(serialized.c_str()); } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } return 0; } int scitoken_deserialize(const char *value, SciToken *token, char const *const *allowed_issuers, char **err_msg) { if (value == nullptr) { if (err_msg) { *err_msg = strdup("Token may not be NULL"); } return -1; } if (token == nullptr) { if (err_msg) { *err_msg = strdup("Output token not provided"); } return -1; } scitokens::SciTokenKey key; scitokens::SciToken *real_token = new scitokens::SciToken(key); int retval = scitoken_deserialize_v2(value, reinterpret_cast(real_token), allowed_issuers, err_msg); if (retval) { delete real_token; } else { *token = real_token; } return retval; } int scitoken_deserialize_v2(const char *value, SciToken token, char const *const *allowed_issuers, char **err_msg) { scitokens::SciToken *real_token = reinterpret_cast(token); std::vector allowed_issuers_vec; if (allowed_issuers != nullptr) { for (int idx = 0; allowed_issuers[idx]; idx++) { allowed_issuers_vec.push_back(allowed_issuers[idx]); } } try { real_token->deserialize(value, allowed_issuers_vec); } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } return 0; } int scitoken_deserialize_start(const char *value, SciToken *token, char const *const *allowed_issuers, SciTokenStatus *status_out, char **err_msg) { if (value == nullptr) { if (err_msg) { *err_msg = strdup("Token may not be NULL"); } return -1; } if (token == nullptr) { if (err_msg) { *err_msg = strdup("Output token not provided"); } return -1; } scitokens::SciTokenKey key; scitokens::SciToken *real_token = new scitokens::SciToken(key); std::vector allowed_issuers_vec; if (allowed_issuers != nullptr) { for (int idx = 0; allowed_issuers[idx]; idx++) { allowed_issuers_vec.push_back(allowed_issuers[idx]); } } std::unique_ptr status; try { status = real_token->deserialize_start(value, allowed_issuers_vec); } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } delete real_token; *status_out = nullptr; return -1; } // Check if we're done if (status->m_status->m_done) { *token = real_token; *status_out = nullptr; return 0; } *token = real_token; *status_out = status.release(); return 0; } int scitoken_deserialize_continue(SciToken *token, SciTokenStatus *status, char **err_msg) { if (token == nullptr) { if (err_msg) { *err_msg = strdup("Output token not provided"); } return -1; } scitokens::SciToken *real_token = reinterpret_cast(*token); std::unique_ptr real_status( reinterpret_cast(*status)); if (*status == nullptr || real_status->m_status->m_done) { *status = nullptr; return 0; } try { real_status = real_token->deserialize_continue(std::move(real_status)); } catch (std::exception &exc) { *status = nullptr; if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } if (real_status->m_status->m_done) { *status = nullptr; } else { *status = real_status.release(); } return 0; } int scitoken_store_public_ec_key(const char *issuer, const char *keyid, const char *key, char **err_msg) { bool success; try { success = scitokens::Validator::store_public_ec_key(issuer, keyid, key); } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } return success ? 0 : -1; } Validator validator_create() { return new Validator(); } void validator_destroy(Validator validator) { scitokens::Validator *real_validator = reinterpret_cast(validator); delete real_validator; } void validator_set_token_profile(Validator validator, SciTokenProfile profile) { if (validator == nullptr) { return; } auto real_validator = reinterpret_cast(validator); real_validator->set_validate_profile( static_cast(profile)); } int validator_add(Validator validator, const char *claim, StringValidatorFunction validator_func, char **err_msg) { if (validator == nullptr) { if (err_msg) { *err_msg = strdup("Validator may not be a null pointer"); } return -1; } auto real_validator = reinterpret_cast(validator); if (claim == nullptr) { if (err_msg) { *err_msg = strdup("Claim name may not be a null pointer"); } return -1; } if (validator_func == nullptr) { if (err_msg) { *err_msg = strdup("Validator function may not be a null pointer"); } return -1; } real_validator->add_string_validator(claim, validator_func); return 0; } int validator_add_critical_claims(Validator validator, const char **claims, char **err_msg) { if (validator == nullptr) { if (err_msg) { *err_msg = strdup("Validator may not be a null pointer"); } return -1; } auto real_validator = reinterpret_cast(validator); if (claims == nullptr) { if (err_msg) { *err_msg = strdup("Claim list may not be a null pointer"); } return -1; } std::vector claims_vec; for (int idx = 0; claims[idx]; idx++) { claims_vec.push_back(claims[idx]); } real_validator->add_critical_claims(claims_vec); return 0; } int validator_validate(Validator validator, SciToken scitoken, char **err_msg) { if (validator == nullptr) { if (err_msg) { *err_msg = strdup("Validator may not be a null pointer"); } return -1; } auto real_validator = reinterpret_cast(validator); if (scitoken == nullptr) { if (err_msg) { *err_msg = strdup("SciToken may not be a null pointer"); } return -1; } auto real_scitoken = reinterpret_cast(scitoken); try { real_validator->verify(*real_scitoken, time(NULL) + 20); } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } return 0; } int validator_set_time(Validator validator, time_t now, char **err_msg) { if (validator == nullptr) { if (err_msg) { *err_msg = strdup("Validator may not be a null pointer"); } return -1; } auto real_validator = reinterpret_cast(validator); real_validator->set_now(std::chrono::system_clock::from_time_t(now)); return 0; } Enforcer enforcer_create(const char *issuer, const char **audience_list, char **err_msg) { if (issuer == nullptr) { if (err_msg) { *err_msg = strdup("Issuer may not be a null pointer"); } return nullptr; } std::vector aud_list; if (audience_list != nullptr) { for (int idx = 0; audience_list[idx]; idx++) { aud_list.push_back(audience_list[idx]); } } return new scitokens::Enforcer(issuer, aud_list); } void enforcer_destroy(Enforcer enf) { if (enf == nullptr) { return; } auto real_enf = reinterpret_cast(enf); delete real_enf; } void enforcer_acl_free(Acl *acls) { for (int idx = 0; acls[idx].authz != nullptr || acls[idx].resource != nullptr; idx++) { free(const_cast(acls[idx].authz)); free(const_cast(acls[idx].resource)); } free(acls); } void enforcer_set_validate_profile(Enforcer enf, SciTokenProfile profile) { if (enf == nullptr) { return; } auto real_enf = reinterpret_cast(enf); real_enf->set_validate_profile( static_cast(profile)); } namespace { Acl *convert_acls(scitokens::Enforcer::AclsList &acls_list, char **err_msg) { Acl *acl_result = static_cast(malloc((acls_list.size() + 1) * sizeof(Acl))); size_t idx = 0; for (const auto &acl : acls_list) { acl_result[idx].authz = strdup(acl.first.c_str()); acl_result[idx].resource = strdup(acl.second.c_str()); if (acl_result[idx].authz == nullptr) { enforcer_acl_free(acl_result); if (err_msg) { *err_msg = strdup("ACL was generated without an authorization set."); } return nullptr; } if (acl_result[idx].resource == nullptr) { enforcer_acl_free(acl_result); if (err_msg) { *err_msg = strdup("ACL was generated without a resource set."); } return nullptr; } idx++; } acl_result[idx].authz = nullptr; acl_result[idx].resource = nullptr; return acl_result; } } // namespace int enforcer_set_time(Enforcer enf, time_t now, char **err_msg) { if (enf == nullptr) { if (err_msg) { *err_msg = strdup("Enforcer may not be a null pointer"); } return -1; } auto real_enf = reinterpret_cast(enf); real_enf->set_now(std::chrono::system_clock::from_time_t(now)); return 0; } int enforcer_generate_acls(const Enforcer enf, const SciToken scitoken, Acl **acls, char **err_msg) { if (enf == nullptr) { if (err_msg) { *err_msg = strdup("Enforcer may not be a null pointer"); } return -1; } auto real_enf = reinterpret_cast(enf); if (scitoken == nullptr) { if (err_msg) { *err_msg = strdup("SciToken may not be a null pointer"); } return -1; } auto real_scitoken = reinterpret_cast(scitoken); scitokens::Enforcer::AclsList acls_list; try { acls_list = real_enf->generate_acls(*real_scitoken); } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } auto result_acls = convert_acls(acls_list, err_msg); if (!result_acls) { return -1; } *acls = result_acls; return 0; } int enforcer_generate_acls_start(const Enforcer enf, const SciToken scitoken, SciTokenStatus *status_out, Acl **acls, char **err_msg) { if (enf == nullptr) { if (err_msg) { *err_msg = strdup("Enforcer may not be a null pointer"); } return -1; } auto real_enf = reinterpret_cast(enf); if (scitoken == nullptr) { if (err_msg) { *err_msg = strdup("SciToken may not be a null pointer"); } return -1; } auto real_scitoken = reinterpret_cast(scitoken); scitokens::Enforcer::AclsList acls_list; std::unique_ptr status; try { status = real_enf->generate_acls_start(*real_scitoken, acls_list); } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } if (status->m_done) { auto result_acls = convert_acls(acls_list, err_msg); if (!result_acls) { return -1; } *acls = result_acls; *status_out = nullptr; return 0; } *status_out = status.release(); return 0; } int enforcer_generate_acls_continue(const Enforcer enf, SciTokenStatus *status, Acl **acls, char **err_msg) { if (enf == nullptr) { if (err_msg) { *err_msg = strdup("Enforcer may not be a null pointer"); } return -1; } auto real_enf = reinterpret_cast(enf); if (status == nullptr) { if (err_msg) { *err_msg = strdup("Status may not be a null pointer"); } return -1; } scitokens::Enforcer::AclsList acls_list; std::unique_ptr status_internal( reinterpret_cast(*status)); try { status_internal = real_enf->generate_acls_continue( std::move(status_internal), acls_list); } catch (std::exception &exc) { *status = nullptr; if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } if (status_internal->m_done) { auto result_acls = convert_acls(acls_list, err_msg); if (!result_acls) { return -1; } *acls = result_acls; *status = nullptr; return 0; } *status = status_internal.release(); return 0; } int enforcer_test(const Enforcer enf, const SciToken scitoken, const Acl *acl, char **err_msg) { if (enf == nullptr) { if (err_msg) { *err_msg = strdup("Enforcer may not be a null pointer"); } return -1; } auto real_enf = reinterpret_cast(enf); if (scitoken == nullptr) { if (err_msg) { *err_msg = strdup("SciToken may not be a null pointer"); } return -1; } auto real_scitoken = reinterpret_cast(scitoken); if (acl == nullptr) { if (err_msg) { *err_msg = strdup("ACL may not be a null pointer"); } return -1; } try { return real_enf->test(*real_scitoken, acl->authz, acl->resource) == true ? 0 : -1; } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } return 0; } void scitoken_status_free(SciTokenStatus status) { std::unique_ptr status_real( reinterpret_cast(status)); } int scitoken_status_get_timeout_val(const SciTokenStatus *status, time_t expiry_time, struct timeval *timeout, char **err_msg) { if (status == nullptr) { if (err_msg) { *err_msg = strdup("Status object may not be a null pointer"); } return -1; } if (timeout == nullptr) { if (err_msg) { *err_msg = strdup("Timeout object may not be a null pointer"); } return -1; } auto real_status = reinterpret_cast(*status); struct timeval timeout_internal = real_status->m_status->get_timeout_val(expiry_time); timeout->tv_sec = timeout_internal.tv_sec; timeout->tv_usec = timeout_internal.tv_usec; return 0; } int scitoken_status_get_read_fd_set(SciTokenStatus *status, fd_set **read_fd_set, char **err_msg) { if (status == nullptr) { if (err_msg) { *err_msg = strdup("Status object may not be a null pointer"); } return -1; } if (read_fd_set == nullptr) { if (err_msg) { *err_msg = strdup("Read fd_set object may not be a null pointer"); } return -1; } auto real_status = reinterpret_cast(*status); *read_fd_set = real_status->m_status->get_read_fd_set(); return 0; } int scitoken_status_get_write_fd_set(SciTokenStatus *status, fd_set **write_fd_set, char **err_msg) { if (status == nullptr) { if (err_msg) { *err_msg = strdup("Status object may not be a null pointer"); } return -1; } if (write_fd_set == nullptr) { if (err_msg) { *err_msg = strdup("Write fd_set object may not be a null pointer"); } return -1; } auto real_status = reinterpret_cast(*status); *write_fd_set = real_status->m_status->get_write_fd_set(); return 0; } int scitoken_status_get_exc_fd_set(SciTokenStatus *status, fd_set **exc_fd_set, char **err_msg) { if (status == nullptr) { if (err_msg) { *err_msg = strdup("Status object may not be a null pointer"); } return -1; } if (exc_fd_set == nullptr) { if (err_msg) { *err_msg = strdup("Read fd_set object may not be a null pointer"); } return -1; } auto real_status = reinterpret_cast(*status); *exc_fd_set = real_status->m_status->get_exc_fd_set(); return 0; } int scitoken_status_get_max_fd(const SciTokenStatus *status, int *max_fd, char **err_msg) { if (status == nullptr) { if (err_msg) { *err_msg = strdup("Status object may not be a null pointer"); } return -1; } if (max_fd == nullptr) { if (err_msg) { *err_msg = strdup("Max FD may not be a null pointer"); } return -1; } auto real_status = reinterpret_cast(*status); *max_fd = real_status->m_status->get_max_fd(); return 0; } int keycache_refresh_jwks(const char *issuer, char **err_msg) { if (!issuer) { if (err_msg) { *err_msg = strdup("Issuer may not be a null pointer"); } return -1; } try { if (!scitokens::Validator::refresh_jwks(issuer)) { if (err_msg) { *err_msg = strdup("Failed to refresh JWKS cache for issuer."); } return -1; } } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } return 0; } int keycache_get_cached_jwks(const char *issuer, char **jwks, char **err_msg) { if (!issuer) { if (err_msg) { *err_msg = strdup("Issuer may not be a null pointer"); } return -1; } if (!jwks) { if (err_msg) { *err_msg = strdup("JWKS output pointer may not be null."); } return -1; } try { *jwks = strdup(scitokens::Validator::get_jwks(issuer).c_str()); } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } return 0; } int keycache_set_jwks(const char *issuer, const char *jwks, char **err_msg) { if (!issuer) { if (err_msg) { *err_msg = strdup("Issuer may not be a null pointer"); } return -1; } if (!jwks) { if (err_msg) { *err_msg = strdup("JWKS pointer may not be null."); } return -1; } try { if (!scitokens::Validator::store_jwks(issuer, jwks)) { if (err_msg) { *err_msg = strdup("Failed to set the JWKS cache for issuer."); } return -1; } } catch (std::exception &exc) { if (err_msg) { *err_msg = strdup(exc.what()); } return -1; } return 0; } int config_set_int(const char *key, int value, char **err_msg) { return scitoken_config_set_int(key, value, err_msg); } int scitoken_config_set_int(const char *key, int value, char **err_msg) { if (!key) { if (err_msg) { *err_msg = strdup("A key must be provided."); } return -1; } std::string _key = key; if (_key == "keycache.update_interval_s") { if (value < 0) { if (err_msg) { *err_msg = strdup("Update interval must be positive."); } return -1; } configurer::Configuration::set_next_update_delta(value); return 0; } else if (_key == "keycache.expiration_interval_s") { if (value < 0) { if (err_msg) { *err_msg = strdup("Expiry interval must be positive."); } return -1; } configurer::Configuration::set_expiry_delta(value); return 0; } else { if (err_msg) { *err_msg = strdup("Key not recognized."); } return -1; } } int config_get_int(const char *key, char **err_msg) { return scitoken_config_get_int(key, err_msg); } int scitoken_config_get_int(const char *key, char **err_msg) { if (!key) { if (err_msg) { *err_msg = strdup("A key must be provided."); } return -1; } std::string _key = key; if (_key == "keycache.update_interval_s") { return configurer::Configuration::get_next_update_delta(); } else if (_key == "keycache.expiration_interval_s") { return configurer::Configuration::get_expiry_delta(); } else { if (err_msg) { *err_msg = strdup("Key not recognized."); } return -1; } } int scitoken_config_set_str(const char *key, const char *value, char **err_msg) { if (!key) { if (err_msg) { *err_msg = strdup("A key must be provided."); } return -1; } std::string _key = key; if (_key == "keycache.cache_home") { auto rp = configurer::Configuration::set_cache_home(value); if (!rp.first) { // There was an error, pass rp.second to err_msg if (err_msg) { *err_msg = strdup(rp.second.c_str()); } return -1; } } else if (_key == "tls.ca_file") { configurer::Configuration::set_tls_ca_file(value ? std::string(value) : ""); } else { if (err_msg) { *err_msg = strdup("Key not recognized."); } return -1; } return 0; } int scitoken_config_get_str(const char *key, char **output, char **err_msg) { if (!key) { if (err_msg) { *err_msg = strdup("A key must be provided."); } return -1; } std::string _key = key; if (_key == "keycache.cache_home") { *output = strdup(configurer::Configuration::get_cache_home().c_str()); } else if (_key == "tls.ca_file") { *output = strdup(configurer::Configuration::get_tls_ca_file().c_str()); } else { if (err_msg) { *err_msg = strdup("Key not recognized."); } return -1; } return 0; } scitokens-cpp-1.2.0/src/scitokens.h000066400000000000000000000270061511466717700172620ustar00rootroot00000000000000/** * Public header for the SciTokens C library. * * */ #include #include #ifdef __cplusplus #include extern "C" { #else #include #endif typedef void *SciTokenKey; typedef void *SciToken; typedef void *Validator; typedef void *Enforcer; typedef void *SciTokenStatus; typedef void *Configuration; typedef int (*StringValidatorFunction)(const char *value, char **err_msg); typedef struct Acl_s { const char *authz; const char *resource; } Acl; /** * Determine the mode we will use to validate tokens. * - COMPAT mode (default) indicates any supported token format * is acceptable. Where possible, the scope names are translated into * equivalent SciTokens 1.0 claim names (i.e., storage.read -> read; * storage.write -> write). If a typ header claim is present, use that to deduce * type (RFC8725 Section 3.11). * - SCITOKENS_1_0, SCITOKENS_2_0, WLCG_1_0, AT_JWT: only accept these specific * profiles. No automatic translation is performed. */ typedef enum _profile { COMPAT = 0, SCITOKENS_1_0, SCITOKENS_2_0, WLCG_1_0, AT_JWT } SciTokenProfile; SciTokenKey scitoken_key_create(const char *key_id, const char *algorithm, const char *public_contents, const char *private_contents, char **err_msg); void scitoken_key_destroy(SciTokenKey private_key); SciToken scitoken_create(SciTokenKey private_key); void scitoken_destroy(SciToken token); int scitoken_set_claim_string(SciToken token, const char *key, const char *value, char **err_msg); int scitoken_get_claim_string(const SciToken token, const char *key, char **value, char **err_msg); /** * Given a SciToken object, parse a specific claim's value as a list of strings. * If the JSON value is not actually a list of strings - or the claim is not set * - returns an error and sets the err_msg appropriately. * * The returned value is a list of strings that ends with a nullptr. */ int scitoken_get_claim_string_list(const SciToken token, const char *key, char ***value, char **err_msg); /** * Given a list of strings that was returned by scitoken_get_claim_string_list, * free all the associated memory. */ void scitoken_free_string_list(char **value); /** * Set the value of a claim to a list of strings. */ int scitoken_set_claim_string_list(const SciToken token, const char *key, const char **values, char **err_msg); int scitoken_get_expiration(const SciToken token, long long *value, char **err_msg); void scitoken_set_lifetime(SciToken token, int lifetime); int scitoken_serialize(const SciToken token, char **value, char **err_msg); /** * Set the profile used for serialization; if COMPAT mode is used, then * the library default is utilized (currently, scitokens 1.0). */ void scitoken_set_serialize_profile(SciToken token, SciTokenProfile profile); void scitoken_set_serialize_mode(SciToken token, SciTokenProfile profile); void scitoken_set_deserialize_profile(SciToken token, SciTokenProfile profile); int scitoken_deserialize(const char *value, SciToken *token, char const *const *allowed_issuers, char **err_msg); /** * @brief Start the deserialization process for a token, returning a status * object. * * @param value The serialized token. * @param token Destination for the token object. * @param allowed_issuers List of allowed issuers, or nullptr for no issuer * check. * @param status Destination for the status object. * @param err_msg Destination for error message. * @return int 0 on success, -1 on error. */ int scitoken_deserialize_start(const char *value, SciToken *token, char const *const *allowed_issuers, SciTokenStatus *status, char **err_msg); /** * @brief Continue the deserialization process for a token, updating the status * object. * * If the status object indicates that the token is complete, the token object * will be populated and the status object will be nullptr. * * @param token The token object, returned from scitoken_deserialize_start. * @param status Status object for the deserialize. * @param err_msg Destination for error message. * @return int 0 on success, -1 on error. */ int scitoken_deserialize_continue(SciToken *token, SciTokenStatus *status, char **err_msg); int scitoken_deserialize_v2(const char *value, SciToken token, char const *const *allowed_issuers, char **err_msg); int scitoken_store_public_ec_key(const char *issuer, const char *keyid, const char *value, char **err_msg); Validator validator_create(); /** * Set the profile used for validating the tokens; COMPAT (default) will accept * any known token type while others will only support that specific profile. */ void validator_set_token_profile(Validator, SciTokenProfile profile); /** * Set the time to use with the validator. Useful if you want to see if the * token would have been valid at some time in the past. */ int validator_set_time(Validator validator, time_t now, char **err_msg); int validator_add(Validator validator, const char *claim, StringValidatorFunction validator_func, char **err_msg); int validator_add_critical_claims(Validator validator, const char **claims, char **err_msg); int validator_validate(Validator validator, SciToken scitoken, char **err_msg); /** * Destroy a validator object. */ void validator_destroy(Validator); Enforcer enforcer_create(const char *issuer, const char **audience, char **err_msg); void enforcer_destroy(Enforcer); /** * Set the profile used for enforcing ACLs; when set to COMPAT (default), then * the authorizations will be converted to SciTokens 1.0-style authorizations * (so, WLCG's storage.read becomes read). */ void enforcer_set_validate_profile(Enforcer, SciTokenProfile profile); /** * Set the time to use with the enforcer. Useful if you want to see if the * token would have been valid at some time in the past. */ int enforcer_set_time(Enforcer enf, time_t now, char **err_msg); int enforcer_generate_acls(const Enforcer enf, const SciToken scitokens, Acl **acls, char **err_msg); /** * The asynchronous versions of enforcer_generate_acls. */ int enforcer_generate_acls_start(const Enforcer enf, const SciToken scitokens, SciTokenStatus *status, Acl **acls, char **err_msg); int enforcer_generate_acls_continue(const Enforcer enf, SciTokenStatus *status, Acl **acls, char **err_msg); void enforcer_acl_free(Acl *acls); int enforcer_test(const Enforcer enf, const SciToken sci, const Acl *acl, char **err_msg); void scitoken_status_free(SciTokenStatus *status); /** * Get the suggested timeout val. After the timeout value has passed, the * asynchronous operation should continue. * * - `expiry_time`: the expiration time (in Unix epoch seconds) for the * operation in total. The returned timeout value will never take the operation * past the expiration time. */ int scitoken_status_get_timeout_val(const SciTokenStatus *status, time_t expiry_time, struct timeval *timeout, char **err_msg); /** * Get the set of read file descriptors. This will return a borrowed pointer * (whose lifetime matches the status object) pointing at a fd_set array of size * FD_SETSIZE. Any file descriptors owned by the status operation will be set * and the returned fd_set can be used for select() operations. * * IMPLEMENTATION NOTE: If the file descriptor monitored by libcurl are too high * to be stored in this set, libcurl should give a corresponding low timeout val * (100ms) and effectively switch to polling. See: * for more information. */ int scitoken_status_get_read_fd_set(SciTokenStatus *status, fd_set **read_fd_set, char **err_msg); /** * Get the set of write FDs; see documentation for * scitoken_status_get_read_fd_set. */ int scitoken_status_get_write_fd_set(SciTokenStatus *status, fd_set **write_fd_set, char **err_msg); /** * Get the set of exception FDs; see documentation for * scitoken_status_get_exc_fd_set. */ int scitoken_status_get_exc_fd_set(SciTokenStatus *status, fd_set **exc_fd_set, char **err_msg); /** * Get the maximum FD in the status set. * * IMPLEMENTATION NOTE: If the max FD is -1 then it implies libcurl is something * that cannot be modelled by a socket. In such a case, the libcurl docs * suggest using a 100ms timeout for select operations. See * . */ int scitoken_status_get_max_fd(const SciTokenStatus *status, int *max_fd, char **err_msg); /** * API for explicity managing the key cache. * * This manipulates the keycache for the current eUID. */ /** * Refresh the JWKS in the keycache for a given issuer; the refresh will occur * even if the JWKS is not otherwise due for updates. * - Returns 0 on success, nonzero on failure. */ int keycache_refresh_jwks(const char *issuer, char **err_msg); /** * Retrieve the JWKS from the keycache for a given issuer. * - Returns 0 if successful, nonzero on failure. * - If the existing JWKS has expired - or does not exist - this does not * trigger a new download of the JWKS from the issuer. Instead, it will return * a JWKS object with an empty set of keys. * - `jwks` is an output variable set to the contents of the JWKS in the key * cache. */ int keycache_get_cached_jwks(const char *issuer, char **jwks, char **err_msg); /** * Replace any existing key cache entry with one provided by the user. * The expiration and next update time of the user-provided JWKS will utilize * the same rules as a download from an issuer with no explicit cache lifetime * directives. * - `jwks` is value that will be set in the cache. */ int keycache_set_jwks(const char *issuer, const char *jwks, char **err_msg); /** * APIs for managing scitokens configuration parameters. */ // On its way to deprecation int config_set_int(const char *key, int value, char **err_msg); /** * Update scitokens int parameters. * Takes in key/value pairs and assigns the input value to whatever * configuration variable is indicated by the key. * Returns 0 on success, and non-zero for invalid keys or values. */ int scitoken_config_set_int(const char *key, int value, char **err_msg); // on its way to deprecation int config_get_int(const char *key, char **err_msg); /** * Get current scitokens int parameters. * Returns the value associated with the supplied input key on success, and -1 * on failure. This assumes there are no keys for which a negative return value * is permissible. */ int scitoken_config_get_int(const char *key, char **err_msg); /** * Set current scitokens str parameters. * Returns 0 on success, nonzero on failure */ int scitoken_config_set_str(const char *key, const char *value, char **err_msg); /** * Get current scitokens str parameters. * Returns 0 on success, nonzero on failure, and populates the value associated * with the input key to output. */ int scitoken_config_get_str(const char *key, char **output, char **err_msg); #ifdef __cplusplus } #endif scitokens-cpp-1.2.0/src/scitokens_cache.cpp000066400000000000000000000211411511466717700207320ustar00rootroot00000000000000 #include #include #include #include #include #include #include #ifndef PICOJSON_USE_INT64 #define PICOJSON_USE_INT64 #endif #include #include #include "scitokens_internal.h" namespace { void initialize_cachedb(const std::string &keycache_file) { sqlite3 *db; int rc = sqlite3_open(keycache_file.c_str(), &db); if (rc != SQLITE_OK) { std::cerr << "SQLite key cache creation failed." << std::endl; sqlite3_close(db); return; } char *err_msg = nullptr; rc = sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS keycache (" "issuer text UNIQUE PRIMARY KEY NOT NULL," "keys text NOT NULL)", NULL, 0, &err_msg); if (rc) { std::cerr << "Sqlite table creation failed: " << err_msg << std::endl; sqlite3_free(err_msg); } sqlite3_close(db); } /** * Get the Cache file location * 1. User-defined through config api * 2. $XDG_CACHE_HOME * 3. .cache subdirectory of home directory as returned by the password * database */ std::string get_cache_file() { const char *xdg_cache_home = getenv("XDG_CACHE_HOME"); auto bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); bufsize = (bufsize == -1) ? 16384 : bufsize; std::unique_ptr buf(new char[bufsize]); std::string home_dir; struct passwd pwd, *result = NULL; getpwuid_r(geteuid(), &pwd, buf.get(), bufsize, &result); if (result && result->pw_dir) { home_dir = result->pw_dir; home_dir += "/.cache"; } // Figure out where to plop the cache based on priority std::string cache_dir; std::string configured_cache_dir = configurer::Configuration::get_cache_home(); if (configured_cache_dir.length() > 0) { // The variable has been configured cache_dir = configured_cache_dir; } else { cache_dir = xdg_cache_home ? xdg_cache_home : home_dir.c_str(); } if (cache_dir.size() == 0) { return ""; } int r = mkdir(cache_dir.c_str(), 0700); if ((r < 0) && errno != EEXIST) { return ""; } std::string keycache_dir = cache_dir + "/scitokens"; r = mkdir(keycache_dir.c_str(), 0700); if ((r < 0) && errno != EEXIST) { return ""; } std::string keycache_file = keycache_dir + "/scitokens_cpp.sqllite"; initialize_cachedb(keycache_file); return keycache_file; } // Remove a given issuer from the database. Starts a new transaction // if `new_transaction` is true. // If a failure occurs, then this function returns nonzero and closes // the database handle. int remove_issuer_entry(sqlite3 *db, const std::string &issuer, bool new_transaction) { int rc; if (new_transaction) { if ((rc = sqlite3_exec(db, "BEGIN", 0, 0, 0)) != SQLITE_OK) { sqlite3_close(db); return -1; } } sqlite3_stmt *stmt; rc = sqlite3_prepare_v2(db, "DELETE FROM keycache WHERE issuer = ?", -1, &stmt, NULL); if (rc != SQLITE_OK) { sqlite3_close(db); return -1; } if (sqlite3_bind_text(stmt, 1, issuer.c_str(), issuer.size(), SQLITE_STATIC) != SQLITE_OK) { sqlite3_finalize(stmt); sqlite3_close(db); return -1; } rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) { sqlite3_finalize(stmt); sqlite3_close(db); return -1; } sqlite3_finalize(stmt); if (new_transaction) { if ((rc = sqlite3_exec(db, "COMMIT", 0, 0, 0)) != SQLITE_OK) { sqlite3_close(db); return -1; } } return 0; } } // namespace bool scitokens::Validator::get_public_keys_from_db(const std::string issuer, int64_t now, picojson::value &keys, int64_t &next_update) { auto cache_fname = get_cache_file(); if (cache_fname.size() == 0) { return false; } sqlite3 *db; int rc = sqlite3_open(cache_fname.c_str(), &db); if (rc) { sqlite3_close(db); return false; } sqlite3_stmt *stmt; rc = sqlite3_prepare_v2(db, "SELECT keys from keycache where issuer = ?", -1, &stmt, NULL); if (rc != SQLITE_OK) { sqlite3_close(db); return false; } if (sqlite3_bind_text(stmt, 1, issuer.c_str(), issuer.size(), SQLITE_STATIC) != SQLITE_OK) { sqlite3_finalize(stmt); sqlite3_close(db); return false; } rc = sqlite3_step(stmt); if (rc == SQLITE_ROW) { const unsigned char *data = sqlite3_column_text(stmt, 0); std::string metadata(reinterpret_cast(data)); sqlite3_finalize(stmt); picojson::value json_obj; auto err = picojson::parse(json_obj, metadata); if (!err.empty() || !json_obj.is()) { if (remove_issuer_entry(db, issuer, true) != 0) { return false; } sqlite3_close(db); return false; } auto top_obj = json_obj.get(); auto iter = top_obj.find("jwks"); if (iter == top_obj.end() || !iter->second.is()) { if (remove_issuer_entry(db, issuer, true) != 0) { return false; } sqlite3_close(db); return false; } auto keys_local = iter->second; iter = top_obj.find("expires"); if (iter == top_obj.end() || !iter->second.is()) { if (remove_issuer_entry(db, issuer, true) != 0) { return false; } sqlite3_close(db); return false; } auto expiry = iter->second.get(); if (now > expiry) { if (remove_issuer_entry(db, issuer, true) != 0) { return false; } sqlite3_close(db); return false; } sqlite3_close(db); iter = top_obj.find("next_update"); if (iter == top_obj.end() || !iter->second.is()) { next_update = expiry - 4 * 3600; } else { next_update = iter->second.get(); } keys = keys_local; return true; } else if (rc == SQLITE_DONE) { sqlite3_finalize(stmt); sqlite3_close(db); return false; } else { // TODO: log error? sqlite3_finalize(stmt); sqlite3_close(db); return false; } } bool scitokens::Validator::store_public_keys(const std::string &issuer, const picojson::value &keys, int64_t next_update, int64_t expires) { picojson::object top_obj; top_obj["jwks"] = keys; top_obj["next_update"] = picojson::value(next_update); top_obj["expires"] = picojson::value(expires); picojson::value db_value(top_obj); std::string db_str = db_value.serialize(); auto cache_fname = get_cache_file(); if (cache_fname.size() == 0) { return false; } sqlite3 *db; int rc = sqlite3_open(cache_fname.c_str(), &db); if (rc) { sqlite3_close(db); return false; } if ((rc = sqlite3_exec(db, "BEGIN", 0, 0, 0)) != SQLITE_OK) { sqlite3_close(db); return false; } if (remove_issuer_entry(db, issuer, false) != 0) { return false; } sqlite3_stmt *stmt; rc = sqlite3_prepare_v2(db, "INSERT INTO keycache VALUES (?, ?)", -1, &stmt, NULL); if (rc != SQLITE_OK) { sqlite3_close(db); return false; } if (sqlite3_bind_text(stmt, 1, issuer.c_str(), issuer.size(), SQLITE_STATIC) != SQLITE_OK) { sqlite3_finalize(stmt); sqlite3_close(db); return false; } if (sqlite3_bind_text(stmt, 2, db_str.c_str(), db_str.size(), SQLITE_STATIC) != SQLITE_OK) { sqlite3_finalize(stmt); sqlite3_close(db); return false; } rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) { sqlite3_finalize(stmt); sqlite3_close(db); return false; } sqlite3_finalize(stmt); if (sqlite3_exec(db, "COMMIT", 0, 0, 0) != SQLITE_OK) { sqlite3_close(db); return false; } sqlite3_close(db); return true; } scitokens-cpp-1.2.0/src/scitokens_internal.cpp000066400000000000000000001262251511466717700215140ustar00rootroot00000000000000 #include #include #include #include #include #include #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x30000000L #include #include #endif #define EC_NAME NID_X9_62_prime256v1 #include "scitokens_internal.h" using namespace scitokens; namespace { struct CurlRaii { CurlRaii() { curl_global_init(CURL_GLOBAL_DEFAULT); } ~CurlRaii() { curl_global_cleanup(); } }; CurlRaii myCurl; std::mutex key_refresh_mutex; } // namespace namespace scitokens { namespace internal { SimpleCurlGet::GetStatus SimpleCurlGet::perform_start(const std::string &url) { m_len = 0; m_curl_multi.reset(curl_multi_init()); if (!m_curl_multi) { throw CurlException("Failed to create a new curl async handle."); } m_curl.reset(curl_easy_init()); if (!m_curl) { throw CurlException("Failed to create a new curl handle."); } if (m_maxbytes > 0) { size_t new_size = std::min(m_maxbytes, 8 * 1024); if (m_data.size() < new_size) { m_data.resize(new_size); } } long timeout = m_timeout > 120 ? 120 : m_timeout; CURLcode rv = curl_easy_setopt(m_curl.get(), CURLOPT_URL, url.c_str()); if (rv != CURLE_OK) { throw CurlException("Failed to set CURLOPT_URL."); } rv = curl_easy_setopt(m_curl.get(), CURLOPT_WRITEFUNCTION, &write_data); if (rv != CURLE_OK) { throw CurlException("Failed to set CURLOPT_WRITEFUNCTION."); } rv = curl_easy_setopt(m_curl.get(), CURLOPT_WRITEDATA, this); if (rv != CURLE_OK) { throw CurlException("Failed to set CURLOPT_WRITEDATA."); } rv = curl_easy_setopt(m_curl.get(), CURLOPT_TIMEOUT, timeout); if (rv != CURLE_OK) { throw CurlException("Failed to set CURLOPT_TIMEOUT."); } rv = curl_easy_setopt(m_curl.get(), CURLOPT_FOLLOWLOCATION, 1L); if (rv != CURLE_OK) { throw CurlException("Failed to set CURLOPT_FOLLOWLOCATION."); } // Disable signal handling to avoid issues in multi-threaded contexts. rv = curl_easy_setopt(m_curl.get(), CURLOPT_NOSIGNAL, 1L); if (rv != CURLE_OK) { throw CurlException("Failed to set CURLOPT_NOSIGNAL."); } auto ca_file = configurer::Configuration::get_tls_ca_file(); if (!ca_file.empty()) { rv = curl_easy_setopt(m_curl.get(), CURLOPT_CAINFO, ca_file.c_str()); if (rv != CURLE_OK) { throw CurlException("Failed to set CURLOPT_CAINFO."); } } { auto mres = curl_multi_add_handle(m_curl_multi.get(), m_curl.get()); if (mres) { throw CurlException("Failed to add curl handle to async object"); } } return perform_continue(); } std::string SimpleCurlGet::get_url() const { if (!m_curl) { return ""; } char *url = nullptr; auto rv = curl_easy_getinfo(m_curl.get(), CURLINFO_EFFECTIVE_URL, &url); if (rv != CURLE_OK) { return ""; } return std::string(url); } SimpleCurlGet::GetStatus SimpleCurlGet::perform_continue() { int still_running; auto resm = curl_multi_perform(m_curl_multi.get(), &still_running); if (!resm && still_running) { resm = curl_multi_timeout(m_curl_multi.get(), &m_timeout_ms); if (resm) { throw CurlException(curl_multi_strerror(resm)); } if (m_timeout_ms < 0) { m_timeout_ms = 100; } FD_ZERO(m_read_fd_set); FD_ZERO(m_write_fd_set); FD_ZERO(m_exc_fd_set); resm = curl_multi_fdset(m_curl_multi.get(), m_read_fd_set, m_write_fd_set, m_exc_fd_set, &m_max_fd); if (resm) { throw CurlException(curl_multi_strerror(resm)); } if (m_max_fd < 0) m_timeout_ms = 100; return GetStatus(); } if (resm) { throw CurlException(curl_multi_strerror(resm)); } CURLMsg *msg; CURLcode res = static_cast(-1); do { int msgq = 0; msg = curl_multi_info_read(m_curl_multi.get(), &msgq); if (msg && (msg->msg == CURLMSG_DONE)) { CURL *easy_handle = msg->easy_handle; res = msg->data.result; curl_multi_remove_handle(m_curl_multi.get(), easy_handle); } } while (msg); if (res) { throw CurlException(curl_easy_strerror(res)); } long status_code; res = curl_easy_getinfo(m_curl.get(), CURLINFO_RESPONSE_CODE, &status_code); if (res != CURLE_OK) { throw CurlException(curl_easy_strerror(res)); } GetStatus status; status.m_done = true; status.m_status_code = status_code; return status; } int SimpleCurlGet::perform(const std::string &url, time_t expiry_time) { GetStatus status = perform_start(url); while (!status.m_done) { auto now = time(NULL); int timeout_ms = 1000 * (expiry_time - now); if (timeout_ms < 0) timeout_ms = 0; if (m_timeout_ms < timeout_ms) timeout_ms = m_timeout_ms; struct timeval timeout; timeout.tv_sec = timeout_ms / 1000; timeout.tv_usec = (timeout_ms % 1000) * 1000; // Return value of select is ignored; curl will take care of it. select(m_max_fd + 1, m_read_fd_set, m_write_fd_set, m_exc_fd_set, &timeout); status = perform_continue(); } return status.m_status_code; } void SimpleCurlGet::get_data(char *&buffer, size_t &len) { buffer = &m_data[0]; len = m_len; } size_t SimpleCurlGet::write_data(void *buffer, size_t size, size_t nmemb, void *userp) { SimpleCurlGet *myself = reinterpret_cast(userp); size_t new_data = size * nmemb; size_t new_length = myself->m_len + new_data; if (myself->m_maxbytes > 0 && (new_length > static_cast(myself->m_maxbytes))) { return 0; } if (myself->m_data.size() < new_length) { myself->m_data.resize(new_length); } memcpy(&(myself->m_data[myself->m_len]), buffer, new_data); myself->m_len = new_length; return new_data; } } // namespace internal } // namespace scitokens namespace { void parse_url(const std::string &url, std::string &schema, std::string &netloc, std::string &path) { const std::string prot_end("://"); std::string::const_iterator prot_iter = std::search(url.begin(), url.end(), prot_end.begin(), prot_end.end()); schema.reserve(distance(url.begin(), prot_iter)); std::transform(url.begin(), prot_iter, std::back_inserter(schema), std::function(tolower)); if (prot_iter == url.end()) { throw InvalidIssuerException("Issuer URL missing hostname."); } std::advance(prot_iter, prot_end.length()); std::string::const_iterator path_iter = std::find(prot_iter, url.end(), '/'); netloc.reserve(std::distance(prot_iter, path_iter)); std::transform(prot_iter, path_iter, std::back_inserter(netloc), std::function(tolower)); std::string::const_iterator query_iter = std::find(path_iter, url.end(), '?'); path.assign(path_iter, query_iter); } void get_metadata_endpoint(const std::string &issuer, std::string &openid_metadata, std::string &oauth_metadata) { std::string schema, netloc, path; parse_url(issuer, schema, netloc, path); if (schema != "https") { throw InvalidIssuerException("Issuer URL must be HTTPS"); } if (path == "/") { path = ""; } std::string new_path = "/.well-known/oauth-authorization-server" + path; oauth_metadata = "https://" + netloc + new_path; openid_metadata = issuer + "/.well-known/openid-configuration"; } /* "keys": [ { "alg": "RS256", "e": "AQAB", "kid": "key-rs256", "kty": "RSA", "n": "uGDGTLXnqh3mfopjys6sFUBvFl3F4Qt6NEYphq_u_aBhtN1X9NEyb78uB_I1KjciJNGLIQU0ECsJiFx6qV1hR9xE1dPyrS3bU92AVtnBrvzUtTU-aUZAmZQiuAC_rC0-z_TOQr6qJkkUgZtxR9n9op55ZBpRfZD5dzhkW4Dm146vfTKt0D4cIMoMNJS5xQx9nibeB4E8hryZDW_fPeD0XZDcpByNyP0jFDYkxdUtQFvyRpz4WMZ4ejUfvW3gf4LRAfGZJtMnsZ7ZW4RfoQbhiXKMfWeBEjQDiXh0r-KuZLykxhYJtpf7fTnPna753IzMgRMmW3F69iQn2LQN3LoSMw==", "use": "sig" }, { "alg": "ES256", "kid": "key-es356", "kty": "EC", "use": "sig", "x": "ncSCrGTBTXXOhNiAOTwNdPjwRz1hVY4saDNiHQK9Bh4=", "y": "sCsFXvx7FAAklwq3CzRCBcghqZOFPB2dKUayS6LY_Lo=" } ] } */ picojson::value::object find_key_id(const picojson::value json, const std::string &kid) { if (!json.is()) { throw JsonException("Top-level JSON is not an object."); } auto top_obj = json.get(); auto iter = top_obj.find("keys"); if (iter == top_obj.end() || (!iter->second.is())) { throw JsonException("Metadata resource is missing 'keys' array value"); } auto keys_array = iter->second.get(); if (kid.empty()) { if (keys_array.size() != 1) { throw JsonException("Key ID empty but multiple keys published."); } auto &key = keys_array.at(0); return key.get(); } else { for (auto &key : keys_array) { if (!key.is()) { continue; } auto key_obj = key.get(); iter = key_obj.find("kid"); if (iter == key_obj.end() || (!iter->second.is())) { continue; } std::string cur_kid = iter->second.get(); if (cur_kid == kid) { return key_obj; } } throw JsonException("Key ID is not published by the issuer."); } } struct local_base64url : public jwt::alphabet::base64url { static const std::string &fill() { static std::string fill = "="; return fill; } }; // Assuming a padding, decode std::string b64url_decode_nopadding(const std::string &input) { std::string result = input; switch (result.size() % 4) { case 1: result += "="; // fallthrough case 2: result += "="; // fallthrough case 3: result += "="; // fallthrough default: break; } return jwt::base::decode(result); } // Base64-encode without padding. std::string b64url_encode_nopadding(const std::string &input) { std::string result = jwt::base::encode(input); auto pos = result.find("="); return result.substr(0, pos); } std::string es256_from_coords(const std::string &x_str, const std::string &y_str) { auto x_decode = b64url_decode_nopadding(x_str); auto y_decode = b64url_decode_nopadding(y_str); std::unique_ptr pubkey_bio( BIO_new(BIO_s_mem()), BIO_free_all); std::unique_ptr x_bignum( BN_bin2bn(reinterpret_cast(x_decode.c_str()), x_decode.size(), nullptr), BN_free); std::unique_ptr y_bignum( BN_bin2bn(reinterpret_cast(y_decode.c_str()), y_decode.size(), nullptr), BN_free); #if OPENSSL_VERSION_NUMBER >= 0x30000000L unsigned char *buf; OSSL_PARAM *params; std::unique_ptr ec_group( EC_GROUP_new_by_curve_name(EC_NAME), EC_GROUP_free); if (!ec_group.get()) { throw UnsupportedKeyException("Unable to get OpenSSL EC group"); } std::unique_ptr Q_point( EC_POINT_new(ec_group.get()), EC_POINT_free); if (!Q_point.get()) { throw UnsupportedKeyException("Unable to allocate new EC point"); } if (!EC_POINT_set_affine_coordinates(ec_group.get(), Q_point.get(), x_bignum.get(), y_bignum.get(), NULL)) { throw UnsupportedKeyException("Invalid elliptic curve point in key"); } size_t out_len = EC_POINT_point2buf(ec_group.get(), Q_point.get(), POINT_CONVERSION_UNCOMPRESSED, &buf, NULL); if (out_len == 0) { throw UnsupportedKeyException( "Failed to convert EC point to octet base buffer"); } std::unique_ptr param_build( OSSL_PARAM_BLD_new(), OSSL_PARAM_BLD_free); if (!param_build.get() || !OSSL_PARAM_BLD_push_utf8_string(param_build.get(), "group", "prime256v1", 0) || !OSSL_PARAM_BLD_push_octet_string(param_build.get(), "pub", buf, out_len) || (params = OSSL_PARAM_BLD_to_param(param_build.get())) == NULL) { throw UnsupportedKeyException( "Failed to build EC public key parameters"); } EVP_PKEY *pkey = NULL; std::unique_ptr ec_ctx( EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL), EVP_PKEY_CTX_free); if (!ec_ctx.get()) { throw UnsupportedKeyException("Failed to set EC PKEY context"); } if (EVP_PKEY_fromdata_init(ec_ctx.get()) <= 0 || EVP_PKEY_fromdata(ec_ctx.get(), &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0 || pkey == NULL) { throw UnsupportedKeyException("Failed to set the EC public key"); } if (PEM_write_bio_PUBKEY(pubkey_bio.get(), pkey) == 0) { throw UnsupportedKeyException("Failed to serialize EC public key"); } EVP_PKEY_free(pkey); OSSL_PARAM_free(params); OPENSSL_free(buf); #else std::unique_ptr ec( EC_KEY_new_by_curve_name(EC_NAME), EC_KEY_free); if (!ec.get()) { throw UnsupportedKeyException( "OpenSSL does not support the P-256 curve"); } EC_GROUP *params = (EC_GROUP *)EC_KEY_get0_group(ec.get()); if (!params) { throw UnsupportedKeyException("Unable to get OpenSSL EC group"); } std::unique_ptr Q_point( EC_POINT_new(params), EC_POINT_free); if (!Q_point.get()) { throw UnsupportedKeyException("Unable to allocate new EC point"); } if (EC_POINT_set_affine_coordinates_GFp( params, Q_point.get(), x_bignum.get(), y_bignum.get(), NULL) != 1) { throw UnsupportedKeyException("Invalid elliptic curve point in key"); } if (EC_KEY_set_public_key(ec.get(), Q_point.get()) != 1) { throw UnsupportedKeyException("Unable to set the EC public key"); } if (PEM_write_bio_EC_PUBKEY(pubkey_bio.get(), ec.get()) == 0) { throw UnsupportedKeyException("Failed to serialize EC public key"); } #endif char *mem_data; size_t mem_len = BIO_get_mem_data(pubkey_bio.get(), &mem_data); std::string result = std::string(mem_data, mem_len); return result; } std::string rs256_from_coords(const std::string &e_str, const std::string &n_str) { auto e_decode = b64url_decode_nopadding(e_str); auto n_decode = b64url_decode_nopadding(n_str); std::unique_ptr pubkey_bio( BIO_new(BIO_s_mem()), BIO_free_all); std::unique_ptr e_bignum( BN_bin2bn(reinterpret_cast(e_decode.c_str()), e_decode.size(), nullptr), BN_free); std::unique_ptr n_bignum( BN_bin2bn(reinterpret_cast(n_decode.c_str()), n_decode.size(), nullptr), BN_free); #if OPENSSL_VERSION_NUMBER >= 0x30000000L OSSL_PARAM *params; std::unique_ptr rsa_ctx( EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL), EVP_PKEY_CTX_free); if (!rsa_ctx.get()) { throw UnsupportedKeyException("Failed to set RSA PKEY context"); } std::unique_ptr param_build( OSSL_PARAM_BLD_new(), OSSL_PARAM_BLD_free); if (!param_build.get() || !OSSL_PARAM_BLD_push_BN_pad(param_build.get(), "e", e_bignum.get(), BN_num_bytes(e_bignum.get())) || !OSSL_PARAM_BLD_push_BN_pad(param_build.get(), "n", n_bignum.get(), BN_num_bytes(n_bignum.get())) || (params = OSSL_PARAM_BLD_to_param(param_build.get())) == NULL) { throw UnsupportedKeyException( "Failed to build RSA public key parameters"); } EVP_PKEY *pkey = NULL; if (EVP_PKEY_fromdata_init(rsa_ctx.get()) <= 0 || EVP_PKEY_fromdata(rsa_ctx.get(), &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0 || pkey == NULL) { throw UnsupportedKeyException("Failed to set the RSA public key"); } if (PEM_write_bio_PUBKEY(pubkey_bio.get(), pkey) == 0) { throw UnsupportedKeyException("Failed to serialize RSA public key"); } EVP_PKEY_free(pkey); OSSL_PARAM_free(params); #else std::unique_ptr rsa(RSA_new(), RSA_free); #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) rsa->e = e_bignum.get(); rsa->n = n_bignum.get(); rsa->d = nullptr; #else RSA_set0_key(rsa.get(), n_bignum.get(), e_bignum.get(), nullptr); #endif std::unique_ptr pkey(EVP_PKEY_new(), EVP_PKEY_free); if (EVP_PKEY_set1_RSA(pkey.get(), rsa.get()) != 1) { throw UnsupportedKeyException("Failed to set the public key"); } if (PEM_write_bio_PUBKEY(pubkey_bio.get(), pkey.get()) == 0) { throw UnsupportedKeyException("Failed to serialize RSA public key"); } #endif char *mem_data; size_t mem_len = BIO_get_mem_data(pubkey_bio.get(), &mem_data); std::string result = std::string(mem_data, mem_len); return result; } /** * Normalize path: collapse etc. * >>> normalize_path('/a/b///c') * '/a/b/c' */ std::string normalize_absolute_path(const std::string &path) { if ((path == "//") || (path == "/") || (path == "")) { return "/"; } std::vector path_components; auto path_iter = path.begin(); while (path_iter != path.end()) { while (*path_iter == '/') { path_iter++; } auto next_path_iter = std::find(path_iter, path.end(), '/'); std::string component; component.reserve(std::distance(path_iter, next_path_iter)); component.assign(path_iter, next_path_iter); path_components.push_back(component); path_iter = next_path_iter; } std::vector path_components_filtered; path_components_filtered.reserve(path_components.size()); for (const auto &component : path_components) { if (component == "..") { path_components_filtered.pop_back(); } else if (!component.empty() && component != ".") { path_components_filtered.push_back(component); } } std::stringstream ss; for (const auto &component : path_components_filtered) { ss << "/" << component; } std::string result = ss.str(); return result.empty() ? "/" : result; } } // namespace // static std::unordered_map json_to_claim_map(const picojson::object &json) { std::unordered_map m; for (const auto &[name, value] : json) { m.emplace(name, jwt::claim(value)); } return m; // nvro } void SciToken::deserialize(const std::string &data, const std::vector allowed_issuers) { m_decoded.reset(new jwt::decoded_jwt(data)); scitokens::Validator val; val.add_allowed_issuers(allowed_issuers); val.set_validate_all_claims_scitokens_1(false); val.set_validate_profile(m_deserialize_profile); val.verify(*m_decoded); // Set all the claims m_claims = json_to_claim_map(m_decoded->get_payload_json()); // Copy over the profile m_profile = val.get_profile(); } std::unique_ptr SciToken::deserialize_start(const std::string &data, const std::vector allowed_issuers) { m_decoded.reset(new jwt::decoded_jwt(data)); std::unique_ptr status(new SciTokenAsyncStatus()); status->m_validator.reset(new scitokens::Validator()); status->m_validator->add_allowed_issuers(allowed_issuers); status->m_validator->set_validate_all_claims_scitokens_1(false); status->m_validator->set_validate_profile(m_deserialize_profile); status->m_status = status->m_validator->verify_async(*m_decoded); return deserialize_continue(std::move(status)); } std::unique_ptr SciToken::deserialize_continue(std::unique_ptr status) { // Check if the status is completed (verification is complete) if (status->m_status->m_done) { // Set all the claims m_claims = json_to_claim_map(m_decoded->get_payload_json()); // Copy over the profile m_profile = status->m_validator->get_profile(); } else { status->m_status = status->m_validator->verify_async_continue( std::move(status->m_status)); if (status->m_status->m_done) { // Set all the claims m_claims = json_to_claim_map(m_decoded->get_payload_json()); // Copy over the profile m_profile = status->m_validator->get_profile(); } } return std::move(status); } std::unique_ptr Validator::get_public_keys_from_web(const std::string &issuer, unsigned timeout) { std::string openid_metadata, oauth_metadata; get_metadata_endpoint(issuer, openid_metadata, oauth_metadata); std::unique_ptr status(new AsyncStatus()); status->m_oauth_metadata_url = oauth_metadata; status->m_cget.reset(new internal::SimpleCurlGet(1024 * 1024, timeout)); auto cget_status = status->m_cget->perform_start(openid_metadata); status->m_continue_fetch = true; if (!cget_status.m_done) { return status; } return get_public_keys_from_web_continue(std::move(status)); } std::unique_ptr Validator::get_public_keys_from_web_continue( std::unique_ptr status) { char *buffer; size_t len; switch (status->m_state) { case AsyncStatus::DOWNLOAD_METADATA: { auto cget_status = status->m_cget->perform_continue(); if (!cget_status.m_done) { return std::move(status); } if (cget_status.m_status_code != 200) { if (status->m_oauth_fallback) { throw CurlException("Failed to retrieve metadata provider " "information for issuer."); } else { status->m_oauth_fallback = true; status->m_cget.reset(new internal::SimpleCurlGet()); cget_status = status->m_cget->perform_start(status->m_oauth_metadata_url); if (!cget_status.m_done) { return std::move(status); } return get_public_keys_from_web_continue(std::move(status)); } } status->m_cget->get_data(buffer, len); std::string metadata(buffer, len); picojson::value json_obj; auto err = picojson::parse(json_obj, metadata); if (!err.empty()) { throw JsonException( "JSON parse failure when downloading from the metadata URL " + status->m_cget->get_url() + ": " + err); } if (!json_obj.is()) { throw JsonException("Metadata resource " + status->m_cget->get_url() + " contains " "improperly-formatted JSON."); } auto top_obj = json_obj.get(); auto iter = top_obj.find("jwks_uri"); if (iter == top_obj.end() || (!iter->second.is())) { throw JsonException("Metadata resource " + status->m_cget->get_url() + " is missing 'jwks_uri' string value"); } auto jwks_uri = iter->second.get(); status->m_has_metadata = true; status->m_state = AsyncStatus::DOWNLOAD_PUBLIC_KEY; status->m_cget.reset(new internal::SimpleCurlGet()); status->m_cget->perform_start(jwks_uri); // This should also fall through the next state } case AsyncStatus::DOWNLOAD_PUBLIC_KEY: { auto cget_status = status->m_cget->perform_continue(); if (!cget_status.m_done) { return std::move(status); } if (cget_status.m_status_code != 200) { throw CurlException("Failed to retrieve the issuer's key set"); } status->m_cget->get_data(buffer, len); auto metadata = std::string(buffer, len); picojson::value json_obj; auto err = picojson::parse(json_obj, metadata); if (!err.empty()) { throw JsonException("JSON parse failure when downloading from the " " public key URL " + status->m_cget->get_url() + ": " + err); } status->m_cget.reset(); auto now = std::time(NULL); // TODO: take expiration time from the cache-control header in the // response. int next_update_delta = configurer::Configuration::get_next_update_delta(); int expiry_delta = configurer::Configuration::get_expiry_delta(); status->m_next_update = now + next_update_delta; status->m_expires = now + expiry_delta; status->m_keys = json_obj; status->m_continue_fetch = false; status->m_done = true; status->m_state = AsyncStatus::DONE; } case AsyncStatus::DONE: status->m_done = true; } // Switch return std::move(status); } std::string Validator::get_jwks(const std::string &issuer) { auto now = std::time(NULL); picojson::value jwks; int64_t next_update; if (get_public_keys_from_db(issuer, now, jwks, next_update)) { return jwks.serialize(); } return std::string("{\"keys\": []}"); } bool Validator::refresh_jwks(const std::string &issuer) { picojson::value keys; std::unique_ptr status = get_public_keys_from_web( issuer, internal::SimpleCurlGet::extended_timeout); while (!status->m_done) { status = get_public_keys_from_web_continue(std::move(status)); } return store_public_keys(issuer, status->m_keys, status->m_next_update, status->m_expires); } bool Validator::store_jwks(const std::string &issuer, const std::string &jwks_str) { picojson::value jwks; std::string err = picojson::parse(jwks, jwks_str); auto now = std::time(NULL); int next_update_delta = configurer::Configuration::get_next_update_delta(); int expiry_delta = configurer::Configuration::get_expiry_delta(); int64_t next_update = now + next_update_delta, expires = now + expiry_delta; if (!err.empty()) { throw JsonException(err); } return store_public_keys(issuer, jwks, next_update, expires); } std::unique_ptr Validator::get_public_key_pem(const std::string &issuer, const std::string &kid, std::string &public_pem, std::string &algorithm) { auto now = std::time(NULL); std::unique_ptr result(new AsyncStatus()); if (get_public_keys_from_db(issuer, now, result->m_keys, result->m_next_update)) { std::unique_lock lock(key_refresh_mutex, std::defer_lock); // If refresh is due *and* the key refresh mutex is free, try to update if (now > result->m_next_update && lock.try_lock()) { try { result->m_ignore_error = true; result = get_public_keys_from_web( issuer, internal::SimpleCurlGet::default_timeout); // Hold refresh mutex in the new result result->m_refresh_lock = std::move(lock); } catch (std::runtime_error &) { result->m_do_store = false; // ignore the exception: we have a valid set of keys already } } else { // Got the keys from the DB, and they are still valid. result->m_continue_fetch = false; result->m_do_store = false; result->m_done = true; } } else { // No keys in the DB, or they are expired, so get them from the web. result = get_public_keys_from_web( issuer, internal::SimpleCurlGet::default_timeout); } result->m_issuer = issuer; result->m_kid = kid; // Always call the continue because it formats the public_pem and algorithm return get_public_key_pem_continue(std::move(result), public_pem, algorithm); } std::unique_ptr Validator::get_public_key_pem_continue(std::unique_ptr status, std::string &public_pem, std::string &algorithm) { if (status->m_continue_fetch) { status = get_public_keys_from_web_continue(std::move(status)); if (status->m_continue_fetch) { return std::move(status); } } if (status->m_do_store) { store_public_keys(status->m_issuer, status->m_keys, status->m_next_update, status->m_expires); } status->m_done = true; auto key_obj = find_key_id(status->m_keys, status->m_kid); auto iter = key_obj.find("alg"); std::string alg; if (iter == key_obj.end() || (!iter->second.is())) { auto iter2 = key_obj.find("kty"); if (iter2 == key_obj.end() || !iter2->second.is()) { throw JsonException("Key is missing key type"); } else { auto kty = iter2->second.get(); if (kty == "RSA") { alg = "RS256"; } else if (kty == "EC") { auto iter3 = key_obj.find("crv"); if (iter3 == key_obj.end() || !iter3->second.is()) { throw JsonException("EC key is missing curve name"); } auto crv = iter3->second.get(); if (crv == "P-256") { alg = "ES256"; } else { throw JsonException("Unsupported EC curve in public key"); } } else { throw JsonException("Unknown public key type"); } } } else { alg = iter->second.get(); } if (alg != "RS256" and alg != "ES256") { throw UnsupportedKeyException( "Issuer is using an unsupported algorithm"); } std::string pem; if (alg == "ES256") { iter = key_obj.find("x"); if (iter == key_obj.end() || (!iter->second.is())) { throw JsonException("Elliptic curve is missing x-coordinate"); } auto x = iter->second.get(); iter = key_obj.find("y"); if (iter == key_obj.end() || (!iter->second.is())) { throw JsonException("Elliptic curve is missing y-coordinate"); } auto y = iter->second.get(); pem = es256_from_coords(x, y); } else { iter = key_obj.find("e"); if (iter == key_obj.end() || (!iter->second.is())) { throw JsonException("Public key is missing exponent"); } auto e = iter->second.get(); iter = key_obj.find("n"); if (iter == key_obj.end() || (!iter->second.is())) { throw JsonException("Public key is missing n-value"); } auto n = iter->second.get(); pem = rs256_from_coords(e, n); } public_pem = pem; algorithm = alg; return std::move(status); } bool scitokens::Validator::store_public_ec_key(const std::string &issuer, const std::string &keyid, const std::string &public_key) { std::unique_ptr pubkey_bio( BIO_new(BIO_s_mem()), BIO_free_all); if ((size_t)BIO_write(pubkey_bio.get(), public_key.data(), public_key.size()) != public_key.size()) { return false; } std::unique_ptr x_bignum(BN_new(), BN_free); std::unique_ptr y_bignum(BN_new(), BN_free); #if OPENSSL_VERSION_NUMBER >= 0x30000000L std::unique_ptr pkey( PEM_read_bio_PUBKEY(pubkey_bio.get(), nullptr, nullptr, nullptr), EVP_PKEY_free); if (!pkey.get()) { return false; } std::unique_ptr ec_group( EC_GROUP_new_by_curve_name(EC_NAME), EC_GROUP_free); if (!ec_group.get()) { throw UnsupportedKeyException("Unable to get OpenSSL EC group"); } std::unique_ptr q_point( EC_POINT_new(ec_group.get()), EC_POINT_free); if (!q_point.get()) { throw UnsupportedKeyException("Unable to get OpenSSL EC point"); } OSSL_PARAM *params; if (!EVP_PKEY_todata(pkey.get(), EVP_PKEY_PUBLIC_KEY, ¶ms)) { throw UnsupportedKeyException( "Unable to get OpenSSL public key parameters"); } const void *buf = NULL; size_t buf_len; OSSL_PARAM *p = OSSL_PARAM_locate(params, "pub"); if (!p || !OSSL_PARAM_get_octet_string_ptr(p, &buf, &buf_len) || !EC_POINT_oct2point(ec_group.get(), q_point.get(), static_cast(buf), buf_len, nullptr)) { throw UnsupportedKeyException( "Failed to to set OpenSSL EC point with public key information"); } if (!EC_POINT_get_affine_coordinates(ec_group.get(), q_point.get(), x_bignum.get(), y_bignum.get(), NULL)) { throw UnsupportedKeyException( "Unable to get OpenSSL affine coordinates"); } OSSL_PARAM_free(params); #else std::unique_ptr pkey( PEM_read_bio_EC_PUBKEY(pubkey_bio.get(), nullptr, nullptr, nullptr), EC_KEY_free); if (!pkey) { return false; } EC_GROUP *params = (EC_GROUP *)EC_KEY_get0_group(pkey.get()); if (!params) { throw UnsupportedKeyException("Unable to get OpenSSL EC group"); } const EC_POINT *point = EC_KEY_get0_public_key(pkey.get()); if (!point) { throw UnsupportedKeyException("Unable to get OpenSSL EC point"); } if (!EC_POINT_get_affine_coordinates_GFp(params, point, x_bignum.get(), y_bignum.get(), nullptr)) { throw UnsupportedKeyException( "Unable to get OpenSSL affine coordinates"); } #endif auto x_num = BN_num_bytes(x_bignum.get()); auto y_num = BN_num_bytes(y_bignum.get()); std::vector x_bin; x_bin.resize(x_num); std::vector y_bin; y_bin.resize(y_num); BN_bn2bin(x_bignum.get(), &x_bin[0]); BN_bn2bin(y_bignum.get(), &y_bin[0]); std::string x_str(reinterpret_cast(&x_bin[0]), x_num); std::string y_str(reinterpret_cast(&y_bin[0]), y_num); picojson::object key_obj; key_obj["alg"] = picojson::value("ES256"); key_obj["kid"] = picojson::value(keyid); key_obj["use"] = picojson::value("sig"); key_obj["kty"] = picojson::value("EC"); key_obj["x"] = picojson::value(b64url_encode_nopadding(x_str)); key_obj["y"] = picojson::value(b64url_encode_nopadding(y_str)); std::vector key_list; key_list.emplace_back(key_obj); picojson::object top_obj; top_obj["keys"] = picojson::value(key_list); picojson::value top_value(top_obj); auto now = std::time(NULL); int next_update_delta = configurer::Configuration::get_next_update_delta(); int expiry_delta = configurer::Configuration::get_expiry_delta(); return store_public_keys(issuer, top_value, now + next_update_delta, now + expiry_delta); } bool scitokens::Enforcer::scope_validator(const jwt::claim &claim, void *myself) { auto me = reinterpret_cast(myself); if (claim.get_type() != jwt::json::type::string) { return false; } std::string scope = claim.as_string(); std::string requested_path = normalize_absolute_path(me->m_test_path); auto scope_iter = scope.begin(); // std::cout << "Comparing scope " << scope << " against test accesses " << // me->m_test_authz << ":" << requested_path << std::endl; bool compat_modify = false, compat_create = false, compat_cancel = false; while (scope_iter != scope.end()) { while (*scope_iter == ' ') { scope_iter++; } auto next_scope_iter = std::find(scope_iter, scope.end(), ' '); std::string full_authz; full_authz.reserve(std::distance(scope_iter, next_scope_iter)); full_authz.assign(scope_iter, next_scope_iter); auto sep_iter = full_authz.find(':'); std::string authz = full_authz.substr(0, sep_iter); std::string path; if (sep_iter == std::string::npos) { path = "/"; } else { path = full_authz.substr((++sep_iter)); } path = normalize_absolute_path(path); // If we are in compatibility mode and this is a WLCG token, then // translate the authorization names to utilize the SciToken-style // names. std::string alt_authz; if (me->m_validate_profile == SciToken::Profile::COMPAT && me->m_validator.get_profile() == SciToken::Profile::WLCG_1_0) { if (authz == "storage.read") { authz = "read"; } else if (authz == "storage.create") { authz = "write"; alt_authz = "create"; } else if (authz == "storage.modify") { authz = "write"; alt_authz = "modify"; } else if (authz == "compute.read") { authz = "condor"; path = "/READ"; } else if (authz == "compute.modify") { compat_modify = true; } else if (authz == "compute.create") { compat_create = true; } else if (authz == "compute.cancel") { compat_cancel = true; } } if (me->m_test_authz.empty()) { me->m_gen_acls.emplace_back(authz, path); if (!alt_authz.empty()) me->m_gen_acls.emplace_back(alt_authz, path); } else if (((me->m_test_authz == authz) || (!alt_authz.empty() && (me->m_test_authz == alt_authz))) && (requested_path.substr(0, path.size()) == path)) { return true; } scope_iter = next_scope_iter; } // Compatibility mode: the combination on compute modify, create, and cancel // mode are equivalent to the condor:/WRITE authorization. if (compat_modify && compat_create && compat_cancel) { if (me->m_test_authz.empty()) { me->m_gen_acls.emplace_back("condor", "/WRITE"); } else if ((me->m_test_authz == "condor") && (requested_path.substr(0, 6) == "/WRITE")) { return true; } } return me->m_test_authz.empty(); } // Configuration class functions std::pair configurer::Configuration::set_cache_home(const std::string dir_path) { // If setting to "", then we should treat as though it is unsetting the // config if (dir_path.length() == 0) { // User is configuring to empty string m_cache_home = std::make_shared(dir_path); return std::make_pair(true, ""); } std::vector path_components = path_split(dir_path); // cleans any extraneous /'s std::string cleaned_dir_path; for (const auto &component : path_components) { // add the / back to the path components cleaned_dir_path += "/" + component; } // Check that the cache_home exists, and if not try to create it auto rp = mkdir_and_parents_if_needed( cleaned_dir_path); // Structured bindings not introduced until cpp 17 if (!rp.first) { // std::string err_prefix{ "An issue was encountered with the provided cache home path: "}; return std::make_pair(false, err_prefix + rp.second); } // Now it exists and we can write to it, set the value and let // scitokens_cache handle the rest m_cache_home = std::make_shared(cleaned_dir_path); return std::make_pair(true, ""); } void configurer::Configuration::set_tls_ca_file(const std::string ca_file) { m_tls_ca_file = std::make_shared(ca_file); } std::string configurer::Configuration::get_cache_home() { return *m_cache_home; } std::string configurer::Configuration::get_tls_ca_file() { return *m_tls_ca_file; } // bool configurer::Configuration::check_dir(const std::string dir_path) { // struct stat info; // return stat(dir_path.c_str(), &info) == 0 && (info.st_mode & S_IFDIR); // } std::pair configurer::Configuration::mkdir_and_parents_if_needed( const std::string dir_path) { // SciTokens-cpp already makes assumptions about using Linux file paths, // so making that assumption here as well. // Using these perms because that's what the actual cache file uses in // scitokens_cache mode_t mode = 0700; // Maybe these permissions should be configurable? int result; std::string currentLevel; std::vector path_components = path_split(dir_path); for (const auto &component : path_components) { currentLevel += "/" + component; result = mkdir(currentLevel.c_str(), mode); if ((result < 0) && errno != EEXIST) { std::string err_prefix{"There was an error while creating/checking " "the directory: mkdir error: "}; return std::make_pair(false, err_prefix + strerror(errno)); } } return std::make_pair(true, ""); } std::vector configurer::Configuration::path_split(std::string path) { std::vector path_components; std::stringstream ss(path); std::string component; while (std::getline(ss, component, '/')) { if (!component.empty()) { path_components.push_back(component); } } if (path_components[0] == "") { path_components.erase(path_components.begin()); } return path_components; } scitokens-cpp-1.2.0/src/scitokens_internal.h000066400000000000000000001036541511466717700211620ustar00rootroot00000000000000 #include #include #include #include #include #include #include #include #if defined(__GNUC__) #define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #else #define WARN_UNUSED_RESULT #endif namespace { struct FixedClock { jwt::date m_now; jwt::date now() const { return m_now; } }; } // namespace namespace jwt { template class decoded_jwt; namespace traits { struct kazuho_picojson; } } // namespace jwt namespace configurer { class Configuration { public: Configuration() {} static void set_next_update_delta(int _next_update_delta) { m_next_update_delta = _next_update_delta; } static int get_next_update_delta() { return m_next_update_delta; } static void set_expiry_delta(int _expiry_delta) { m_expiry_delta = _expiry_delta; } static int get_expiry_delta() { return m_expiry_delta; } static std::pair set_cache_home(const std::string cache_home); static std::string get_cache_home(); static void set_tls_ca_file(const std::string ca_file); static std::string get_tls_ca_file(); private: static std::atomic_int m_next_update_delta; static std::atomic_int m_expiry_delta; static std::shared_ptr m_cache_home; static std::shared_ptr m_tls_ca_file; // static bool check_dir(const std::string dir_path); static std::pair mkdir_and_parents_if_needed(const std::string dir_path); static std::vector path_split(const std::string dir_path); }; } // namespace configurer namespace scitokens { namespace internal { class SimpleCurlGet { int m_maxbytes{1048576}; unsigned m_timeout; std::vector m_data; size_t m_len{0}; std::unique_ptr m_curl; std::unique_ptr m_curl_multi; fd_set m_read_fd_set[FD_SETSIZE]; fd_set m_write_fd_set[FD_SETSIZE]; fd_set m_exc_fd_set[FD_SETSIZE]; int m_max_fd{-1}; long m_timeout_ms{0}; public: static const unsigned default_timeout = 4; static const unsigned extended_timeout = 30; SimpleCurlGet(int maxbytes = 1024 * 1024, unsigned timeout = 30) : m_maxbytes(maxbytes), m_timeout(timeout), m_curl(nullptr, &curl_easy_cleanup), m_curl_multi(nullptr, &curl_multi_cleanup) {} struct GetStatus { bool m_done{false}; int m_status_code{-1}; }; GetStatus perform_start(const std::string &url); GetStatus perform_continue(); int perform(const std::string &url, time_t expiry_time); void get_data(char *&buffer, size_t &len); std::string get_url() const; long get_timeout_ms() const { return m_timeout_ms; } int get_max_fd() const { return m_max_fd; } fd_set *get_read_fd_set() { return m_read_fd_set; } fd_set *get_write_fd_set() { return m_write_fd_set; } fd_set *get_exc_fd_set() { return m_exc_fd_set; } private: static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp); }; } // namespace internal class UnsupportedKeyException : public std::runtime_error { public: explicit UnsupportedKeyException(const std::string &msg) : std::runtime_error(msg) {} }; class JWTVerificationException : public std::runtime_error { public: explicit JWTVerificationException(const std::string &msg) : std::runtime_error("token verification failed: " + msg) {} }; class CurlException : public std::runtime_error { public: explicit CurlException(const std::string &msg) : std::runtime_error(msg) {} }; class MissingIssuerException : public std::runtime_error { public: MissingIssuerException() : std::runtime_error("Issuer not specified in claims") {} }; class InvalidIssuerException : public std::runtime_error { public: InvalidIssuerException(const std::string &msg) : std::runtime_error(msg) {} }; class JsonException : public std::runtime_error { public: JsonException(const std::string &msg) : std::runtime_error(msg) {} }; class SciTokenKey { public: SciTokenKey() : m_kid("none"), m_name("none") {} SciTokenKey(const std::string &key_id, const std::string &algorithm, const std::string &public_contents, const std::string &private_contents) : m_kid(key_id), m_name(algorithm), m_public(public_contents), m_private(private_contents) {} std::string serialize(jwt::builder &builder) { if (m_kid != "none") { builder.set_key_id(m_kid); } return builder.sign(*this); } std::string sign(const std::string &data, std::error_code &ec) const { if (m_name == "RS256") { return jwt::algorithm::rs256(m_public, m_private).sign(data, ec); } else if (m_name == "ES256") { return jwt::algorithm::es256(m_public, m_private).sign(data, ec); } throw UnsupportedKeyException( "Provided algorithm name is not supported"); } std::string name() const { return m_name; } void verify(const std::string &data, const std::string &signature, std::error_code &ec) const { if (m_name == "RS256") { jwt::algorithm::rs256(m_public, m_private) .verify(data, signature, ec); } else if (m_name == "ES256") { jwt::algorithm::es256(m_public, m_private) .verify(data, signature, ec); } else { throw UnsupportedKeyException( "Provided algorithm is not supported."); } } private: std::string m_kid; std::string m_name; std::string m_public; std::string m_private; }; class Validator; class AsyncStatus { public: AsyncStatus() = default; AsyncStatus(const AsyncStatus &) = delete; AsyncStatus &operator=(const AsyncStatus &) = delete; enum AsyncState { DOWNLOAD_METADATA, DOWNLOAD_PUBLIC_KEY, DONE }; bool m_done{false}; bool m_continue_fetch{false}; bool m_ignore_error{false}; bool m_do_store{true}; bool m_has_metadata{false}; bool m_oauth_fallback{false}; AsyncState m_state{DOWNLOAD_METADATA}; std::unique_lock m_refresh_lock; int64_t m_next_update{-1}; int64_t m_expires{-1}; picojson::value m_keys; std::string m_issuer; std::string m_kid; std::string m_oauth_metadata_url; std::unique_ptr m_cget; std::string m_jwt_string; std::string m_public_pem; std::string m_algorithm; struct timeval get_timeout_val(time_t expiry_time) const { auto now = time(NULL); long timeout_ms = 100 * (expiry_time - now); if (m_cget && (m_cget->get_timeout_ms() < timeout_ms)) timeout_ms = m_cget->get_timeout_ms(); struct timeval timeout; timeout.tv_sec = timeout_ms / 1000; timeout.tv_usec = (timeout_ms % 1000) * 1000; return timeout; } int get_max_fd() const { return m_cget ? m_cget->get_max_fd() : -1; } fd_set *get_read_fd_set() { return m_cget ? m_cget->get_read_fd_set() : nullptr; } fd_set *get_write_fd_set() { return m_cget ? m_cget->get_write_fd_set() : nullptr; } fd_set *get_exc_fd_set() { return m_cget ? m_cget->get_exc_fd_set() : nullptr; } }; class SciTokenAsyncStatus { public: SciTokenAsyncStatus() = default; SciTokenAsyncStatus(const SciTokenAsyncStatus &) = delete; SciTokenAsyncStatus &operator=(const SciTokenAsyncStatus &) = delete; std::unique_ptr m_validator; std::unique_ptr m_status; }; class SciToken { friend class scitokens::Validator; public: enum class Profile { COMPAT = 0, SCITOKENS_1_0, SCITOKENS_2_0, WLCG_1_0, AT_JWT }; SciToken(SciTokenKey &signing_algorithm) : m_key(signing_algorithm) {} void set_claim(const std::string &key, const jwt::claim &value) { m_claims[key] = value; if (key == "iss") { m_issuer_set = true; } } void set_serialize_mode(Profile profile) { m_serialize_profile = profile; } void set_deserialize_mode(Profile profile) { m_deserialize_profile = profile; } const jwt::claim get_claim(const std::string &key) { return m_claims[key]; } bool has_claim(const std::string &key) const { return m_claims.find(key) != m_claims.end(); } void set_claim_list(const std::string &claim, std::vector &claim_list) { picojson::array array; array.reserve(claim_list.size()); for (const auto &entry : claim_list) { array.emplace_back(entry); } m_claims[claim] = jwt::claim(picojson::value(array)); } // Return a claim as a string // If the claim is not a string, it can throw // a std::bad_cast() exception. const std::string get_claim_string(const std::string &key) { return m_claims[key].as_string(); } const std::vector get_claim_list(const std::string &key) { picojson::array array; try { array = m_claims[key].as_array(); } catch (std::bad_cast &) { throw JsonException("Claim's value is not a JSON list"); } std::vector result; for (const auto &value : array) { result.emplace_back(value.get()); } return result; } void set_lifetime(int lifetime) { m_lifetime = lifetime; } std::string serialize() { auto builder(jwt::create()); if (!m_issuer_set) { throw MissingIssuerException(); } auto time = std::chrono::system_clock::now(); builder.set_issued_at(time); builder.set_not_before(time); builder.set_expires_at(time + std::chrono::seconds(m_lifetime)); if (m_serialize_profile == Profile::AT_JWT) { builder.set_type("at+jwt"); } uuid_t uuid; uuid_generate(uuid); char uuid_str[37]; uuid_unparse_lower(uuid, uuid_str); m_claims["jti"] = jwt::claim(std::string(uuid_str)); if (m_serialize_profile == Profile::SCITOKENS_2_0) { m_claims["ver"] = jwt::claim(std::string("scitoken:2.0")); auto iter = m_claims.find("aud"); if (iter == m_claims.end()) { m_claims["aud"] = jwt::claim(std::string("ANY")); } } else if (m_serialize_profile == Profile::WLCG_1_0) { m_claims["wlcg.ver"] = jwt::claim(std::string("1.0")); auto iter = m_claims.find("aud"); if (iter == m_claims.end()) { m_claims["aud"] = jwt::claim(std::string("https://wlcg.cern.ch/jwt/v1/any")); } } // Set all the payload claims for (auto it : m_claims) { builder.set_payload_claim(it.first, it.second); } return m_key.serialize(builder); } void deserialize(const std::string &data, std::vector allowed_issuers = {}); std::unique_ptr deserialize_start(const std::string &data, std::vector allowed_issuers = {}); std::unique_ptr deserialize_continue(std::unique_ptr status); private: bool m_issuer_set{false}; int m_lifetime{600}; Profile m_profile{Profile::SCITOKENS_1_0}; Profile m_serialize_profile{Profile::COMPAT}; Profile m_deserialize_profile{Profile::COMPAT}; std::unordered_map m_claims; std::unique_ptr> m_decoded; SciTokenKey &m_key; }; class Validator { typedef int (*StringValidatorFunction)(const char *value, char **err_msg); typedef bool (*ClaimValidatorFunction)(const jwt::claim &claim_value, void *data); typedef std::map> ClaimStringValidatorMap; typedef std::map>> ClaimValidatorMap; public: Validator() : m_now(std::chrono::system_clock::now()) {} void set_now(std::chrono::system_clock::time_point now) { m_now = now; } std::unique_ptr verify_async(const SciToken &scitoken) { const jwt::decoded_jwt *jwt_decoded = scitoken.m_decoded.get(); if (!jwt_decoded) { throw JWTVerificationException( "Token is not deserialized from string."); } return verify_async(*jwt_decoded); } void verify(const SciToken &scitoken, time_t expiry_time) { auto result = verify_async(scitoken); while (!result->m_done) { auto timeout_val = result->get_timeout_val(expiry_time); select(result->get_max_fd() + 1, result->get_read_fd_set(), result->get_write_fd_set(), result->get_exc_fd_set(), &timeout_val); if (time(NULL) >= expiry_time) { throw CurlException("Timeout when loading the OIDC metadata."); } result = verify_async_continue(std::move(result)); } } void verify(const jwt::decoded_jwt &jwt) { auto result = verify_async(jwt); while (!result->m_done) { result = verify_async_continue(std::move(result)); } } std::unique_ptr verify_async(const jwt::decoded_jwt &jwt) { // If token has a typ header claim (RFC8725 Section 3.11), trust that in // COMPAT mode. if (jwt.has_type()) { std::string t_type = jwt.get_type(); if (m_validate_profile == SciToken::Profile::COMPAT) { if (t_type == "at+jwt" || t_type == "application/at+jwt") { m_profile = SciToken::Profile::AT_JWT; } } else if (m_validate_profile == SciToken::Profile::AT_JWT) { if (t_type != "at+jwt" && t_type != "application/at+jwt") { throw JWTVerificationException( "'typ' header claim must be at+jwt"); } m_profile = SciToken::Profile::AT_JWT; } } else { if (m_validate_profile == SciToken::Profile::AT_JWT) { throw JWTVerificationException( "'typ' header claim must be set for at+jwt tokens"); } } if (!jwt.has_payload_claim("iat")) { throw JWTVerificationException("'iat' claim is mandatory"); } if (m_profile == SciToken::Profile::SCITOKENS_1_0 || m_profile == SciToken::Profile::SCITOKENS_2_0) { if (!jwt.has_payload_claim("nbf")) { throw JWTVerificationException("'nbf' claim is mandatory"); } } if (!jwt.has_payload_claim("exp")) { throw JWTVerificationException("'exp' claim is mandatory"); } if (!jwt.has_payload_claim("iss")) { throw JWTVerificationException("'iss' claim is mandatory"); } if (!m_allowed_issuers.empty()) { std::string issuer = jwt.get_issuer(); bool permitted = false; for (const auto &allowed_issuer : m_allowed_issuers) { if (issuer == allowed_issuer) { permitted = true; break; } } if (!permitted) { std::string safe_issuer = format_issuer_for_error(jwt); throw JWTVerificationException( "Token issuer " + safe_issuer + " is not in list of allowed issuers."); } } for (const auto &claim : m_critical_claims) { if (!jwt.has_payload_claim(claim)) { std::stringstream ss; ss << "'" << claim << "' claim is mandatory"; throw JWTVerificationException(ss.str()); } } std::string public_pem; std::string algorithm; // Key id is optional in the RFC, set to blank if it doesn't exist std::string key_id; if (jwt.has_key_id()) { key_id = jwt.get_key_id(); } auto status = get_public_key_pem(jwt.get_issuer(), key_id, public_pem, algorithm); status->m_jwt_string = jwt.get_token(); status->m_public_pem = public_pem; status->m_algorithm = algorithm; return verify_async_continue(std::move(status)); } std::unique_ptr verify_async_continue(std::unique_ptr status) { if (!status->m_done) { std::string public_pem, algorithm; status = get_public_key_pem_continue(std::move(status), public_pem, algorithm); status->m_public_pem = public_pem; status->m_algorithm = algorithm; if (!status->m_done) { return std::move(status); } } // std::cout << "Public PEM: " << public_pem << std::endl << "Algorithm: // " << algorithm << std::endl; SciTokenKey key(status->m_kid, status->m_algorithm, status->m_public_pem, ""); auto verifier = jwt::verify({m_now}) .allow_algorithm(key); const jwt::decoded_jwt jwt( status->m_jwt_string); verifier.verify(jwt); bool must_verify_everything = true; if (jwt.has_payload_claim("ver")) { const jwt::claim &claim = jwt.get_payload_claim("ver"); if (claim.get_type() != jwt::json::type::string) { throw JWTVerificationException( "'ver' claim value must be a string (if present)"); } std::string ver_string = claim.as_string(); if ((ver_string == "scitokens:2.0") || (ver_string == "scitoken:2.0")) { must_verify_everything = false; if ((m_validate_profile != SciToken::Profile::COMPAT) && (m_validate_profile != SciToken::Profile::SCITOKENS_2_0)) { throw JWTVerificationException( "Invalidate token type; not expecting a SciToken 2.0."); } m_profile = SciToken::Profile::SCITOKENS_2_0; if (!jwt.has_payload_claim("aud")) { throw JWTVerificationException( "'aud' claim required for SciTokens 2.0 profile"); } } else if (ver_string == "scitokens:1.0") { must_verify_everything = m_validate_all_claims; if ((m_validate_profile != SciToken::Profile::COMPAT) && (m_validate_profile != SciToken::Profile::SCITOKENS_1_0)) { throw JWTVerificationException( "Invalidate token type; not expecting a SciToken 1.0."); } m_profile = SciToken::Profile::SCITOKENS_1_0; } else { std::stringstream ss; ss << "Unknown profile version in token: " << ver_string; throw JWTVerificationException(ss.str()); } // Handle WLCG common JWT profile. } else if (jwt.has_payload_claim("wlcg.ver")) { if ((m_validate_profile != SciToken::Profile::COMPAT) && (m_validate_profile != SciToken::Profile::WLCG_1_0)) { throw JWTVerificationException( "Invalidate token type; not expecting a WLCG 1.0."); } m_profile = SciToken::Profile::WLCG_1_0; must_verify_everything = false; const jwt::claim &claim = jwt.get_payload_claim("wlcg.ver"); if (claim.get_type() != jwt::json::type::string) { throw JWTVerificationException( "'ver' claim value must be a string (if present)"); } std::string ver_string = claim.as_string(); if (ver_string != "1.0") { std::stringstream ss; ss << "Unknown WLCG profile version in token: " << ver_string; throw JWTVerificationException(ss.str()); } if (!jwt.has_payload_claim("aud")) { throw JWTVerificationException( "Malformed token: 'aud' claim required for WLCG profile"); } } else if (m_profile == SciToken::Profile::AT_JWT) { // detected early above from typ header claim. must_verify_everything = false; } else { if ((m_validate_profile != SciToken::Profile::COMPAT) && (m_validate_profile != SciToken::Profile::SCITOKENS_1_0)) { throw JWTVerificationException( "Invalidate token type; not expecting a SciToken 1.0."); } m_profile = SciToken::Profile::SCITOKENS_1_0; must_verify_everything = m_validate_all_claims; } auto claims = jwt.get_payload_json(); for (const auto &claim_pair : claims) { if (claim_pair.first == "iat" || claim_pair.first == "nbf" || claim_pair.first == "exp" || claim_pair.first == "ver") { continue; } auto iter = m_validators.find(claim_pair.first); auto iter_claim = m_claim_validators.find(claim_pair.first); if ((iter == m_validators.end() || iter->second.empty()) && (iter_claim == m_claim_validators.end() || iter_claim->second.empty())) { bool is_issuer = claim_pair.first == "iss"; if (is_issuer && !m_allowed_issuers.empty()) { // skip; we verified it above } else if (must_verify_everything) { std::stringstream ss; ss << "'" << claim_pair.first << "' claim verification is mandatory"; // std::cout << ss.str() << std::endl; throw JWTVerificationException(ss.str()); } } // std::cout << "Running claim " << claim_pair.first << " through // validation." << std::endl; if (iter != m_validators.end()) for (const auto &verification_func : iter->second) { const jwt::claim &claim = jwt.get_payload_claim(claim_pair.first); if (claim.get_type() != jwt::json::type::string) { std::stringstream ss; ss << "'" << claim_pair.first << "' claim value must be a string to verify."; throw JWTVerificationException(ss.str()); } std::string value = claim.as_string(); char *err_msg = nullptr; if (verification_func(value.c_str(), &err_msg)) { if (err_msg) { throw JWTVerificationException(err_msg); } else { std::stringstream ss; ss << "'" << claim_pair.first << "' claim verification failed."; throw JWTVerificationException(ss.str()); } } } if (iter_claim != m_claim_validators.end()) for (const auto &verification_pair : iter_claim->second) { const jwt::claim &claim = jwt.get_payload_claim(claim_pair.first); if (verification_pair.first( claim, verification_pair.second) == false) { std::stringstream ss; ss << "'" << claim_pair.first << "' claim verification failed."; throw JWTVerificationException(ss.str()); } } } std::unique_ptr result(new AsyncStatus()); result->m_done = true; return result; } void add_critical_claims(const std::vector &claims) { std::copy(claims.begin(), claims.end(), std::back_inserter(m_critical_claims)); } void add_allowed_issuers(const std::vector &allowed_issuers) { std::copy(allowed_issuers.begin(), allowed_issuers.end(), std::back_inserter(m_allowed_issuers)); } void add_string_validator(const std::string &claim, StringValidatorFunction func) { auto result = m_validators.insert( {claim, std::vector()}); result.first->second.push_back(func); } void add_claim_validator(const std::string &claim, ClaimValidatorFunction func, void *data) { auto result = m_claim_validators.insert( {claim, std::vector>()}); result.first->second.push_back({func, data}); } void set_validate_all_claims_scitokens_1(bool new_val) { m_validate_all_claims = new_val; } /** * Get the profile of the last validated token. * * If there has been no validation - or the validation failed, * then the return value is unspecified. * * Will not return Profile::COMPAT. */ SciToken::Profile get_profile() const { if (m_profile == SciToken::Profile::COMPAT) { throw JWTVerificationException("Token profile has not been set."); } return m_profile; } /** * Set the profile that will be used for validation; COMPAT indicates any * supported profile is allowable. */ void set_validate_profile(SciToken::Profile profile) { m_validate_profile = profile; } /** * Store the contents of a public EC key for a given issuer. */ static bool store_public_ec_key(const std::string &issuer, const std::string &kid, const std::string &key); /** * Store the contents of a JWKS for a given issuer. */ static bool store_jwks(const std::string &issuer, const std::string &jwks); /** * Trigger a refresh of the JWKS or a given issuer. */ static bool refresh_jwks(const std::string &issuer); /** * Fetch the contents of fa JWKS for a given issuer (do not trigger a * refresh). Will return an empty JWKS if no valid JWKS is available. */ static std::string get_jwks(const std::string &issuer); private: static std::unique_ptr get_public_key_pem(const std::string &issuer, const std::string &kid, std::string &public_pem, std::string &algorithm); static std::unique_ptr get_public_key_pem_continue(std::unique_ptr status, std::string &public_pem, std::string &algorithm); static std::unique_ptr get_public_keys_from_web(const std::string &issuer, unsigned timeout); static std::unique_ptr get_public_keys_from_web_continue(std::unique_ptr status); static bool get_public_keys_from_db(const std::string issuer, int64_t now, picojson::value &keys, int64_t &next_update); static bool store_public_keys(const std::string &issuer, const picojson::value &keys, int64_t next_update, int64_t expires); /** * Safely format an issuer for error messages. * Serializes the issuer claim back to JSON format and limits the size * to prevent malicious issuers from causing problems in error output. */ static std::string format_issuer_for_error( const jwt::decoded_jwt &jwt) { try { if (!jwt.has_payload_claim("iss")) { return ""; } // Get the raw claim and serialize it back to JSON const auto &claim = jwt.get_payload_claim("iss"); std::string serialized = claim.to_json().serialize(); // Limit the size to prevent abuse const size_t max_issuer_length = 256; if (serialized.length() > max_issuer_length) { serialized = serialized.substr(0, max_issuer_length - 3) + "..."; } return serialized; } catch (...) { // If anything goes wrong, return a safe fallback return ""; } } bool m_validate_all_claims{true}; SciToken::Profile m_profile{SciToken::Profile::COMPAT}; SciToken::Profile m_validate_profile{SciToken::Profile::COMPAT}; ClaimStringValidatorMap m_validators; ClaimValidatorMap m_claim_validators; std::chrono::system_clock::time_point m_now; std::vector m_critical_claims; std::vector m_allowed_issuers; }; class Enforcer { public: typedef std::vector> AclsList; Enforcer(std::string issuer, std::vector audience_list) : m_issuer(issuer), m_audiences(audience_list) { m_validator.add_allowed_issuers({m_issuer}); m_validator.add_claim_validator("jti", &Enforcer::str_validator, nullptr); m_validator.add_claim_validator("sub", &Enforcer::str_validator, nullptr); m_validator.add_claim_validator("opt", &Enforcer::all_validator, nullptr); m_validator.add_claim_validator("aud", &Enforcer::aud_validator, this); m_validator.add_claim_validator("scope", &Enforcer::scope_validator, this); std::vector critical_claims = {"scope"}; // If any audiences are in the given to us, then force the validator to // check it. if (!m_audiences.empty()) { critical_claims.push_back("aud"); } m_validator.add_critical_claims(critical_claims); } void set_now(std::chrono::system_clock::time_point now) { m_validator.set_now(now); } void set_validate_profile(SciToken::Profile profile) { m_validate_profile = profile; } bool test(const SciToken &scitoken, const std::string &authz, const std::string &path) { reset_state(); m_test_path = path; m_test_authz = authz; try { m_validator.verify(scitoken, time(NULL) + 20); return true; } catch (std::runtime_error &) { throw; } } AclsList generate_acls(const SciToken &scitoken) { reset_state(); m_validator.verify(scitoken, time(NULL) + 20); return m_gen_acls; } std::unique_ptr generate_acls_start(const SciToken &scitoken, AclsList &acls) { reset_state(); auto status = m_validator.verify_async(scitoken); if (status->m_done) { acls = m_gen_acls; } return status; } std::unique_ptr generate_acls_continue(std::unique_ptr status, AclsList &acls) { auto result = m_validator.verify_async_continue(std::move(status)); if (result->m_done) { acls = m_gen_acls; } return result; } private: static bool all_validator(const jwt::claim &, void *) { return true; } static bool str_validator(const jwt::claim &claim, void *) { return claim.get_type() == jwt::json::type::string; } static bool scope_validator(const jwt::claim &claim, void *myself); static bool aud_validator(const jwt::claim &claim, void *myself) { auto me = reinterpret_cast(myself); std::vector jwt_audiences; if (claim.get_type() == jwt::json::type::string) { const std::string &audience = claim.as_string(); jwt_audiences.push_back(audience); } else if (claim.get_type() == jwt::json::type::array) { const picojson::array &audiences = claim.as_array(); for (const auto &aud_value : audiences) { const std::string &audience = aud_value.get(); jwt_audiences.push_back(audience); } } for (const auto &aud_value : jwt_audiences) { if (((me->m_validator.get_profile() == SciToken::Profile::SCITOKENS_2_0) && (aud_value == "ANY")) || ((me->m_validator.get_profile() == SciToken::Profile::WLCG_1_0) && (aud_value == "https://wlcg.cern.ch/jwt/v1/any"))) { return true; } for (const auto &aud : me->m_audiences) { if (aud == aud_value) { return true; } } } return false; } void reset_state() { m_test_path = ""; m_test_authz = ""; m_gen_acls.clear(); m_validator.set_validate_profile(m_validate_profile); } SciToken::Profile m_validate_profile{SciToken::Profile::COMPAT}; std::string m_test_path; std::string m_test_authz; AclsList m_gen_acls; std::string m_issuer; std::vector m_audiences; scitokens::Validator m_validator; }; } // namespace scitokens scitokens-cpp-1.2.0/src/test.cpp000066400000000000000000000070451511466717700165730ustar00rootroot00000000000000#include #include #include #include #include "scitokens.h" int main(int argc, const char **argv) { std::string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9." "AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE"; if (argc == 2) { token = argv[1]; } auto decoded = jwt::decode(token); for (auto &e : decoded.get_payload_json()) std::cout << e.first << " = " << e.second << std::endl; std::ifstream priv_ifs("test.pem"); std::string private_contents((std::istreambuf_iterator(priv_ifs)), (std::istreambuf_iterator())); std::ifstream pub_ifs("test.pem.pub"); std::string public_contents((std::istreambuf_iterator(pub_ifs)), (std::istreambuf_iterator())); char *err_msg; SciTokenKey key = scitoken_key_create("key-es356", "RS256", public_contents.c_str(), private_contents.c_str(), &err_msg); if (!key) { std::cout << "Failed to generate a key: " << err_msg << std::endl; return 1; } SciToken scitoken = scitoken_create(key); if (scitoken_set_claim_string(scitoken, "iss", "https://demo.scitokens.org", &err_msg)) { std::cout << "Failed to set a claim: " << err_msg << std::endl; } // Test setting and getting a claim char *value; if (scitoken_get_claim_string(scitoken, "iss", &value, &err_msg)) { std::cout << "Failed to get a claim: " << err_msg << std::endl; } if (strcmp(value, "https://demo.scitokens.org") != 0) { std::cout << "Failed to get same claim a claim: " << err_msg << std::endl; } if (scitoken_serialize(scitoken, &value, &err_msg)) { std::cout << "Failed to generate a token: " << err_msg << std::endl; return 1; } std::cout << "SciToken: " << value << std::endl; auto decoded2 = jwt::decode(value); for (auto &e : decoded2.get_payload_json()) std::cout << e.first << " = " << e.second << std::endl; scitoken_destroy(scitoken); scitoken_key_destroy(key); // Get updated strings from https://demo.scitokens.org/ // std::string test_value = // "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImtleS1lczI1NiJ9.eyJpc3MiOiJodHRwczovL2RlbW8uc2NpdG9rZW5zLm9yZyIsImV4cCI6MTU0NjM4OTU5MiwiaWF0IjoxNTQ2Mzg4OTkyLCJuYmYiOjE1NDYzODg5OTIsImp0aSI6IjRkMzM2MTU5LWMxMDEtNGRhYy1iYzI5LWI5NDQ3ZDRkY2IxZSJ9.VfSCPj79IfdVCZHw8n0RJJupbaSU0OqMWxRVAnVUNvk1SCz0Ep3O06Boe5I0SRiZR8_0jzHw9vHZ0YOT_0kPAw"; std::string test_value = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtleS1yczI1NiJ9." "eyJpc3MiOiJodHRwczovL2RlbW8uc2NpdG9rZW5zLm9yZyIsImV4cCI6MTU0NjM5MjAwOS" "wiaWF0IjoxNTQ2MzkxNDA5LCJuYmYiOjE1NDYzOTE0MDksImp0aSI6ImFkYTk2MjdiLWEx" "MGYtNGMyYS05Nzc2LTE4ZThkN2JmN2M4NSJ9.cNMG5zI2-JHh7l_" "PUPUAxom5Vi6Q3akKmv6q57CoVKHtxZAZRc47Uoix_" "AH3Xzr42qohr2FPamRTxUMsfZjrAFDJ_4JhJ-kKjJ3cRXXF-" "gj7lbniCDGOBuPXeMsVmeED15nauZ3XKXUHTGLEsg5O6RjS7sGKM_" "e9YiYvcTvWXcdkrkxZ2dPPU-R3IxdK6PtE9OB2XOk85H670OAJT3qimKm8Dk_" "Ri6DEEty1Su_" "1Tov3ac5B19iZkbhhVPMVP0cRolR9UNLhMxQAsbgEmArQOcs046AOzqQz6osOkdYOrVVO7" "lO2owUyMol94mB_39y1M8jcf5WNq3ukMMIzMCAPwA"; if (scitoken_deserialize(test_value.c_str(), &scitoken, nullptr, &err_msg)) { std::cout << "Failed to deserialize a token: " << err_msg << std::endl; return 1; } return 0; } scitokens-cpp-1.2.0/src/test_access.cpp000066400000000000000000000027131511466717700201110ustar00rootroot00000000000000#include #include "scitokens.h" int main(int argc, const char **argv) { if (argc < 6) { std::cerr << "Usage: " << argv[0] << " (TOKEN) (ISSUER) (AUDIENCE) (AUTHZ) (PATH)" << std::endl; return 1; } std::string token(argv[1]); std::string issuer(argv[2]); std::string audience(argv[3]); std::string authz(argv[4]); std::string path(argv[5]); const char *aud_list[2]; aud_list[0] = audience.c_str(); aud_list[1] = nullptr; SciToken scitoken; char *err_msg = nullptr; if (scitoken_deserialize(token.c_str(), &scitoken, nullptr, &err_msg)) { std::cout << "Failed to deserialize a token: " << err_msg << std::endl; return 1; } std::cout << "Token deserialization successful. Checking authorizations." << std::endl; Enforcer enf; if (!(enf = enforcer_create(issuer.c_str(), aud_list, &err_msg))) { std::cout << "Failed to create a new enforcer object: " << err_msg << std::endl; return 1; } const Acl acl{authz.c_str(), path.c_str()}; if (enforcer_test(enf, scitoken, &acl, &err_msg)) { if (err_msg) { std::cout << "Access test failed: " << err_msg << std::endl; } else { std::cout << "Access test failed." << std::endl; } return 1; } std::cout << "Access test successful." << std::endl; enforcer_destroy(enf); return 0; } scitokens-cpp-1.2.0/src/verify.cpp000066400000000000000000000100031511466717700171040ustar00rootroot00000000000000#include "scitokens.h" #include #include #include #include namespace { const char usage[] = "\n" "Syntax: %s [--cred cred_file] < TOKENFILE\n" "\n" " Options\n" " -h | --help Display usage\n" " -c | --cred File containing the signing credential.\n" " -i | --issuer Issuer of the token to verify.\n" " -K | --keyid Name of the token key.\n" " -p | --profile Profile to enforce (wlcg, scitokens1, " "scitokens2, atjwt).\n" "\n" " The token to verify must be provided via standard input (stdin).\n"; const struct option long_options[] = {{"help", no_argument, NULL, 'h'}, {"cred", required_argument, NULL, 'c'}, {"issuer", required_argument, NULL, 'i'}, {"keyid", required_argument, NULL, 'K'}, {"profile", required_argument, NULL, 'p'}, {0, 0, 0, 0}}; const char short_options[] = "hc:i:K:p:"; std::string g_cred, g_issuer, g_keyid, g_profile; int init_arguments(int argc, char *const argv[]) { int arg; while ((arg = getopt_long(argc, argv, short_options, long_options, nullptr)) != -1) { switch (arg) { case 'h': printf(usage, argv[0]); exit(0); break; case 'c': g_cred = optarg; break; case 'i': g_issuer = optarg; break; case 'K': g_keyid = optarg; break; case 'p': g_profile = optarg; break; default: fprintf(stderr, usage, argv[0]); exit(1); break; } } if (optind < argc - 1) { fprintf(stderr, "%s: invalid option -- %s\n", argv[0], argv[optind]); fprintf(stderr, usage, argv[0]); exit(1); } if ((!g_cred.empty() || !g_issuer.empty() || !g_keyid.empty()) && (g_cred.empty() || g_issuer.empty() || g_keyid.empty())) { fprintf(stderr, "%s: If --cred, --keyid, or --issuer are set, then all must be " "set.\n", argv[0]); fprintf(stderr, usage, argv[0]); exit(1); } return 0; } } // namespace int main(int argc, char *const *argv) { if (init_arguments(argc, argv)) { return 1; } std::string token; // If a positional argument is present, treat it as the token (with warning) if (optind < argc) { std::cout << argv[0] << ": Warning: Providing the token on the command line is " << "insecure. Please use stdin instead." << std::endl; token = argv[optind]; } else { // Read token from stdin std::getline(std::cin, token); } if (token.empty()) { fprintf(stderr, "%s: No token provided on stdin or command line.\n", argv[0]); fprintf(stderr, usage, argv[0]); return 1; } if (!g_issuer.empty()) { char *err_msg; std::ifstream pub_ifs(g_cred); std::string public_contents((std::istreambuf_iterator(pub_ifs)), (std::istreambuf_iterator())); auto rv = scitoken_store_public_ec_key(g_issuer.c_str(), g_keyid.c_str(), public_contents.c_str(), &err_msg); if (rv) { fprintf(stderr, "%s: %s\n", argv[0], err_msg); free(err_msg); return 1; } } SciToken scitoken; char *err_msg = nullptr; if (scitoken_deserialize(token.c_str(), &scitoken, nullptr, &err_msg)) { std::cout << "Failed to deserialize a token: " << err_msg << std::endl; return 1; } scitoken_destroy(scitoken); std::cout << "Token deserialization successful." << std::endl; return 0; } scitokens-cpp-1.2.0/test/000077500000000000000000000000001511466717700152725ustar00rootroot00000000000000scitokens-cpp-1.2.0/test/CMakeLists.txt000066400000000000000000000010201511466717700200230ustar00rootroot00000000000000 add_executable(scitokens-gtest main.cpp) if( NOT SCITOKENS_EXTERNAL_GTEST ) add_dependencies(scitokens-gtest gtest) include_directories("${PROJECT_SOURCE_DIR}/vendor/gtest/googletest/include") endif() if(SCITOKENS_EXTERNAL_GTEST) set(LIBGTEST "gtest") else() set(LIBGTEST "${CMAKE_BINARY_DIR}/external/gtest/src/gtest-build/lib/libgtest.a") endif() target_link_libraries(scitokens-gtest SciTokens "${LIBGTEST}" pthread) add_test( NAME unit COMMAND ${CMAKE_CURRENT_BINARY_DIR}/scitokens-gtest ) scitokens-cpp-1.2.0/test/main.cpp000066400000000000000000001020441511466717700167230ustar00rootroot00000000000000#include "../src/scitokens.h" #include #include #include namespace { const char ec_private[] = "-----BEGIN EC PRIVATE KEY-----\n" "MHcCAQEEIESSMxT7PLTR9A/aqd+CM0/6vv6fQWqDm0mNx8uE9EbpoAoGCCqGSM49\n" "AwEHoUQDQgAE1i+ImZ//iQhOPh0OMfZzdbmPH+3G1ouWezolCugQYWIRqNmwq3zR\n" "EnTbe4EmymTpJ1MJTPP/tCEUP3G/QqQuhA==\n" "-----END EC PRIVATE KEY-----\n"; const char ec_public[] = "-----BEGIN PUBLIC KEY-----\n" "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1i+ImZ//iQhOPh0OMfZzdbmPH+3G\n" "1ouWezolCugQYWIRqNmwq3zREnTbe4EmymTpJ1MJTPP/tCEUP3G/QqQuhA==\n" "-----END PUBLIC KEY-----\n"; const char ec_private_2[] = "-----BEGIN EC PRIVATE KEY-----\n" "MHcCAQEEIJH6NpWPHcM7wxL/bv89Nezug+KEUQjI9fZxhrBHNA1ioAoGCCqGSM49\n" "AwEHoUQDQgAEb8M7AxRN+DmbfYOoA6DeHCcSeA+kXWCq4E/g2ME/uBOdP8RE0tql\n" "e8fxYcaPikgMcppGq2ycTiLGgEYXgsq2JA==\n" "-----END EC PRIVATE KEY-----\n"; const char ec_public_2[] = "-----BEGIN PUBLIC KEY-----\n" "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEb8M7AxRN+DmbfYOoA6DeHCcSeA+k\n" "XWCq4E/g2ME/uBOdP8RE0tqle8fxYcaPikgMcppGq2ycTiLGgEYXgsq2JA==\n" "-----END PUBLIC KEY-----\n"; TEST(SciTokenTest, CreateToken) { SciToken token = scitoken_create(nullptr); ASSERT_TRUE(token != nullptr); scitoken_destroy(token); } TEST(SciTokenTest, SignToken) { char *err_msg = nullptr; std::unique_ptr mykey( scitoken_key_create("1", "ES256", ec_public, ec_private, &err_msg), scitoken_key_destroy); ASSERT_TRUE(mykey.get() != nullptr) << err_msg; std::unique_ptr mytoken( scitoken_create(mykey.get()), scitoken_destroy); ASSERT_TRUE(mytoken.get() != nullptr); auto rv = scitoken_set_claim_string( mytoken.get(), "iss", "https://demo.scitokens.org/gtest", &err_msg); ASSERT_TRUE(rv == 0) << err_msg; char *value; rv = scitoken_serialize(mytoken.get(), &value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; EXPECT_TRUE(value != nullptr); std::unique_ptr value_ptr(value, free); ASSERT_TRUE(strlen(value) > 50); } class SerializeTest : public ::testing::Test { protected: void SetUp() override { char *err_msg = nullptr; m_key = KeyPtr( scitoken_key_create("1", "ES256", ec_public, ec_private, &err_msg), scitoken_key_destroy); ASSERT_TRUE(m_key.get() != nullptr) << err_msg; m_token = TokenPtr(scitoken_create(m_key.get()), scitoken_destroy); ASSERT_TRUE(m_token.get() != nullptr); auto rv = scitoken_set_claim_string( m_token.get(), "iss", "https://demo.scitokens.org/gtest", &err_msg); ASSERT_TRUE(rv == 0) << err_msg; rv = scitoken_store_public_ec_key("https://demo.scitokens.org/gtest", "1", ec_public, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; scitoken_set_lifetime(m_token.get(), 60); m_audiences_array.push_back("https://demo.scitokens.org/"); m_audiences_array.push_back(nullptr); const char *groups[3] = {nullptr, nullptr, nullptr}; const char group0[] = "group0"; const char group1[] = "group1"; groups[0] = group0; groups[1] = group1; rv = scitoken_set_claim_string_list(m_token.get(), "groups", groups, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; m_read_token.reset(scitoken_create(nullptr)); ASSERT_TRUE(m_read_token.get() != nullptr); } using KeyPtr = std::unique_ptr; KeyPtr m_key{nullptr, scitoken_key_destroy}; using TokenPtr = std::unique_ptr; TokenPtr m_token{nullptr, scitoken_destroy}; std::vector m_audiences_array; TokenPtr m_read_token{nullptr, scitoken_destroy}; }; TEST_F(SerializeTest, VerifyTest) { char *err_msg = nullptr; char *token_value = nullptr; auto rv = scitoken_serialize(m_token.get(), &token_value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; std::unique_ptr token_value_ptr(token_value, free); rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; char *value; rv = scitoken_get_claim_string(m_read_token.get(), "iss", &value, &err_msg); ASSERT_TRUE(value != nullptr); ASSERT_TRUE(rv == 0) << err_msg; std::unique_ptr value_ptr(value, free); EXPECT_STREQ(value, "https://demo.scitokens.org/gtest"); value_ptr.reset(); rv = scitoken_get_claim_string(m_read_token.get(), "doesnotexist", &value, &err_msg); free(err_msg); EXPECT_FALSE(rv == 0); } TEST_F(SerializeTest, TestStringList) { char *err_msg = nullptr; char **value; auto rv = scitoken_get_claim_string_list(m_token.get(), "groups", &value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; ASSERT_TRUE(value != nullptr); ASSERT_TRUE(value[0] != nullptr); EXPECT_STREQ(value[0], "group0"); ASSERT_TRUE(value[1] != nullptr); EXPECT_STREQ(value[1], "group1"); EXPECT_TRUE(value[2] == nullptr); scitoken_free_string_list(value); } TEST_F(SerializeTest, VerifyWLCGTest) { char *err_msg = nullptr; char *token_value = nullptr; scitoken_set_serialize_profile(m_token.get(), SciTokenProfile::WLCG_1_0); auto rv = scitoken_serialize(m_token.get(), &token_value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; std::unique_ptr token_value_ptr(token_value, free); // Accepts any profile. rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; char *value; rv = scitoken_get_claim_string(m_read_token.get(), "wlcg.ver", &value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; ASSERT_TRUE(value != nullptr); std::unique_ptr value_ptr(value, free); EXPECT_STREQ(value, "1.0"); value_ptr.reset(); rv = scitoken_get_claim_string(m_read_token.get(), "ver", &value, &err_msg); free(err_msg); err_msg = nullptr; EXPECT_FALSE(rv == 0); // Accepts only a WLCG token scitoken_set_deserialize_profile(m_read_token.get(), SciTokenProfile::WLCG_1_0); rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; // Accepts only SciToken 1.0; should fail. scitoken_set_deserialize_profile(m_read_token.get(), SciTokenProfile::SCITOKENS_1_0); rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); free(err_msg); ASSERT_FALSE(rv == 0); } TEST_F(SerializeTest, FailVerifyToken) { char *err_msg = nullptr; std::unique_ptr mykey( scitoken_key_create("1", "ES256", ec_public_2, ec_private_2, &err_msg), scitoken_key_destroy); ASSERT_TRUE(mykey.get() != nullptr) << err_msg; std::unique_ptr mytoken( scitoken_create(mykey.get()), scitoken_destroy); ASSERT_TRUE(mytoken.get() != nullptr); auto rv = scitoken_set_claim_string( mytoken.get(), "iss", "https://demo.scitokens.org/gtest", &err_msg); ASSERT_TRUE(rv == 0) << err_msg; char *value; rv = scitoken_serialize(mytoken.get(), &value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; EXPECT_TRUE(value != nullptr); std::unique_ptr value_ptr(value, free); EXPECT_TRUE(strlen(value) > 50); // Should fail; we signed it with the wrong public key. rv = scitoken_deserialize_v2(value, m_read_token.get(), nullptr, &err_msg); free(err_msg); EXPECT_FALSE(rv == 0); } TEST_F(SerializeTest, VerifyATJWTTest) { char *err_msg = nullptr; // Serialize as at+jwt token. char *token_value = nullptr; scitoken_set_serialize_profile(m_token.get(), SciTokenProfile::AT_JWT); auto rv = scitoken_serialize(m_token.get(), &token_value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; std::unique_ptr token_value_ptr(token_value, free); // Accepts any profile. rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; // Accepts only an at+jwt token, should work with at+jwt token scitoken_set_deserialize_profile(m_read_token.get(), SciTokenProfile::AT_JWT); rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; // Accepts only SciToken 2.0; should fail scitoken_set_deserialize_profile(m_read_token.get(), SciTokenProfile::SCITOKENS_2_0); rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); free(err_msg); ASSERT_FALSE(rv == 0); } TEST_F(SerializeTest, FailVerifyATJWTTest) { char *err_msg = nullptr; // Serialize as "compat" token. char *token_value = nullptr; scitoken_set_serialize_profile(m_token.get(), SciTokenProfile::COMPAT); auto rv = scitoken_serialize(m_token.get(), &token_value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; std::unique_ptr token_value_ptr(token_value, free); // Accepts any profile. rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; // Accepts only an at+jwt token, should fail with COMPAT token scitoken_set_deserialize_profile(m_read_token.get(), SciTokenProfile::AT_JWT); rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); free(err_msg); ASSERT_FALSE(rv == 0); } TEST_F(SerializeTest, EnforcerTest) { /* * Test that the enforcer works and returns an err_msg */ char *err_msg = nullptr; auto rv = scitoken_set_claim_string( m_token.get(), "aud", "https://demo.scitokens.org/", &err_msg); ASSERT_TRUE(rv == 0) << err_msg; auto enforcer = enforcer_create("https://demo.scitokens.org/gtest", &m_audiences_array[0], &err_msg); ASSERT_TRUE(enforcer != nullptr) << err_msg; Acl acl; acl.authz = "read"; acl.resource = "/stuff"; rv = scitoken_set_claim_string(m_token.get(), "scope", "read:/blah", &err_msg); ASSERT_TRUE(rv == 0) << err_msg; rv = scitoken_set_claim_string(m_token.get(), "ver", "scitoken:2.0", &err_msg); ASSERT_TRUE(rv == 0) << err_msg; char *token_value = nullptr; rv = scitoken_serialize(m_token.get(), &token_value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; std::unique_ptr token_value_ptr(token_value, free); rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; free(err_msg); rv = enforcer_test(enforcer, m_read_token.get(), &acl, &err_msg); ASSERT_STREQ( err_msg, "token verification failed: 'scope' claim verification failed."); ASSERT_TRUE(rv == -1) << err_msg; free(err_msg); enforcer_destroy(enforcer); } TEST_F(SerializeTest, EnforcerScopeTest) { char *err_msg = nullptr; auto rv = scitoken_set_claim_string( m_token.get(), "aud", "https://demo.scitokens.org/", &err_msg); ASSERT_TRUE(rv == 0) << err_msg; auto enforcer = enforcer_create("https://demo.scitokens.org/gtest", &m_audiences_array[0], &err_msg); ASSERT_TRUE(enforcer != nullptr) << err_msg; scitoken_set_serialize_profile(m_token.get(), SciTokenProfile::WLCG_1_0); rv = scitoken_set_claim_string( m_token.get(), "scope", "storage.modify:/ storage.read:/ openid offline_access", &err_msg); ASSERT_TRUE(rv == 0) << err_msg; char *token_value = nullptr; rv = scitoken_serialize(m_token.get(), &token_value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); free(token_value); ASSERT_TRUE(rv == 0) << err_msg; Acl *acls; enforcer_generate_acls(enforcer, m_read_token.get(), &acls, &err_msg); ASSERT_TRUE(acls != nullptr) << err_msg; int idx = 0; bool found_read = false; bool found_write = false; while (acls[idx].resource && acls[idx++].authz) { auto resource = acls[idx - 1].resource; auto authz = acls[idx - 1].authz; if (strcmp(authz, "read") == 0) { found_read = true; ASSERT_STREQ(resource, "/"); } else if (strcmp(authz, "write") == 0) { found_write = true; ASSERT_STREQ(resource, "/"); } } enforcer_acl_free(acls); enforcer_destroy(enforcer); ASSERT_TRUE(found_read); ASSERT_TRUE(found_write); } } // namespace TEST_F(SerializeTest, DeserializeAsyncTest) { char *err_msg = nullptr; // Serialize as "compat" token. char *token_value = nullptr; scitoken_set_serialize_profile(m_token.get(), SciTokenProfile::COMPAT); auto rv = scitoken_serialize(m_token.get(), &token_value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; std::unique_ptr token_value_ptr(token_value, free); SciToken scitoken; SciTokenStatus status; // Accepts any profile. rv = scitoken_deserialize_start(token_value, &scitoken, nullptr, &status, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; // Accepts only an at+jwt token, should fail with COMPAT token while (rv == 0 && status) { rv = scitoken_deserialize_continue(&scitoken, &status, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; } scitoken_destroy(scitoken); } TEST_F(SerializeTest, FailDeserializeAsyncTest) { char *err_msg = nullptr; std::unique_ptr mykey( scitoken_key_create("1", "ES256", ec_public_2, ec_private_2, &err_msg), scitoken_key_destroy); ASSERT_TRUE(mykey.get() != nullptr) << err_msg; std::unique_ptr mytoken( scitoken_create(mykey.get()), scitoken_destroy); ASSERT_TRUE(mytoken.get() != nullptr); auto rv = scitoken_set_claim_string( mytoken.get(), "iss", "https://demo.scitokens.org/gtest", &err_msg); ASSERT_TRUE(rv == 0) << err_msg; char *value; rv = scitoken_serialize(mytoken.get(), &value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; EXPECT_TRUE(value != nullptr); std::unique_ptr value_ptr(value, free); EXPECT_TRUE(strlen(value) > 50); char *token_value = nullptr; std::unique_ptr token_value_ptr(token_value, free); SciToken scitoken; SciTokenStatus status; // Accepts any profile. rv = scitoken_deserialize_start(value, &scitoken, nullptr, &status, &err_msg); EXPECT_FALSE(rv == 0) << err_msg; free(err_msg); err_msg = nullptr; // Accepts only an at+jwt token, should fail with COMPAT token while (rv == 0 && status) { rv = scitoken_deserialize_continue(&scitoken, &status, &err_msg); EXPECT_FALSE(rv == 0) << err_msg; free(err_msg); err_msg = nullptr; } } TEST_F(SerializeTest, ExplicitTime) { char *err_msg = nullptr; scitoken_set_serialize_profile(m_token.get(), SciTokenProfile::WLCG_1_0); auto rv = scitoken_set_claim_string(m_token.get(), "scope", "storage.read:/", &err_msg); ASSERT_TRUE(rv == 0) << err_msg; char *token_value = nullptr; rv = scitoken_serialize(m_token.get(), &token_value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); free(token_value); ASSERT_TRUE(rv == 0) << err_msg; auto enforcer = enforcer_create("https://demo.scitokens.org/gtest", &m_audiences_array[0], &err_msg); ASSERT_TRUE(enforcer != nullptr) << err_msg; Acl *acls; rv = enforcer_generate_acls(enforcer, m_read_token.get(), &acls, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; ASSERT_TRUE(acls != nullptr); enforcer_acl_free(acls); enforcer_set_time(enforcer, time(NULL), &err_msg); rv = enforcer_generate_acls(enforcer, m_read_token.get(), &acls, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; enforcer_set_time(enforcer, time(NULL) + 100, &err_msg); enforcer_acl_free(acls); rv = enforcer_generate_acls(enforcer, m_read_token.get(), &acls, &err_msg); free(err_msg); err_msg = nullptr; ASSERT_FALSE(rv == 0); enforcer_set_time(enforcer, time(NULL) - 100, &err_msg); rv = enforcer_generate_acls(enforcer, m_read_token.get(), &acls, &err_msg); ASSERT_FALSE(rv == 0); free(err_msg); enforcer_destroy(enforcer); } TEST_F(SerializeTest, GetExpirationErrorHandling) { char *err_msg = nullptr; // Test NULL token handling long long expiry; auto rv = scitoken_get_expiration(nullptr, &expiry, &err_msg); ASSERT_FALSE(rv == 0); ASSERT_TRUE(err_msg != nullptr); EXPECT_STREQ(err_msg, "Token cannot be NULL"); free(err_msg); err_msg = nullptr; // Test NULL expiry parameter handling rv = scitoken_get_expiration(m_token.get(), nullptr, &err_msg); ASSERT_FALSE(rv == 0); ASSERT_TRUE(err_msg != nullptr); EXPECT_STREQ(err_msg, "Expiry output parameter cannot be NULL"); free(err_msg); err_msg = nullptr; // Test normal operation works char *token_value = nullptr; rv = scitoken_serialize(m_token.get(), &token_value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; rv = scitoken_get_expiration(m_read_token.get(), &expiry, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; ASSERT_TRUE(expiry > 0); free(token_value); } class SerializeNoKidTest : public ::testing::Test { protected: void SetUp() override { char *err_msg = nullptr; m_key = KeyPtr(scitoken_key_create("none", "ES256", ec_public, ec_private, &err_msg), scitoken_key_destroy); ASSERT_TRUE(m_key.get() != nullptr) << err_msg; m_token = TokenPtr(scitoken_create(m_key.get()), scitoken_destroy); ASSERT_TRUE(m_token.get() != nullptr); auto rv = scitoken_set_claim_string( m_token.get(), "iss", "https://demo.scitokens.org/gtest", &err_msg); ASSERT_TRUE(rv == 0) << err_msg; rv = scitoken_store_public_ec_key("https://demo.scitokens.org/gtest", "1", ec_public, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; scitoken_set_lifetime(m_token.get(), 60); m_audiences_array.push_back("https://demo.scitokens.org/"); m_audiences_array.push_back(nullptr); const char *groups[3] = {nullptr, nullptr, nullptr}; const char group0[] = "group0"; const char group1[] = "group1"; groups[0] = group0; groups[1] = group1; rv = scitoken_set_claim_string_list(m_token.get(), "groups", groups, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; m_read_token.reset(scitoken_create(nullptr)); ASSERT_TRUE(m_read_token.get() != nullptr); } using KeyPtr = std::unique_ptr; KeyPtr m_key{nullptr, scitoken_key_destroy}; using TokenPtr = std::unique_ptr; TokenPtr m_token{nullptr, scitoken_destroy}; std::vector m_audiences_array; TokenPtr m_read_token{nullptr, scitoken_destroy}; }; TEST_F(SerializeNoKidTest, VerifyATJWTTest) { char *err_msg = nullptr; // Serialize as at+jwt token. char *token_value = nullptr; scitoken_set_serialize_profile(m_token.get(), SciTokenProfile::AT_JWT); auto rv = scitoken_serialize(m_token.get(), &token_value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; std::unique_ptr token_value_ptr(token_value, free); // Accepts any profile. rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; // Accepts only an at+jwt token, should work with at+jwt token scitoken_set_deserialize_profile(m_read_token.get(), SciTokenProfile::AT_JWT); rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; // Accepts only SciToken 2.0; should fail scitoken_set_deserialize_profile(m_read_token.get(), SciTokenProfile::SCITOKENS_2_0); rv = scitoken_deserialize_v2(token_value, m_read_token.get(), nullptr, &err_msg); free(err_msg); ASSERT_FALSE(rv == 0); } class KeycacheTest : public ::testing::Test { protected: std::string demo_scitokens_url = "https://demo.scitokens.org"; void SetUp() override { char *err_msg = nullptr; auto rv = keycache_set_jwks(demo_scitokens_url.c_str(), demo_scitokens.c_str(), &err_msg); ASSERT_TRUE(rv == 0) << err_msg; } // Reference copy of the keys at https://demo.scitokens.org/oauth2/certs; // may need to be updated periodically. std::string demo_scitokens = "{\"keys\":[{\"alg\":\"RS256\",\"e\":\"AQAB\",\"kid\":\"key-rs256\"," "\"kty\":\"RSA\",\"n\":\"uGDGTLXnqh3mfopjys6sFUBvFl3F4Qt6NEYphq_u_" "aBhtN1X9NEyb78uB_" "I1KjciJNGLIQU0ECsJiFx6qV1hR9xE1dPyrS3bU92AVtnBrvzUtTU-aUZAmZQiuAC_rC0-" "z_" "TOQr6qJkkUgZtxR9n9op55ZBpRfZD5dzhkW4Dm146vfTKt0D4cIMoMNJS5xQx9nibeB4E8" "hryZDW_" "fPeD0XZDcpByNyP0jFDYkxdUtQFvyRpz4WMZ4ejUfvW3gf4LRAfGZJtMnsZ7ZW4RfoQbhi" "XKMfWeBEjQDiXh0r-KuZLykxhYJtpf7fTnPna753IzMgRMmW3F69iQn2LQN3LoSMw==\"," "\"use\":\"sig\"},{\"alg\":\"ES256\",\"kid\":\"key-es256\",\"kty\":" "\"EC\",\"use\":\"sig\",\"x\":" "\"ncSCrGTBTXXOhNiAOTwNdPjwRz1hVY4saDNiHQK9Bh4=\",\"y\":" "\"sCsFXvx7FAAklwq3CzRCBcghqZOFPB2dKUayS6LY_Lo=\"}]}"; std::string demo_scitokens2 = "{\"keys\":[{\"alg\":\"ES256\",\"kid\":\"key-es256\",\"kty\":\"EC\"," "\"use\":\"sig\",\"x\":\"ncSCrGTBTXXOhNiAOTwNdPjwRz1hVY4saDNiHQK9Bh4=" "\",\"y\":\"sCsFXvx7FAAklwq3CzRCBcghqZOFPB2dKUayS6LY_Lo=\"}]}"; }; TEST_F(KeycacheTest, RefreshTest) { char *err_msg = nullptr; auto rv = keycache_refresh_jwks(demo_scitokens_url.c_str(), &err_msg); ASSERT_TRUE(rv == 0) << err_msg; char *output_jwks; rv = keycache_get_cached_jwks(demo_scitokens_url.c_str(), &output_jwks, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; ASSERT_TRUE(output_jwks != nullptr); std::string output_jwks_str(output_jwks); free(output_jwks); EXPECT_EQ(demo_scitokens, output_jwks_str); } TEST_F(KeycacheTest, RefreshInvalid) { char *err_msg = nullptr, *jwks; auto rv = keycache_refresh_jwks("https://demo.scitokens.org/invalid", &err_msg); ASSERT_FALSE(rv == 0); free(err_msg); rv = keycache_get_cached_jwks("https://demo.scitokens.org/invalid", &jwks, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; ASSERT_TRUE(jwks != nullptr); std::string jwks_str(jwks); free(jwks); EXPECT_EQ(jwks_str, "{\"keys\": []}"); } TEST_F(KeycacheTest, GetInvalid) { char *err_msg = nullptr, *jwks; auto rv = keycache_get_cached_jwks("https://demo.scitokens.org/unknown", &jwks, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; ASSERT_TRUE(jwks != nullptr); std::string jwks_str(jwks); free(jwks); } TEST_F(KeycacheTest, GetTest) { char *err_msg = nullptr, *jwks; auto rv = keycache_get_cached_jwks(demo_scitokens_url.c_str(), &jwks, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; ASSERT_TRUE(jwks != nullptr); std::string jwks_str(jwks); free(jwks); EXPECT_EQ(demo_scitokens, jwks_str); } TEST_F(KeycacheTest, SetGetTest) { char *err_msg = nullptr; auto rv = keycache_set_jwks(demo_scitokens_url.c_str(), demo_scitokens2.c_str(), &err_msg); ASSERT_TRUE(rv == 0) << err_msg; char *jwks; rv = keycache_get_cached_jwks(demo_scitokens_url.c_str(), &jwks, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; ASSERT_TRUE(jwks != nullptr); std::string jwks_str(jwks); free(jwks); EXPECT_EQ(demo_scitokens2, jwks_str); } TEST_F(KeycacheTest, SetGetConfiguredCacheHome) { // Set cache home char cache_path[FILENAME_MAX]; ASSERT_TRUE(getcwd(cache_path, sizeof(cache_path)) != nullptr); // Side effect gets cwd char *err_msg = nullptr; std::string key = "keycache.cache_home"; auto rv = scitoken_config_set_str(key.c_str(), cache_path, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; // Set the jwks at the new cache home rv = keycache_set_jwks(demo_scitokens_url.c_str(), demo_scitokens2.c_str(), &err_msg); ASSERT_TRUE(rv == 0) << err_msg; // Fetch the cached jwks from the new cache home char *jwks; rv = keycache_get_cached_jwks(demo_scitokens_url.c_str(), &jwks, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; ASSERT_TRUE(jwks != nullptr); std::string jwks_str(jwks); free(jwks); EXPECT_EQ(demo_scitokens2, jwks_str); // Check that cache home is still what was set char *output; rv = scitoken_config_get_str(key.c_str(), &output, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; EXPECT_EQ(*output, *cache_path); free(output); // Reset cache home to whatever it was before by setting empty config rv = scitoken_config_set_str(key.c_str(), "", &err_msg); ASSERT_TRUE(rv == 0) << err_msg; } TEST_F(KeycacheTest, InvalidConfigKeyTest) { char *err_msg = nullptr; int new_update_interval = 400; std::string key = "invalid key"; auto rv = scitoken_config_set_int(key.c_str(), new_update_interval, &err_msg); free(err_msg); err_msg = nullptr; ASSERT_FALSE(rv == 0); const char *key2 = nullptr; rv = scitoken_config_set_int(key2, new_update_interval, &err_msg); free(err_msg); ASSERT_FALSE(rv == 0); } TEST_F(KeycacheTest, SetGetUpdateTest) { char *err_msg = nullptr; int new_update_interval = 400; std::string key = "keycache.update_interval_s"; auto rv = scitoken_config_set_int(key.c_str(), new_update_interval, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; rv = scitoken_config_get_int(key.c_str(), &err_msg); EXPECT_EQ(rv, new_update_interval) << err_msg; } TEST_F(KeycacheTest, SetGetExpirationTest) { char *err_msg = nullptr; int new_expiration_interval = 2 * 24 * 3600; std::string key = "keycache.expiration_interval_s"; auto rv = scitoken_config_set_int(key.c_str(), new_expiration_interval, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; rv = scitoken_config_get_int(key.c_str(), &err_msg); EXPECT_EQ(rv, new_expiration_interval) << err_msg; } TEST_F(KeycacheTest, SetInvalidUpdateTest) { char *err_msg = nullptr; int new_update_interval = -1; std::string key = "keycache.update_interval_s"; auto rv = scitoken_config_set_int(key.c_str(), new_update_interval, &err_msg); free(err_msg); ASSERT_FALSE(rv == 0); } TEST_F(KeycacheTest, SetInvalidExpirationTest) { char *err_msg = nullptr; int new_expiration_interval = -2 * 24 * 3600; std::string key = "keycache.expiration_interval_s"; auto rv = scitoken_config_set_int(key.c_str(), new_expiration_interval, &err_msg); free(err_msg); ASSERT_FALSE(rv == 0); } TEST_F(KeycacheTest, RefreshExpiredTest) { char *err_msg = nullptr, *jwks; int new_expiration_interval = 0; std::string key = "keycache.expiration_interval_s"; auto rv = scitoken_config_set_int(key.c_str(), new_expiration_interval, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; rv = keycache_refresh_jwks(demo_scitokens_url.c_str(), &err_msg); ASSERT_TRUE(rv == 0) << err_msg; sleep(1); rv = keycache_get_cached_jwks(demo_scitokens_url.c_str(), &jwks, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; ASSERT_TRUE(jwks != nullptr); std::string jwks_str(jwks); free(jwks); EXPECT_EQ(jwks_str, "{\"keys\": []}"); } class IssuerSecurityTest : public ::testing::Test { protected: void SetUp() override { char *err_msg = nullptr; m_key = KeyPtr( scitoken_key_create("1", "ES256", ec_public, ec_private, &err_msg), scitoken_key_destroy); ASSERT_TRUE(m_key.get() != nullptr) << err_msg; m_token = TokenPtr(scitoken_create(m_key.get()), scitoken_destroy); ASSERT_TRUE(m_token.get() != nullptr); // Store public key for verification auto rv = scitoken_store_public_ec_key( "https://demo.scitokens.org/gtest", "1", ec_public, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; scitoken_set_lifetime(m_token.get(), 60); m_read_token.reset(scitoken_create(nullptr)); ASSERT_TRUE(m_read_token.get() != nullptr); } using KeyPtr = std::unique_ptr; KeyPtr m_key{nullptr, scitoken_key_destroy}; using TokenPtr = std::unique_ptr; TokenPtr m_token{nullptr, scitoken_destroy}; TokenPtr m_read_token{nullptr, scitoken_destroy}; }; TEST_F(IssuerSecurityTest, LongIssuerTruncation) { char *err_msg = nullptr; // Create a very long issuer (1000 characters) std::string very_long_issuer(1000, 'A'); auto rv = scitoken_set_claim_string(m_token.get(), "iss", very_long_issuer.c_str(), &err_msg); ASSERT_TRUE(rv == 0) << err_msg; char *token_value = nullptr; rv = scitoken_serialize(m_token.get(), &token_value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; std::unique_ptr token_value_ptr(token_value, free); // Try to verify with a restricted issuer list to trigger error const char *allowed_issuers[] = {"https://good.issuer.com", nullptr}; rv = scitoken_deserialize_v2(token_value, m_read_token.get(), allowed_issuers, &err_msg); // Should fail ASSERT_FALSE(rv == 0); ASSERT_TRUE(err_msg != nullptr); std::string error_message(err_msg); std::unique_ptr err_msg_ptr(err_msg, free); // Error message should be reasonable length (< 400 chars) EXPECT_LT(error_message.length(), 400); // Should contain expected error text EXPECT_NE(error_message.find("is not in list of allowed issuers"), std::string::npos); // Should contain truncated issuer with ellipsis EXPECT_NE(error_message.find("..."), std::string::npos); } TEST_F(IssuerSecurityTest, SpecialCharacterIssuer) { char *err_msg = nullptr; // Create an issuer with special characters and control chars std::string special_issuer = "https://bad.com/\"\n\t\r\x01\x1f"; auto rv = scitoken_set_claim_string(m_token.get(), "iss", special_issuer.c_str(), &err_msg); ASSERT_TRUE(rv == 0) << err_msg; char *token_value = nullptr; rv = scitoken_serialize(m_token.get(), &token_value, &err_msg); ASSERT_TRUE(rv == 0) << err_msg; std::unique_ptr token_value_ptr(token_value, free); // Try to verify with a restricted issuer list to trigger error const char *allowed_issuers[] = {"https://good.issuer.com", nullptr}; rv = scitoken_deserialize_v2(token_value, m_read_token.get(), allowed_issuers, &err_msg); // Should fail ASSERT_FALSE(rv == 0); ASSERT_TRUE(err_msg != nullptr); std::string error_message(err_msg); std::unique_ptr err_msg_ptr(err_msg, free); // Error message should be reasonable length EXPECT_LT(error_message.length(), 300); // Should contain expected error text EXPECT_NE(error_message.find("is not in list of allowed issuers"), std::string::npos); // Should contain properly escaped JSON (with quotes) EXPECT_NE(error_message.find("\""), std::string::npos); } int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } scitokens-cpp-1.2.0/vendor/000077500000000000000000000000001511466717700156105ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/.clang-format000066400000000000000000000000501511466717700201560ustar00rootroot00000000000000DisableFormat: true SortIncludes: Never scitokens-cpp-1.2.0/vendor/gtest/000077500000000000000000000000001511466717700167365ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/000077500000000000000000000000001511466717700171745ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.editorconfig000066400000000000000000000004411511466717700216500ustar00rootroot00000000000000root = true [!*.{h,cpp}] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.{h,cpp}] indent_style = tab trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false scitokens-cpp-1.2.0/vendor/jwt-cpp/.gitattributes000066400000000000000000000005721511466717700220730ustar00rootroot00000000000000# Auto detect text files and perform LF normalization * text=auto # Custom for Visual Studio *.cs diff=csharp # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/000077500000000000000000000000001511466717700205345ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/FUNDING.yml000066400000000000000000000000701511466717700223460ustar00rootroot00000000000000github: [Thalhammer,prince-chrismc] patreon: Thalhammer scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/ISSUE_TEMPLATE/000077500000000000000000000000001511466717700227175ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/ISSUE_TEMPLATE/bug-report.yml000066400000000000000000000044361511466717700255370ustar00rootroot00000000000000name: Bug Report 🐛 description: File a bug report labels: ["bug"] body: - type: markdown attributes: value: | Thanks for taking the time to fill out this bug report! validations: required: false - type: textarea id: what-happened attributes: label: What happened? description: Also tell us, what did you expect to happen? Feel free to include some screenshots placeholder: Tell us what you see! value: "A bug happened!" validations: required: true - type: textarea id: reproduce attributes: label: How To Reproduce? description: Please provide a small snippet to reproduce the issue placeholder: Some C++ code or Shell code to recreate th problem value: | ```c++ #include "jwt-cpp/jwt.h" int main() { return 0; } ``` - type: dropdown id: version attributes: label: Version description: What version of our software are you running? options: - 0.7.1 - 0.7.0 - 0.6.0 - 0.5.2 - Older (please let us know if the "What happened" box) validations: required: true - type: dropdown id: operating-system attributes: label: What OS are you seeing the problem on? multiple: true options: - Windows - Linux - MacOS - Other (please let us know if the "What happened" box) validations: required: true - type: dropdown id: compiler attributes: label: What compiler are you seeing the problem on? multiple: true options: - GCC - Clang - MSVC - Other (please let us know if the "What happened" box) validations: required: true - type: textarea id: logs attributes: label: Relevant log output description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. render: shell - type: checkboxes id: terms attributes: label: Code of Conduct description: By submitting this issue, you agree to follow our [Code of Conduct](https://example.com) options: - label: I agree to follow this project's Code of Conduct required: true scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/ISSUE_TEMPLATE/feature-request.yml000066400000000000000000000007171511466717700265700ustar00rootroot00000000000000name: Feature Request 🧪 description: Have a great idea? Find something is missing? labels: ["enhancement"] body: - type: markdown attributes: value: | We'd love to hear your idea(s)! - type: input id: question attributes: label: "What would you like to see added?" validations: required: true - type: textarea id: context attributes: label: Additional Context validations: required: true scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/ISSUE_TEMPLATE/support-question.yml000066400000000000000000000007161511466717700270270ustar00rootroot00000000000000name: Support Question 🤹 description: Have some questions? We can offer help. labels: ["question"] body: - type: markdown attributes: value: | Don't hesitate to ask any question you might have! - type: input id: question attributes: label: "What's your question?" validations: required: true - type: textarea id: context attributes: label: Additional Context validations: required: true scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/000077500000000000000000000000001511466717700221745ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/badge/000077500000000000000000000000001511466717700232365ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/badge/action.yml000066400000000000000000000016631511466717700252440ustar00rootroot00000000000000name: Regular badging sequence description: Publishes a badge based on the job status inputs: category: description: The subfolder where to group the badges required: true label: description: The label to you in the badge (this should be unique for each badge in a category) required: true github_token: description: The token to use to publish the changes required: false default: ${{ github.token }} runs: using: composite steps: - if: job.status == 'success' uses: ./.github/actions/badge/write with: category: ${{ inputs.category }} label: ${{ inputs.label }} - if: job.status == 'failure' uses: ./.github/actions/badge/write with: category: ${{ inputs.category }} label: ${{ inputs.label }} message: Failing color: red - uses: ./.github/actions/badge/publish with: github_token: ${{ inputs.github_token }} scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/badge/write/000077500000000000000000000000001511466717700243705ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/badge/write/action.yml000066400000000000000000000016071511466717700263740ustar00rootroot00000000000000name: Make a Badge description: Creates a JSON file for use with Sheilds.io. The default is a "brightgreen" "Passing" badge inputs: category: description: The subfolder where to group the badges required: true label: description: The label to you in the badge (this should be unqie for each badge in a category) required: true message: description: The message you wish to have in the badge required: false default: "Passing" color: description: The color you wish the badge to be required: false default: "brightgreen" runs: using: composite steps: - run: | mkdir -p badges/${{ inputs.category }}/${{ inputs.label }} echo '{ "schemaVersion": 1, "label": "${{ inputs.label }}", "message": "${{ inputs.message }}", "color": "${{ inputs.color }}" }' > badges/${{ inputs.category }}/${{ inputs.label }}/shields.json shell: bash scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/000077500000000000000000000000001511466717700236425ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/boost-json/000077500000000000000000000000001511466717700257375ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/boost-json/action.yml000066400000000000000000000010231511466717700277330ustar00rootroot00000000000000name: Install Boost.JSON description: Install Boost.JSON for building test application inputs: version: description: The desired Boost.JSON version to install required: false default: "1.78.0" runs: using: composite steps: - run: | cd /tmp wget https://github.com/boostorg/json/archive/boost-${{ inputs.version }}.tar.gz tar -zxf /tmp/boost-${{ inputs.version }}.tar.gz cd json-boost-${{ inputs.version }} sudo cp -vR include/boost /usr/local/include shell: bash scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/cmake/000077500000000000000000000000001511466717700247225ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/cmake/action.yml000066400000000000000000000023151511466717700267230ustar00rootroot00000000000000name: Install CMake description: Download, Build and Cache CMake inputs: version: description: The desired CMake version to install required: true url: description: "The corresponding URL to download the source code from" required: true runs: using: composite steps: - name: Cache CMake id: cache-cmake uses: actions/cache@v3 with: path: cmake-${{ inputs.version }} key: ${{ runner.name }}-${{ runner.os }}-${{ runner.arch }}-${{ job.container.id }}-cmake-${{ inputs.version }} - name: Build cmake if: steps.cache-cmake.outputs.cache-hit != 'true' run: | wget ${{ inputs.url }} tar -zxf cmake-${{ inputs.version }}.tar.gz cd cmake-${{ inputs.version }} ./bootstrap make -j $(nproc) shell: bash - name: Install cmake run: | cd cmake-${{ inputs.version }} # Depending if we run in on a GitHub Actions or from within a Docker image we have different permissions if [[ $EUID > 0 ]]; then # If we are not root then we need to sudo sudo make install else # Default docker image does not have users setup so we are only root and can not sudo make install fi shell: bash scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/danielaparker-jsoncons/000077500000000000000000000000001511466717700302765ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/danielaparker-jsoncons/action.yml000066400000000000000000000010051511466717700322720ustar00rootroot00000000000000name: Install jsoncons description: Install jsoncons for building test application inputs: version: description: The desired jsoncons version to install required: false default: "0.168.7" runs: using: composite steps: - run: | cd /tmp wget https://github.com/danielaparker/jsoncons/archive/v${{ inputs.version }}.tar.gz tar -zxf /tmp/v${{ inputs.version }}.tar.gz cd jsoncons-${{ inputs.version }} cmake . sudo cmake --install . shell: bash scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/gtest/000077500000000000000000000000001511466717700247705ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/gtest/action.yml000066400000000000000000000010151511466717700267650ustar00rootroot00000000000000name: Install GTest description: Install and setup GTest for linking and building test application runs: using: composite steps: - run: sudo apt-get install libgtest-dev lcov shell: bash - run: (cd /usr/src/gtest && sudo `which cmake` .) shell: bash - run: sudo make -j $(nproc) -C /usr/src/gtest shell: bash - run: sudo ln -s /usr/src/gtest/libgtest.a /usr/lib/libgtest.a shell: bash - run: sudo ln -s /usr/src/gtest/libgtest_main.a /usr/lib/libgtest_main.a shell: bash scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/kazuho-picojson/000077500000000000000000000000001511466717700267655ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/kazuho-picojson/action.yml000066400000000000000000000010061511466717700307620ustar00rootroot00000000000000name: Install PicoJSON description: Install PicoJSON for building test application inputs: version: description: The desired PicoJSON version to install required: false default: "v1.3.0" runs: using: composite steps: - run: | cd /tmp wget https://github.com/kazuho/picojson/archive/${{ inputs.version }}.tar.gz tar -zxf /tmp/${{ inputs.version }}.tar.gz cd picojson-${{ inputs.version }} sudo cp -v picojson.h /usr/local/include/picojson shell: bash scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/libressl/000077500000000000000000000000001511466717700254615ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/libressl/action.yml000066400000000000000000000012561511466717700274650ustar00rootroot00000000000000name: Install LibreSSL description: Install and setup LibreSSL for linking and building test application inputs: version: description: The desired LibreSSL version to install required: false default: "3.3.0" runs: using: composite steps: - run: | wget https://raw.githubusercontent.com/libressl-portable/portable/v${{ inputs.version }}/FindLibreSSL.cmake -P cmake/ cd /tmp wget https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${{ inputs.version }}.tar.gz tar -zvxf /tmp/libressl-${{ inputs.version }}.tar.gz cd libressl-${{ inputs.version }} ./configure sudo make -j $(nproc) install shell: bash scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/nlohmann-json/000077500000000000000000000000001511466717700264235ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/nlohmann-json/action.yml000066400000000000000000000010061511466717700304200ustar00rootroot00000000000000name: Install nlohmann-json description: Install nlohmann-json for building test application inputs: version: description: The desired nlohmann-json version to install required: false default: "3.11.2" runs: using: composite steps: - run: | cd /tmp wget https://github.com/nlohmann/json/archive/v${{ inputs.version }}.tar.gz tar -zxf /tmp/v${{ inputs.version }}.tar.gz cd json-${{ inputs.version }} cmake . sudo cmake --install . shell: bash scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/open-source-parsers-jsoncpp/000077500000000000000000000000001511466717700312305ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/open-source-parsers-jsoncpp/action.yml000066400000000000000000000015231511466717700332310ustar00rootroot00000000000000name: Install open-source-parsers/jsoncpp description: Install open-source-parsers/jsoncpp for building test application inputs: version: description: The desired open-source-parsers/jsoncpp version to install required: false default: "1.9.5" runs: using: composite steps: - run: | cd /tmp wget https://github.com/open-source-parsers/jsoncpp/archive/${{ inputs.version }}.tar.gz tar -zxf /tmp/${{ inputs.version }}.tar.gz cd jsoncpp-${{ inputs.version }} # https://github.com/open-source-parsers/jsoncpp/blob/69098a18b9af0c47549d9a271c054d13ca92b006/include/PreventInSourceBuilds.cmake#L8 mkdir build cd build cmake .. -DJSONCPP_WITH_TESTS=OFF -DBUILD_SHARED_LIBS=OFF -DBUILD_OBJECT_LIBS=OFF cmake --build . sudo cmake --install . shell: bash scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/openssl/000077500000000000000000000000001511466717700253255ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/openssl/action.yml000066400000000000000000000013051511466717700273240ustar00rootroot00000000000000name: Install OpenSSL description: Install and setup OpenSSL for linking and building test application inputs: version: description: The desired OpenSSL version to install required: false default: "openssl-3.0.0" runs: using: composite steps: - run: | cd /tmp wget https://github.com/openssl/openssl/archive/refs/tags/${{ inputs.version }}.tar.gz tar -zxf /tmp/${{ inputs.version }}.tar.gz cd openssl-${{ inputs.version }} ./config --prefix=/tmp --libdir=lib make -j $(nproc) sudo make -j $(nproc) install_sw echo "OPENSSL_ROOT_DIR=/tmp" >> "$GITHUB_ENV" echo "OpenSSL_ROOT=/tmp" >> "$GITHUB_ENV" shell: bash scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/wolfssl/000077500000000000000000000000001511466717700253335ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/install/wolfssl/action.yml000066400000000000000000000013601511466717700273330ustar00rootroot00000000000000name: Install wolfSSL description: Install and setup wolfSSL for linking and building test application inputs: version: description: The desired stable wolfSSL version to install required: false default: "v4.8.1-stable" runs: using: composite steps: - run: | cd /tmp wget -O wolfssl.tar.gz https://github.com/wolfSSL/wolfssl/archive/${{ inputs.version }}.tar.gz tar -zxf /tmp/wolfssl.tar.gz cd wolfssl-* autoreconf -fiv ./configure --enable-opensslall --enable-opensslextra --disable-examples --disable-crypttests --enable-harden --enable-all --enable-all-crypto make sudo make install shell: bash - run: sudo rm -rf /usr/include/openssl shell: bash scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/process-linting-results/000077500000000000000000000000001511466717700270135ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/process-linting-results/action.yml000066400000000000000000000017221511466717700310150ustar00rootroot00000000000000name: Process Linting Results description: Add a comment to a pull request with when `git diff` present and save the changes as an artifact so they can be applied manually inputs: linter_name: description: The name of the tool to credit in the comment required: true runs: using: "composite" steps: - run: git add --update shell: bash - id: stage #continue-on-error: true uses: Thalhammer/patch-generator-action@v3 # Unfortunately the previous action reports a failure so nothing else can run # partially a limitation on composite actions since `continue-on-error` is not # yet supported - if: steps.stage.outputs.result == 'dirty' uses: actions-ecosystem/action-create-comment@v1 with: github_token: ${{ github.token }} body: | Hello, @${{ github.actor }}! `${{ inputs.linter_name }}` had some concerns :scream: - run: exit $(git status -uno -s | wc -l) shell: bash scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/render/000077500000000000000000000000001511466717700234535ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/render/defaults/000077500000000000000000000000001511466717700252625ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/render/defaults/action.yml000066400000000000000000000046361511466717700272730ustar00rootroot00000000000000name: "Render `defaults.h` Template" description: "Generate the `defaults.h` header file for a JSON library" inputs: traits_name: description: "Name of the traits structure to be used. Typically in the format `author_repository` or equivilant" required: true library_name: description: "Name of the JSON library." required: true library_url: description: "URL to the JSON library." required: true disable_default_traits: description: "Set the macro to disable the default traits" required: false default: "true" outputs: file_path: description: "Relative path which the 'defaults.h' was written to" value: ${{ steps.script.outputs.result }} runs: using: composite steps: - uses: actions/setup-node@v3 with: node-version: 14 - run: npm install mustache shell: bash - uses: actions/github-script@v6 id: script env: TRAITS_NAME: ${{ inputs.traits_name }} LIBRARY_NAME: ${{ inputs.library_name }} LIBRARY_URL: ${{ inputs.library_url }} DISABLE_DEFAULT_TRAITS: ${{ inputs.disable_default_traits }} with: result-encoding: string script: | const mustache = require('mustache') const path = require('path') const fs = require('fs') const { TRAITS_NAME, LIBRARY_NAME, LIBRARY_URL, DISABLE_DEFAULT_TRAITS } = process.env console.log(`Rendering ${TRAITS_NAME}!`) const disableDefault = DISABLE_DEFAULT_TRAITS === 'true' const template = fs.readFileSync(path.join('include', 'jwt-cpp', 'traits', 'defaults.h.mustache'), 'utf8') const content = mustache.render(template, { traits_name: TRAITS_NAME, traits_name_upper: TRAITS_NAME.toUpperCase(), library_name: LIBRARY_NAME, library_url: LIBRARY_URL, disable_default_traits: disableDefault, }) // https://dmitripavlutin.com/replace-all-string-occurrences-javascript/ function replaceAll(string, search, replace) { return string.split(search).join(replace); } const outputDir = path.join('include', 'jwt-cpp', 'traits', replaceAll(TRAITS_NAME, '_', '-')) fs.mkdirSync(outputDir, { recursive: true }) const filePath = path.join(outputDir, 'defaults.h') fs.writeFileSync(filePath, content) return filePath scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/render/tests/000077500000000000000000000000001511466717700246155ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/actions/render/tests/action.yml000066400000000000000000000031521511466717700266160ustar00rootroot00000000000000name: "Render `TraitsTests.cpp` Template" description: "Generate the `TraitsTests.cpp` header file for a JSON library" inputs: traits_name: description: "Name of the traits structure to be used. Typically in the format `author_repository` or equivilant" required: true test_suite_name: description: "Name of the JSON library." required: true runs: using: composite steps: - uses: actions/setup-node@v3 with: node-version: 14 - run: npm install mustache shell: bash - uses: actions/github-script@v6 env: TRAITS_NAME: ${{ inputs.traits_name }} SUITE_NAME: ${{ inputs.test_suite_name }} with: script: | const mustache = require('mustache') const path = require('path') const fs = require('fs') const { TRAITS_NAME, SUITE_NAME } = process.env console.log(`Rendering ${TRAITS_NAME}!`) // https://dmitripavlutin.com/replace-all-string-occurrences-javascript/ function replaceAll(string, search, replace) { return string.split(search).join(replace); } const template = fs.readFileSync(path.join('tests', 'traits', 'TraitsTest.cpp.mustache'), 'utf8') const content = mustache.render(template, { traits_name: TRAITS_NAME, traits_dir: replaceAll(TRAITS_NAME, '_', '-'), test_suite_name: SUITE_NAME, }) const outputDir = path.join('tests', 'traits') fs.mkdirSync(outputDir, { recursive: true }) fs.writeFileSync(path.join(outputDir, `${SUITE_NAME}.cpp`), content) scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/logo.svg000066400000000000000000000065151511466717700222240ustar00rootroot00000000000000 scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/security.md000066400000000000000000000015371511466717700227330ustar00rootroot00000000000000# Reporting Security Issues If you believe you have found a security vulnerability in JWT-CPP, we encourage you to let us know right away. We will investigate all legitimate reports and do our best to quickly fix the problem. Please refer to the section below for our responsible disclosure policy: ## Disclosure Policy Please contact one or more of the maintainers using the email advertised on our GitHub profiles: - [@Thalhammer](https://github.com/Thalhammer) - [@prince-chrismc](https://github.com/prince-chrismc) Please provide as many details as possible about version, platform, and workflow as possible. Having steps and reproducible code is better and is always greatly appreciated. ## Supported Version Typically, fixes will be immediately released as a new patch release. However, older affected versions may receive a new patch upon request. scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/workflows/000077500000000000000000000000001511466717700225715ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/workflows/cmake.yml000066400000000000000000000155601511466717700244030ustar00rootroot00000000000000name: CMake CI on: push: branches: [master] pull_request: branches: [master] paths: - "CMakeLists.txt" - "cmake/**" - "include/jwt-cpp/**" - "tests/cmake/**" - ".github/actions/**" - ".github/workflows/cmake.yml" jobs: default-linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - name: setup run: | cmake --preset release sudo cmake --build --preset release --target install - name: test working-directory: tests/cmake run: | cmake . -DTEST:STRING="defaults-enabled" -DCMAKE_FIND_DEBUG_MODE=1 cmake --build . default-linux-with-examples: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - name: setup run: | mkdir build cd build cmake .. -DJWT_BUILD_EXAMPLES=ON sudo cmake --install . - name: test working-directory: tests/cmake run: | cmake . -DTEST:STRING="defaults-enabled" cmake --build . default-win: runs-on: windows-latest steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - run: choco install openssl - name: setup run: | cmake -E make_directory build cd build cmake .. -DJWT_BUILD_EXAMPLES=OFF cmake --install . - name: test run: | cd tests/cmake cmake . -DTEST:STRING="defaults-enabled" -DCMAKE_FIND_DEBUG_MODE=1 cmake --build . min-req: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 - uses: ./.github/actions/install/cmake with: version: "3.14.7" url: "https://cmake.org/files/v3.14/cmake-3.14.7.tar.gz" - uses: ./.github/actions/install/gtest - name: setup run: | mkdir build cd build cmake .. -DJWT_BUILD_EXAMPLES=ON -DJWT_BUILD_TESTS=ON sudo make install - name: test run: | cd tests/cmake cmake . -DTEST:STRING="defaults-enabled" cmake --build . preset-support-but-not-enough: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - uses: ./.github/actions/install/cmake with: version: "3.24.4" url: "https://cmake.org/files/v3.24/cmake-3.24.4.tar.gz" - uses: ./.github/actions/install/gtest - name: setup run: | mkdir build cd build cmake .. -DJWT_BUILD_EXAMPLES=ON -DJWT_BUILD_TESTS=ON sudo make install - name: test run: | cd tests/cmake cmake . -DTEST:STRING="defaults-enabled" cmake --build . custom-install-linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - name: setup run: | mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX:STRING="/opt/jwt-cpp" -DJWT_BUILD_EXAMPLES=OFF make install - name: test run: | cd tests/cmake cmake . -DCMAKE_PREFIX_PATH="/opt/jwt-cpp" -DTEST:STRING="defaults-enabled" -DCMAKE_FIND_DEBUG_MODE=1 cmake --build . root-hint-install-linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - name: setup run: | mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX:STRING="/opt/jwt-cpp" -DJWT_BUILD_EXAMPLES=OFF make install - name: test run: | cd tests/cmake cmake . -Djwt-cpp_ROOT="/opt/jwt-cpp" -DTEST:STRING="defaults-enabled" -DCMAKE_FIND_DEBUG_MODE=1 cmake --build . custom-install-win: runs-on: windows-latest steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - run: choco install openssl - name: setup run: | cmake -E make_directory build cd build cmake .. -DCMAKE_INSTALL_PREFIX:STRING="C:/jwt-cpp" -DJWT_BUILD_EXAMPLES=OFF cmake --install . - name: test run: | cd tests/cmake cmake . -DCMAKE_PREFIX_PATH="C:/jwt-cpp" -DTEST:STRING="defaults-enabled" -DCMAKE_FIND_DEBUG_MODE=1 cmake --build . no-pico: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - 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@v4 - 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 . with-libressl: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - uses: ./.github/actions/install/libressl - name: setup run: | mkdir build cd build cmake .. -DJWT_SSL_LIBRARY:STRING="LibreSSL" -DJWT_BUILD_EXAMPLES=OFF sudo make install - name: test run: | cd tests/cmake cmake . -DCMAKE_PREFIX_PATH=/usr/local/cmake -DCMAKE_MODULE_PATH=../../cmake -DTEST:STRING="libressl-is-used" cmake --build . with-wolfssl: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - uses: ./.github/actions/install/wolfssl with: version: ab3bbf11e9d39b52e24bf42bbc6babc16d4a669b - name: setup run: | mkdir build cd build cmake .. -DJWT_SSL_LIBRARY:STRING="wolfSSL" -DJWT_BUILD_EXAMPLES=OFF sudo make install - name: test run: | cd tests/cmake cmake . -DTEST:STRING="wolfssl-is-used" cmake --build . with-hunter: # This is actually testing the integration with the package management runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - name: setup run: | mkdir build cd build cmake .. -DJWT_BUILD_TESTS=ON -DJWT_BUILD_EXAMPLES=ON -DJWT_ENABLE_COVERAGE=OFF -DHUNTER_ENABLED=ON make - name: test run: | cd build ./tests/jwt-cpp-test scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/workflows/cross-platform.yml000066400000000000000000000013511511466717700262670ustar00rootroot00000000000000name: Cross-Platform CI on: push: branches: [master] pull_request: branches: [master] jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [macos-latest, windows-latest, ubuntu-latest] steps: - uses: actions/checkout@v4 - name: configure run: cmake --preset examples - name: build run: cmake --build --preset examples - name: test run: | cmake --build --preset examples --target rsa-create-run cmake --build --preset examples --target rsa-verify-run - if: github.event_name == 'push' && always() uses: ./.github/actions/badge with: category: cross-platform label: ${{ matrix.os }} scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/workflows/documentation.yml000066400000000000000000000011321511466717700261620ustar00rootroot00000000000000name: Documentation CI on: push: branches: [master] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ssciwr/doxygen-install@v1 with: version: "1.10.0" - run: sudo apt install graphviz - run: | cmake . -DJWT_BUILD_DOCS=ON cmake --build . --target jwt-docs - if: github.event_name == 'push' name: deploy uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./build/html force_orphan: true scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/workflows/jwt.yml000066400000000000000000000055201511466717700241220ustar00rootroot00000000000000name: JWT CI on: push: branches: [master] pull_request: branches: [master] jobs: coverage: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - uses: ./.github/actions/install/gtest - uses: ./.github/actions/install/danielaparker-jsoncons - uses: ./.github/actions/install/boost-json - uses: ./.github/actions/install/open-source-parsers-jsoncpp - name: configure run: cmake --preset coverage - name: run run: cmake --build --preset coverage - uses: coverallsapp/github-action@v2 with: github-token: ${{ secrets.GITHUB_TOKEN }} file: build/coverage.info format: lcov fuzzing: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - name: configure run: cmake --preset ci-fuzzing - name: build run: cmake --build --preset ci-fuzzing - name: run run: | cmake --build --preset ci-fuzzing --target jwt-cpp-fuzz-BaseEncodeFuzz-run cmake --build --preset ci-fuzzing --target jwt-cpp-fuzz-BaseDecodeFuzz-run cmake --build --preset ci-fuzzing --target jwt-cpp-fuzz-TokenDecodeFuzz-run asan: runs-on: ubuntu-latest strategy: fail-fast: false matrix: openssl: - { tag: "openssl-3.0.5", name: "3.0.5" } - { tag: "OpenSSL_1_1_1q", name: "1.1.1q" } steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - uses: ./.github/actions/install/gtest - uses: ./.github/actions/install/openssl with: version: ${{ matrix.openssl.tag }} - name: configure run: cmake --preset ci-asan - name: build run: cmake --build --preset ci-asan - name: run run: | cmake --build --preset ci-asan --target private-claims-run cmake --build --preset ci-asan --target rsa-create-run cmake --build --preset ci-asan --target rsa-verify-run cmake --build --preset ci-asan --target jwks-verify-run cmake --build --preset ci-asan --target jwt-cpp-test-run ubsan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - uses: ./.github/actions/install/gtest - name: configure run: cmake --preset ci-ubsan - name: build run: cmake --build --preset ci-ubsan -DCMAKE_CXX_STANDARD=20 - name: run run: | cmake --build --preset ci-ubsan --target private-claims-run cmake --build --preset ci-ubsan --target rsa-create-run cmake --build --preset ci-ubsan --target rsa-verify-run cmake --build --preset ci-ubsan --target jwks-verify-run cmake --build --preset ci-ubsan --target jwt-cpp-test-run scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/workflows/lint.yml000066400000000000000000000104731511466717700242670ustar00rootroot00000000000000name: Lint CI on: push: branches: [master] pull_request: branches: [master] jobs: clang-format: runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: files: - "include/jwt-cpp/*.h" - "include/jwt-cpp/traits/**/*.h" - "tests/*.cpp" - "tests/**/*.cpp" - "example/*.cpp" - "example/**/*.cpp" steps: - run: | sudo apt-get install clang-format-14 shopt -s globstar - uses: actions/checkout@v4 - run: clang-format-14 -i ${{ matrix.files }} - uses: ./.github/actions/process-linting-results with: linter_name: clang-format cmake-format: runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: files: ["**/CMakeLists.txt", "cmake/code-coverage.cmake"] steps: - uses: actions/setup-python@v5 with: python-version: "3.x" - run: pip install cmakelang - run: shopt -s globstar - uses: actions/checkout@v4 - run: cmake-format -i ${{ matrix.files }} - uses: ./.github/actions/process-linting-results with: linter_name: cmake-format clang-tidy: runs-on: ubuntu-20.04 steps: - run: sudo apt-get install clang-tidy - uses: lukka/get-cmake@latest - uses: actions/checkout@v4 - name: configure run: cmake --preset examples -DCMAKE_CXX_CLANG_TIDY="clang-tidy;-fix" - name: run run: cmake --build --preset examples - uses: ./.github/actions/process-linting-results with: linter_name: clang-tidy render-defaults: runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: traits: - { name: "boost_json", library: "Boost.JSON", url: "https://github.com/boostorg/json", disable_pico: true } - { name: "danielaparker_jsoncons", library: "jsoncons", url: "https://github.com/danielaparker/jsoncons", disable_pico: true } - { name: "kazuho_picojson", library: "picojson", url: "https://github.com/kazuho/picojson", disable_pico: false } - { name: "nlohmann_json", library: "JSON for Modern C++", url: "https://github.com/nlohmann/json", disable_pico: true } - { name: "open_source_parsers_jsoncpp", library: "jsoncpp", url: "https://github.com/open-source-parsers/jsoncpp", disable_pico: true } name: render-defaults (${{ matrix.traits.name }}) steps: - uses: actions/checkout@v4 - run: | sudo apt-get install clang-format-14 - uses: ./.github/actions/render/defaults id: render with: traits_name: ${{ matrix.traits.name }} library_name: ${{ matrix.traits.library }} library_url: ${{ matrix.traits.url }} disable_default_traits: ${{ matrix.traits.disable_pico }} - run: clang-format-14 -i ${{ steps.render.outputs.file_path }} - run: git add ${{ steps.render.outputs.file_path }} - uses: ./.github/actions/process-linting-results with: linter_name: render-defaults render-tests: runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: traits: # - { name: "boost_json", suite: "BoostJsonTest" } # Currently needs work arounds for API limitations - { name: "danielaparker_jsoncons", suite: "JsonconsTest" } # - { name: "kazuho_picojson", suite: "PicoJsonTest" } # Currently the default everything tests against this! - { name: "nlohmann_json", suite: "NlohmannTest" } - { name: "open_source_parsers_jsoncpp", suite: "OspJsoncppTest" } name: render-tests (${{ matrix.traits.name }}) steps: - uses: actions/checkout@v4 - run: | sudo apt-get install clang-format-14 shopt -s globstar - uses: ./.github/actions/render/tests with: traits_name: ${{ matrix.traits.name }} test_suite_name: ${{ matrix.traits.suite }} - run: clang-format-14 -i tests/**/*.cpp - run: git add tests/traits/* - uses: ./.github/actions/process-linting-results with: linter_name: render-tests line-ending: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 - run: git add --renormalize . - uses: ./.github/actions/process-linting-results with: linter_name: line-ending scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/workflows/release.yml000066400000000000000000000027241511466717700247410ustar00rootroot00000000000000name: Release CD on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release release: types: [published] jobs: nuget: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup NuGet uses: NuGet/setup-nuget@v1 with: nuget-api-key: ${{ secrets.nuget_api_key }} - name: Create NuGet pkg working-directory: ./nuget run: nuget pack jwt-cpp.nuspec - name: Publish NuGet pkg working-directory: ./nuget run: nuget push *.nupkg -Source 'https://api.nuget.org/v3/index.json' release-asset: if: github.event_name != 'workflow_dispatch' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: tar --exclude='./.git' -vczf /tmp/jwt-cpp-${{ github.event.release.tag_name }}.tar.gz . - uses: shogo82148/actions-upload-release-asset@v1 with: upload_url: ${{ github.event.release.upload_url }} asset_path: /tmp/jwt-cpp-${{ github.event.release.tag_name }}.tar.gz - run: zip -x './.git/*' -r /tmp/jwt-cpp-${{ github.event.release.tag_name }}.zip . - uses: shogo82148/actions-upload-release-asset@v1 with: upload_url: ${{ github.event.release.upload_url }} asset_path: /tmp/jwt-cpp-${{ github.event.release.tag_name }}.zip scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/workflows/ssl.yml000066400000000000000000000067311511466717700241240ustar00rootroot00000000000000name: SSL Compatibility CI on: push: branches: [master] pull_request: branches: [master] jobs: openssl: runs-on: ubuntu-latest strategy: matrix: openssl: - { tag: "openssl-3.0.5", name: "3.0.5" } - { tag: "OpenSSL_1_1_1q", name: "1.1.1q" } - { tag: "OpenSSL_1_1_0i", name: "1.1.0i" } # Do not bump, there's a broken in the autoconfig script and it's not maintained - { tag: "OpenSSL_1_0_2u", name: "1.0.2u" } - { tag: "OpenSSL_1_0_1u", name: "1.0.1u" } name: OpenSSL ${{ matrix.openssl.name }} steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - uses: ./.github/actions/install/gtest - uses: ./.github/actions/install/openssl with: version: ${{ matrix.openssl.tag }} - name: configure run: cmake --preset unit-tests -DOPENSSL_ROOT_DIR=/tmp - run: cmake --build --preset unit-tests - name: test run: ctest --preset unit-tests --output-on-failure - if: github.event_name == 'push' && always() uses: ./.github/actions/badge with: category: openssl label: ${{ matrix.openssl.name }} openssl-no-deprecated: runs-on: ubuntu-latest name: OpenSSL 3.0 No Deprecated steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - uses: ./.github/actions/install/gtest - uses: ./.github/actions/install/openssl with: version: "openssl-3.0.5" - name: configure run: cmake --preset unit-tests -DOPENSSL_ROOT_DIR=/tmp -DCMAKE_CXX_FLAGS="-DOPENSSL_NO_DEPRECATED=1" -DCMAKE_C_FLAGS="-DOPENSSL_NO_DEPRECATED=1" - run: cmake --build --preset unit-tests - name: test run: ctest --preset unit-tests libressl: runs-on: ubuntu-latest strategy: fail-fast: false matrix: libressl: ["3.5.3", "3.4.3", "3.3.6"] name: LibreSSL ${{ matrix.libressl }} steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - uses: ./.github/actions/install/gtest - uses: ./.github/actions/install/libressl with: version: ${{ matrix.libressl }} - name: configure run: cmake --preset unit-tests -DJWT_SSL_LIBRARY:STRING=LibreSSL - run: cmake --build --preset unit-tests - name: test run: ctest --preset unit-tests - if: github.event_name == 'push' && always() uses: ./.github/actions/badge with: category: libressl label: ${{ matrix.libressl }} wolfssl: runs-on: ubuntu-latest strategy: matrix: wolfssl: - { ref: "v5.1.1-stable", name: "5.1.1"} - { ref: "v5.2.0-stable", name: "5.2.0" } - { ref: "v5.3.0-stable", name: "5.3.0"} - { ref: "v5.7.0-stable", name: "5.7.0"} name: wolfSSL ${{ matrix.wolfssl.name }} steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - uses: ./.github/actions/install/gtest - uses: ./.github/actions/install/wolfssl with: version: ${{ matrix.wolfssl.ref }} - name: configure run: cmake --preset unit-tests -DJWT_SSL_LIBRARY:STRING=wolfSSL - run: cmake --build --preset unit-tests - name: test run: ctest --preset unit-tests - if: github.event_name == 'push' && always() uses: ./.github/actions/badge with: category: wolfssl label: ${{ matrix.wolfssl.name }} scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/workflows/targets.yml000066400000000000000000000040541511466717700247700ustar00rootroot00000000000000name: Specific Targets CI on: push: branches: [master] pull_request: branches: [master] paths: - "CMakeLists.txt" - "cmake/**" - "include/jwt-cpp/**" - "tests/cmake/**" - ".github/actions/**" - ".github/workflows/targets.yml" jobs: gcc-4-8: if: false # There's no existing image with node20 and gcc4.8 https://github.com/actions/checkout/issues/1809 name: GCC 4.8 runs-on: ubuntu-latest container: image: ubuntu:bionic-20230530 # 18.04 env: CC: /usr/bin/gcc-4.8 CXX: /usr/bin/g++-4.8 steps: - run: | apt-get update apt-get install -y g++-4.8 wget make libssl-dev - uses: actions/checkout@v3 # Can not be upgrade as v4 needs NodeJS 20 doesn't exist next to gcc-4.8 - uses: ./.github/actions/install/cmake with: version: "3.26.3" url: "https://cmake.org/files/v3.26/cmake-3.26.3.tar.gz" - name: setup run: | mkdir build cd build cmake .. cmake --build . cmake --install . - name: test working-directory: tests/cmake run: | CC=gcc-4.8 CXX=g++-4.8 cmake . -DTEST:STRING="defaults-enabled" cmake --build . gcc-12: name: GCC 12 runs-on: ubuntu-latest container: image: ubuntu:jammy-20231004 # 22.04 env: CC: /usr/bin/gcc-12 CXX: /usr/bin/g++-12 steps: - run: | apt-get update apt-get install -y g++-12 wget make libssl-dev - uses: actions/checkout@v4 - uses: ./.github/actions/install/cmake with: version: "3.26.3" url: "https://cmake.org/files/v3.26/cmake-3.26.3.tar.gz" - name: setup run: | mkdir build cd build cmake .. cmake --build . cmake --install . - name: test working-directory: tests/cmake run: | CC=gcc-12 CXX=g++-12 cmake . -DCMAKE_CXX_STANDARD=20 -DTEST:STRING="defaults-enabled" cmake --build . scitokens-cpp-1.2.0/vendor/jwt-cpp/.github/workflows/traits.yml000066400000000000000000000053761511466717700246350ustar00rootroot00000000000000name: Traits CI on: push: branches: [master] pull_request: branches: [master] jobs: traits: name: Traits (${{ matrix.target.name }}) runs-on: ubuntu-latest strategy: matrix: target: - { name: "danielaparker-jsoncons", tag: "0.168.7", version: "v0.168.7" } - { name: "boost-json", tag: "1.78.0", version: "v1.80.0" } - { name: "nlohmann-json", tag: "3.11.2", version: "v3.11.2" } - { name: "kazuho-picojson", tag: "111c9be5188f7350c2eac9ddaedd8cca3d7bf394", version: "111c9be" } - { name: "open-source-parsers-jsoncpp", tag: "1.9.5", version: "v1.9.5" } steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest - name: setup run: | mkdir build cd build cmake .. -DJWT_BUILD_EXAMPLES=OFF sudo cmake --install . # Install the JSON library - if: matrix.target.name == 'danielaparker-jsoncons' uses: ./.github/actions/install/danielaparker-jsoncons with: version: ${{matrix.target.tag}} - if: matrix.target.name == 'boost-json' uses: ./.github/actions/install/boost-json with: version: ${{matrix.target.tag}} - if: matrix.target.name == 'nlohmann-json' uses: ./.github/actions/install/nlohmann-json with: version: ${{matrix.target.tag}} - if: matrix.target.name == 'kazuho-picojson' run: rm -rf include/picojson - if: matrix.target.name == 'kazuho-picojson' uses: ./.github/actions/install/kazuho-picojson with: version: ${{matrix.target.tag}} - if: matrix.target.name == 'open-source-parsers-jsoncpp' uses: ./.github/actions/install/open-source-parsers-jsoncpp with: version: ${{matrix.target.tag}} - name: test working-directory: example/traits run: | cmake . -DCMAKE_FIND_DEBUG_MODE=1 cmake --build . --target ${{ matrix.target.name }} ./${{ matrix.target.name }} - name: badge success if: github.event_name == 'push' && success() uses: ./.github/actions/badge/write with: category: traits label: ${{ matrix.target.name }} message: ${{ matrix.target.version }} color: lightblue # turquoise - name: badge failure if: github.event_name == 'push' && !success() uses: ./.github/actions/badge/write with: category: traits label: ${{ matrix.target.name }} message: ${{ matrix.target.version }} color: orange - if: github.event_name == 'push' && always() uses: ./.github/actions/badge/publish with: github_token: ${{ secrets.GITHUB_TOKEN }} scitokens-cpp-1.2.0/vendor/jwt-cpp/.gitignore000066400000000000000000000117431511466717700211720ustar00rootroot00000000000000## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX project.lock.json artifacts/ *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.pfx *.publishsettings node_modules/ orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # ========================= # Operating System Files # ========================= # OSX # ========================= .DS_Store .AppleDouble .LSOverride # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk # Windows # ========================= # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Windows Installer files *.cab *.msi *.msm *.msp # Windows shortcuts *.lnk # Linux files test *.o *.o.d # IDE-specific files .vscode/ .vscode/!extensions.json # Allow to provide recommended extensions # ClangD cache files .cache build/* package-lock.json CMakeUserPresets.json scitokens-cpp-1.2.0/vendor/jwt-cpp/CMakeLists.txt000066400000000000000000000163311511466717700217400ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.14) cmake_policy(VERSION 3.14) if(POLICY CMP0135) # DOWNLOAD_EXTRACT_TIMESTAMP cmake_policy(SET CMP0135 OLD) endif() # HUNTER_ENABLED is always set if this package is included in a project using hunter (HunterGate sets it) In this case # we will use hunter as well to stay consistent. If not the use can supply it on configure to force using hunter. if(HUNTER_ENABLED) include("cmake/HunterGate.cmake") huntergate(URL "https://github.com/cpp-pm/hunter/archive/v0.23.314.tar.gz" SHA1 "95c47c92f68edb091b5d6d18924baabe02a6962a") message(STATUS "jwt-cpp: using hunter for dependency resolution") endif() project(jwt-cpp LANGUAGES CXX) 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_BUILD_DOCS "Adds a target for building the doxygen documentation" OFF) option(JWT_ENABLE_COVERAGE "Enable code coverage testing" OFF) option(JWT_ENABLE_FUZZING "Enable fuzz testing" OFF) option(JWT_DISABLE_PICOJSON "Do not provide the picojson template specialiaze" OFF) option(JWT_DISABLE_BASE64 "Do not include the base64 implementation from this library" OFF) include(CMakeDependentOption) cmake_dependent_option(JWT_EXTERNAL_PICOJSON "Use find_package() to locate picojson, provided to integrate with package managers" OFF "NOT JWT_DISABLE_PICOJSON" OFF) cmake_dependent_option(JWT_EXTERNAL_NLOHMANN_JSON "Use find_package() to locate nlohman-json required for tests and examples" OFF "JWT_BUILD_EXAMPLES OR JWT_BUILD_TESTS" OFF) set(JWT_SSL_LIBRARY_OPTIONS OpenSSL LibreSSL wolfSSL) set(JWT_SSL_LIBRARY OpenSSL CACHE STRING "Determines which SSL library to build with") set_property(CACHE JWT_SSL_LIBRARY PROPERTY STRINGS ${JWT_SSL_LIBRARY_OPTIONS}) set(JWT_JSON_TRAITS_OPTIONS boost-json danielaparker-jsoncons kazuho-picojson nlohmann-json open-source-parsers-jsoncpp) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") if(NOT JWT_SSL_LIBRARY IN_LIST JWT_SSL_LIBRARY_OPTIONS) message(FATAL_ERROR "JWT_SSL_LIBRARY must be one of ${JWT_SSL_LIBRARY_OPTIONS}") endif() # If Hunter is enabled, we configure it to resolve OpenSSL and warn the user if he selected an option not supported by # hunter. We fall back to the system library in this case. if(HUNTER_ENABLED) if(${JWT_SSL_LIBRARY} MATCHES "OpenSSL") hunter_add_package(OpenSSL) elseif(${JWT_SSL_LIBRARY} MATCHES "LibreSSL") message(WARNING "Hunter does not support LibreSSL yet, the system library will be used (if available)") elseif(${JWT_SSL_LIBRARY} MATCHES "wolfSSL") message(WARNING "Hunter does not support wolfSSL yet, the system library will be used (if available)") endif() if(JWT_EXTERNAL_PICOJSON) message(WARNING "Hunter does not support picojson yet, the system library will be used (if available)") endif() endif() # Lookup dependencies if(${JWT_SSL_LIBRARY} MATCHES "OpenSSL") find_package(OpenSSL 1.0.1 REQUIRED) elseif(${JWT_SSL_LIBRARY} MATCHES "LibreSSL") find_package(LibreSSL 3.0.0 REQUIRED) elseif(${JWT_SSL_LIBRARY} MATCHES "wolfSSL") find_package(PkgConfig REQUIRED) pkg_check_modules(wolfssl REQUIRED IMPORTED_TARGET wolfssl) list(TRANSFORM wolfssl_INCLUDE_DIRS APPEND "/wolfssl") # This is required to access OpenSSL compatibility API endif() if(NOT JWT_DISABLE_PICOJSON AND JWT_EXTERNAL_PICOJSON) find_package(picojson 1.3.0 REQUIRED) endif() if(JWT_BUILD_EXAMPLES OR JWT_BUILD_TESTS) if(JWT_EXTERNAL_NLOHMANN_JSON) message(STATUS "jwt-cpp: using find_package for nlohmann-json required for tests") find_package(nlohmann_json CONFIG REQUIRED) else() message(STATUS "jwt-cpp: using FetchContent for nlohmann-json required for tests") include(FetchContent) fetchcontent_declare(nlohmann_json URL https://github.com/nlohmann/json/releases/download/v3.11.2/json.tar.xz URL_MD5 127794b2c82c0c5693805feaa2a703e2) fetchcontent_makeavailable(nlohmann_json) endif() endif() set(JWT_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include) set(JWT_HEADER_FILES ${JWT_INCLUDE_PATH}/jwt-cpp/jwt.h) foreach(traits ${JWT_JSON_TRAITS_OPTIONS}) list(APPEND JWT_HEADER_FILES ${JWT_INCLUDE_PATH}/jwt-cpp/traits/${traits}/defaults.h ${JWT_INCLUDE_PATH}/jwt-cpp/traits/${traits}/traits.h) endforeach() 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(JWT_DISABLE_BASE64) target_compile_definitions(jwt-cpp INTERFACE JWT_DISABLE_BASE64) endif() if(JWT_DISABLE_PICOJSON) target_compile_definitions(jwt-cpp INTERFACE JWT_DISABLE_PICOJSON) endif() include(GNUInstallDirs) include(CMakePackageConfigHelpers) target_include_directories(jwt-cpp INTERFACE $ $) if(${JWT_SSL_LIBRARY} MATCHES "OpenSSL") target_link_libraries(jwt-cpp INTERFACE OpenSSL::SSL OpenSSL::Crypto) endif() if(${JWT_SSL_LIBRARY} MATCHES "LibreSSL") target_link_libraries(jwt-cpp INTERFACE LibreSSL::TLS) endif() if(${JWT_SSL_LIBRARY} MATCHES "wolfSSL") target_link_libraries(jwt-cpp INTERFACE PkgConfig::wolfssl) # This is required to access OpenSSL compatibility API target_include_directories(jwt-cpp INTERFACE ${wolfssl_INCLUDE_DIRS}) # This flag is required to have the mandatory header included automatically # https://github.com/Thalhammer/jwt-cpp/pull/352#discussion_r1627971786 # https://github.com/wolfSSL/wolfssl/blob/3b74a6402998a8b8839e25e31ba8ac74749aa9b0/wolfssl/wolfcrypt/settings.h#L58 target_compile_definitions(jwt-cpp INTERFACE EXTERNAL_OPTS_OPENVPN) endif() if(NOT JWT_DISABLE_PICOJSON AND JWT_EXTERNAL_PICOJSON) target_link_libraries(jwt-cpp INTERFACE picojson::picojson>) endif() # Hunter needs relative paths so the files are placed correctly if(NOT JWT_CMAKE_FILES_INSTALL_DIR) set(JWT_CMAKE_FILES_INSTALL_DIR cmake) endif() configure_package_config_file( ${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}) write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-config-version.cmake VERSION 0.7.1 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(DIRECTORY ${JWT_INCLUDE_PATH}/jwt-cpp DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 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(JWT_BUILD_EXAMPLES) add_subdirectory(example) endif() if(JWT_BUILD_TESTS) enable_testing() add_subdirectory(tests) endif() if(JWT_ENABLE_FUZZING) add_subdirectory(tests/fuzz) endif() if(JWT_BUILD_DOCS) add_subdirectory(docs) endif() scitokens-cpp-1.2.0/vendor/jwt-cpp/CMakePresets.json000066400000000000000000000013461511466717700224210ustar00rootroot00000000000000{ "version": 6, "cmakeMinimumRequired": { "major": 3, "minor": 25, "patch": 0 }, "include": [ "example/CMakePresets.json", "tests/CMakePresets.json" ], "configurePresets": [ { "name": "dev", "displayName": "Development", "inherits": "debug", "environment": { "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" }, "cacheVariables": { "JWT_BUILD_EXAMPLES": "ON", "JWT_BUILD_TESTS": "ON" } } ], "buildPresets": [ { "name": "dev", "configurePreset": "dev", "configuration": "Debug" } ] }scitokens-cpp-1.2.0/vendor/jwt-cpp/LICENSE000066400000000000000000000020631511466717700202020ustar00rootroot00000000000000MIT License Copyright (c) 2018 Dominik Thalhammer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. scitokens-cpp-1.2.0/vendor/jwt-cpp/README.md000066400000000000000000000174751511466717700204710ustar00rootroot00000000000000logo [![License Badge](https://img.shields.io/github/license/Thalhammer/jwt-cpp)](https://github.com/Thalhammer/jwt-cpp/blob/master/LICENSE) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/5f7055e294744901991fd0a1620b231d)](https://app.codacy.com/gh/Thalhammer/jwt-cpp/dashboard) [![Linux Badge][Linux]][Cross-Platform] [![MacOS Badge][MacOS]][Cross-Platform] [![Windows Badge][Windows]][Cross-Platform] [![Coverage Status](https://coveralls.io/repos/github/Thalhammer/jwt-cpp/badge.svg?branch=master)](https://coveralls.io/github/Thalhammer/jwt-cpp?branch=master) [![Documentation Badge](https://img.shields.io/badge/Documentation-master-blue)](https://thalhammer.github.io/jwt-cpp/) [![Stars Badge](https://img.shields.io/github/stars/Thalhammer/jwt-cpp?style=flat)](https://github.com/Thalhammer/jwt-cpp/stargazers) [![GitHub release (latest SemVer including pre-releases)](https://img.shields.io/github/v/release/Thalhammer/jwt-cpp?include_prereleases)](https://github.com/Thalhammer/jwt-cpp/releases) [![ConanCenter package](https://repology.org/badge/version-for-repo/conancenter/jwt-cpp.svg)](https://repology.org/project/jwt-cpp/versions) [![Vcpkg package](https://repology.org/badge/version-for-repo/vcpkg/jwt-cpp.svg)](https://repology.org/project/jwt-cpp/versions) [Linux]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/cross-platform/ubuntu-latest/shields.json [MacOS]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/cross-platform/macos-latest/shields.json [Windows]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/cross-platform/windows-latest/shields.json [Cross-Platform]: https://github.com/Thalhammer/jwt-cpp/actions?query=workflow%3A%22Cross-Platform+CI%22 ## Overview A header only library for creating and validating [JSON Web Tokens](https://tools.ietf.org/html/rfc7519) in C++11. For a great introduction, [read this](https://jwt.io/introduction/). The objective is to deliver a versatile and universally applicable collection of algorithms, classes, and data structures, fostering adaptability and seamless integration with other libraries that you may already be employing. ## Signature algorithms jwt-cpp comprehensively supports all algorithms specified in the standard. Its modular design facilitates the seamless [inclusion of additional algorithms](docs/signing.md#custom-signature-algorithms) without encountering any complications. Should you wish to contribute new algorithms, feel free to initiate a pull request or [open an issue](https://github.com/Thalhammer/jwt-cpp/issues/new). For completeness, here is a list of all supported algorithms: | HMSC | RSA | ECDSA | PSS | EdDSA | | ----- | ----- | ------ | ----- | ------- | | HS256 | RS256 | ES256 | PS256 | Ed25519 | | HS384 | RS384 | ES384 | PS384 | Ed448 | | HS512 | RS512 | ES512 | PS512 | | | | | ES256K | | | ## Getting Started Installation instructions can be found [here](docs/install.md). A simple example is decoding a token and printing all of its [claims](https://tools.ietf.org/html/rfc7519#section-4) let's ([try it out](https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCIsInNhbXBsZSI6InRlc3QifQ.lQm3N2bVlqt2-1L-FsOjtR6uE-L4E9zJutMWKIe1v1M)): ```cpp #include #include int main() { std::string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCIsInNhbXBsZSI6InRlc3QifQ.lQm3N2bVlqt2-1L-FsOjtR6uE-L4E9zJutMWKIe1v1M"; auto decoded = jwt::decode(token); for(auto& e : decoded.get_payload_json()) std::cout << e.first << " = " << e.second << std::endl; } ``` You can build and run [this example](example/print-claims.cpp) locally after cloning the repository. Running some commands, we can see the contents of the [JWT payload](https://datatracker.ietf.org/doc/html/rfc7519#section-3) ```sh cmake . cmake --build . --target print-claims ./print-claims # iss = "auth0" # sample = "test" ``` You'll very quickly notice JWT are not encrypted but rather cryptographically signed to provide [non-repudiation](https://csrc.nist.gov/glossary/term/non_repudiation). In order to verify a token you first build a verifier and use it to verify a decoded token. ```cpp auto verifier = jwt::verify() .with_issuer("auth0") .with_claim("sample", jwt::claim(std::string("test"))) .allow_algorithm(jwt::algorithm::hs256{"secret"}); verifier.verify(decoded_token); ``` The verifier is stateless so you can reuse it for different tokens. Creating the token above (and signing it) is equally as easy. ```cpp auto token = jwt::create() .set_type("JWS") .set_issuer("auth0") .set_payload_claim("sample", jwt::claim(std::string("test"))) .sign(jwt::algorithm::hs256{"secret"}); ``` If you are looking to issue or verify more unique tokens, checkout out the [examples](https://github.com/Thalhammer/jwt-cpp/tree/master/example) working with RSA public and private keys, elliptic curve tokens, and much more! ### Configuration Options Building on the goal of providing flexibility. #### SSL Compatibility jwt-cpp supports [OpenSSL](https://github.com/openssl/openssl), [LibreSSL](https://github.com/libressl-portable/portable), and [wolfSSL](https://github.com/wolfSSL/wolfssl). For a listed of tested versions, check [this page](docs/ssl.md) for more details. #### JSON Implementation There is no strict reliance on a specific JSON library in this context. Instead, the jwt-cpp utilizes a generic `jwt::basic_claim` that is templated based on type trait. This trait provides the semantic [JSON types](https://json-schema.org/understanding-json-schema/reference/type.html) for values, objects, arrays, strings, numbers, integers, and booleans, along with methods to seamlessly translate between them. This design offers flexibility in choosing the JSON library that best suits your needs. To leverage one of the provided JSON traits, refer to [docs/traits.md](docs/traits.md#selecting-a-json-library) for detailed guidance. ##### Providing your own JSON Traits ```cpp jwt::basic_claim claim(json::object({{"json", true},{"example", 0}})); ``` To learn how to writes a trait's implementation, checkout the [these instructions](docs/traits.md#providing-your-own-json-traits) #### Base64 Options With regard to the base64 specifications for JWTs, this library includes `base.h` encompassing all necessary variants. While the library itself offers a proficient base64 implementation, it's worth noting that base64 implementations are widely available, exhibiting diverse performance levels. If you prefer to use your own base64 implementation, you have the option to define `JWT_DISABLE_BASE64` to exclude the jwt-cpp implementation. ## Contributing If you have suggestions for improvement or if you've identified a bug, please don't hesitate to [open an issue](https://github.com/Thalhammer/jwt-cpp/issues/new) or contribute by creating a pull request. When reporting a bug, provide comprehensive details about your environment, including compiler version and other relevant information, to facilitate issue reproduction. Additionally, if you're introducing a new feature, ensure that you include corresponding test cases to validate its functionality. ### Dependencies In order to use jwt-cpp you need the following tools. * libcrypto (openssl or compatible) * libssl-dev (for the header files) * a compiler supporting at least c++11 * basic stl support In order to build the test cases you also need * gtest * pthread ## Troubleshooting See the [FAQs](docs/faqs.md) for tips. ## Conference Coverage [![CppCon](https://img.youtube.com/vi/Oq4NW5idmiI/0.jpg)](https://www.youtube.com/watch?v=Oq4NW5idmiI) scitokens-cpp-1.2.0/vendor/jwt-cpp/cmake/000077500000000000000000000000001511466717700202545ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/cmake/CMakePresets.json000066400000000000000000000022211511466717700234720ustar00rootroot00000000000000{ "version": 6, "cmakeMinimumRequired": { "major": 3, "minor": 25, "patch": 0 }, "configurePresets": [ { "name": "default", "displayName": "Default Config", "hidden": true, "binaryDir": "${sourceDir}/build", "cacheVariables": { "JWT_BUILD_EXAMPLES": "OFF", "JWT_BUILD_TESTS": "OFF" } }, { "name": "debug", "displayName": "Debug", "inherits": "default", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } }, { "name": "release", "displayName": "Release", "inherits": "default", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } } ], "buildPresets": [ { "name": "debug", "configurePreset": "debug", "configuration": "Debug" }, { "name": "release", "configurePreset": "release", "configuration": "Release" } ] }scitokens-cpp-1.2.0/vendor/jwt-cpp/cmake/HunterGate.cmake000066400000000000000000000412411511466717700233260ustar00rootroot00000000000000# Copyright (c) 2013-2019, Ruslan Baratov # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # This is a gate file to Hunter package manager. # Include this file using `include` command and add package you need, example: # # cmake_minimum_required(VERSION 3.2) # # include("cmake/HunterGate.cmake") # HunterGate( # URL "https://github.com/path/to/hunter/archive.tar.gz" # SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d" # ) # # project(MyProject) # # hunter_add_package(Foo) # hunter_add_package(Boo COMPONENTS Bar Baz) # # Projects: # * https://github.com/hunter-packages/gate/ # * https://github.com/ruslo/hunter option(HUNTER_ENABLED "Enable Hunter package manager support" ON) if(HUNTER_ENABLED) if(CMAKE_VERSION VERSION_LESS "3.2") message( FATAL_ERROR "At least CMake version 3.2 required for Hunter dependency management." " Update CMake or set HUNTER_ENABLED to OFF." ) endif() endif() include(CMakeParseArguments) # cmake_parse_arguments option(HUNTER_STATUS_PRINT "Print working status" ON) option(HUNTER_STATUS_DEBUG "Print a lot info" OFF) option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON) set(HUNTER_ROOT "" CACHE FILEPATH "Override the HUNTER_ROOT.") set(HUNTER_ERROR_PAGE "https://hunter.readthedocs.io/en/latest/reference/errors") function(hunter_gate_status_print) if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG) foreach(print_message ${ARGV}) message(STATUS "[hunter] ${print_message}") endforeach() endif() endfunction() function(hunter_gate_status_debug) if(HUNTER_STATUS_DEBUG) foreach(print_message ${ARGV}) string(TIMESTAMP timestamp) message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}") endforeach() endif() endfunction() function(hunter_gate_error_page error_page) message("------------------------------ ERROR ------------------------------") message(" ${HUNTER_ERROR_PAGE}/${error_page}.html") message("-------------------------------------------------------------------") message("") message(FATAL_ERROR "") endfunction() function(hunter_gate_internal_error) message("") foreach(print_message ${ARGV}) message("[hunter ** INTERNAL **] ${print_message}") endforeach() message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") message("") hunter_gate_error_page("error.internal") endfunction() function(hunter_gate_fatal_error) cmake_parse_arguments(hunter "" "ERROR_PAGE" "" "${ARGV}") if("${hunter_ERROR_PAGE}" STREQUAL "") hunter_gate_internal_error("Expected ERROR_PAGE") endif() message("") foreach(x ${hunter_UNPARSED_ARGUMENTS}) message("[hunter ** FATAL ERROR **] ${x}") endforeach() message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") message("") hunter_gate_error_page("${hunter_ERROR_PAGE}") endfunction() function(hunter_gate_user_error) hunter_gate_fatal_error(${ARGV} ERROR_PAGE "error.incorrect.input.data") endfunction() function(hunter_gate_self root version sha1 result) string(COMPARE EQUAL "${root}" "" is_bad) if(is_bad) hunter_gate_internal_error("root is empty") endif() string(COMPARE EQUAL "${version}" "" is_bad) if(is_bad) hunter_gate_internal_error("version is empty") endif() string(COMPARE EQUAL "${sha1}" "" is_bad) if(is_bad) hunter_gate_internal_error("sha1 is empty") endif() string(SUBSTRING "${sha1}" 0 7 archive_id) if(EXISTS "${root}/cmake/Hunter") set(hunter_self "${root}") else() set( hunter_self "${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked" ) endif() set("${result}" "${hunter_self}" PARENT_SCOPE) endfunction() # Set HUNTER_GATE_ROOT cmake variable to suitable value. function(hunter_gate_detect_root) # Check CMake variable if(HUNTER_ROOT) set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE) hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable") return() endif() # Check environment variable if(DEFINED ENV{HUNTER_ROOT}) set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE) hunter_gate_status_debug("HUNTER_ROOT detected by environment variable") return() endif() # Check HOME environment variable if(DEFINED ENV{HOME}) set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE) hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable") return() endif() # Check SYSTEMDRIVE and USERPROFILE environment variable (windows only) if(WIN32) if(DEFINED ENV{SYSTEMDRIVE}) set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE) hunter_gate_status_debug( "HUNTER_ROOT set using SYSTEMDRIVE environment variable" ) return() endif() if(DEFINED ENV{USERPROFILE}) set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE) hunter_gate_status_debug( "HUNTER_ROOT set using USERPROFILE environment variable" ) return() endif() endif() hunter_gate_fatal_error( "Can't detect HUNTER_ROOT" ERROR_PAGE "error.detect.hunter.root" ) endfunction() function(hunter_gate_download dir) string( COMPARE NOTEQUAL "$ENV{HUNTER_DISABLE_AUTOINSTALL}" "" disable_autoinstall ) if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL) hunter_gate_fatal_error( "Hunter not found in '${dir}'" "Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'" "Settings:" " HUNTER_ROOT: ${HUNTER_GATE_ROOT}" " HUNTER_SHA1: ${HUNTER_GATE_SHA1}" ERROR_PAGE "error.run.install" ) endif() string(COMPARE EQUAL "${dir}" "" is_bad) if(is_bad) hunter_gate_internal_error("Empty 'dir' argument") endif() string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" is_bad) if(is_bad) hunter_gate_internal_error("HUNTER_GATE_SHA1 empty") endif() string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" is_bad) if(is_bad) hunter_gate_internal_error("HUNTER_GATE_URL empty") endif() set(done_location "${dir}/DONE") set(sha1_location "${dir}/SHA1") set(build_dir "${dir}/Build") set(cmakelists "${dir}/CMakeLists.txt") hunter_gate_status_debug("Locking directory: ${dir}") file(LOCK "${dir}" DIRECTORY GUARD FUNCTION) hunter_gate_status_debug("Lock done") if(EXISTS "${done_location}") # while waiting for lock other instance can do all the job hunter_gate_status_debug("File '${done_location}' found, skip install") return() endif() file(REMOVE_RECURSE "${build_dir}") file(REMOVE_RECURSE "${cmakelists}") file(MAKE_DIRECTORY "${build_dir}") # check directory permissions # Disabling languages speeds up a little bit, reduces noise in the output # and avoids path too long windows error file( WRITE "${cmakelists}" "cmake_minimum_required(VERSION 3.2)\n" "project(HunterDownload LANGUAGES NONE)\n" "include(ExternalProject)\n" "ExternalProject_Add(\n" " Hunter\n" " URL\n" " \"${HUNTER_GATE_URL}\"\n" " URL_HASH\n" " SHA1=${HUNTER_GATE_SHA1}\n" " DOWNLOAD_DIR\n" " \"${dir}\"\n" " TLS_VERIFY\n" " ${HUNTER_TLS_VERIFY}\n" " SOURCE_DIR\n" " \"${dir}/Unpacked\"\n" " CONFIGURE_COMMAND\n" " \"\"\n" " BUILD_COMMAND\n" " \"\"\n" " INSTALL_COMMAND\n" " \"\"\n" ")\n" ) if(HUNTER_STATUS_DEBUG) set(logging_params "") else() set(logging_params OUTPUT_QUIET) endif() hunter_gate_status_debug("Run generate") # Need to add toolchain file too. # Otherwise on Visual Studio + MDD this will fail with error: # "Could not find an appropriate version of the Windows 10 SDK installed on this machine" if(EXISTS "${CMAKE_TOOLCHAIN_FILE}") get_filename_component(absolute_CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE) set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${absolute_CMAKE_TOOLCHAIN_FILE}") else() # 'toolchain_arg' can't be empty set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=") endif() string(COMPARE EQUAL "${CMAKE_MAKE_PROGRAM}" "" no_make) if(no_make) set(make_arg "") else() # Test case: remove Ninja from PATH but set it via CMAKE_MAKE_PROGRAM set(make_arg "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}") endif() execute_process( COMMAND "${CMAKE_COMMAND}" "-H${dir}" "-B${build_dir}" "-G${CMAKE_GENERATOR}" "${toolchain_arg}" ${make_arg} WORKING_DIRECTORY "${dir}" RESULT_VARIABLE download_result ${logging_params} ) if(NOT download_result EQUAL 0) hunter_gate_internal_error( "Configure project failed." "To reproduce the error run: ${CMAKE_COMMAND} -H${dir} -B${build_dir} -G${CMAKE_GENERATOR} ${toolchain_arg} ${make_arg}" "In directory ${dir}" ) endif() hunter_gate_status_print( "Initializing Hunter workspace (${HUNTER_GATE_SHA1})" " ${HUNTER_GATE_URL}" " -> ${dir}" ) execute_process( COMMAND "${CMAKE_COMMAND}" --build "${build_dir}" WORKING_DIRECTORY "${dir}" RESULT_VARIABLE download_result ${logging_params} ) if(NOT download_result EQUAL 0) hunter_gate_internal_error("Build project failed") endif() file(REMOVE_RECURSE "${build_dir}") file(REMOVE_RECURSE "${cmakelists}") file(WRITE "${sha1_location}" "${HUNTER_GATE_SHA1}") file(WRITE "${done_location}" "DONE") hunter_gate_status_debug("Finished") endfunction() # Must be a macro so master file 'cmake/Hunter' can # apply all variables easily just by 'include' command # (otherwise PARENT_SCOPE magic needed) macro(HunterGate) if(HUNTER_GATE_DONE) # variable HUNTER_GATE_DONE set explicitly for external project # (see `hunter_download`) set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) endif() # First HunterGate command will init Hunter, others will be ignored get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET) if(NOT HUNTER_ENABLED) # Empty function to avoid error "unknown function" function(hunter_add_package) endfunction() set( _hunter_gate_disabled_mode_dir "${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/disabled-mode" ) if(EXISTS "${_hunter_gate_disabled_mode_dir}") hunter_gate_status_debug( "Adding \"disabled-mode\" modules: ${_hunter_gate_disabled_mode_dir}" ) list(APPEND CMAKE_PREFIX_PATH "${_hunter_gate_disabled_mode_dir}") endif() elseif(_hunter_gate_done) hunter_gate_status_debug("Secondary HunterGate (use old settings)") hunter_gate_self( "${HUNTER_CACHED_ROOT}" "${HUNTER_VERSION}" "${HUNTER_SHA1}" _hunter_self ) include("${_hunter_self}/cmake/Hunter") else() set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}") string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name) if(_have_project_name) hunter_gate_fatal_error( "Please set HunterGate *before* 'project' command. " "Detected project: ${PROJECT_NAME}" ERROR_PAGE "error.huntergate.before.project" ) endif() cmake_parse_arguments( HUNTER_GATE "LOCAL" "URL;SHA1;GLOBAL;FILEPATH" "" ${ARGV} ) string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" _empty_sha1) string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" _empty_url) string( COMPARE NOTEQUAL "${HUNTER_GATE_UNPARSED_ARGUMENTS}" "" _have_unparsed ) string(COMPARE NOTEQUAL "${HUNTER_GATE_GLOBAL}" "" _have_global) string(COMPARE NOTEQUAL "${HUNTER_GATE_FILEPATH}" "" _have_filepath) if(_have_unparsed) hunter_gate_user_error( "HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}" ) endif() if(_empty_sha1) hunter_gate_user_error("SHA1 suboption of HunterGate is mandatory") endif() if(_empty_url) hunter_gate_user_error("URL suboption of HunterGate is mandatory") endif() if(_have_global) if(HUNTER_GATE_LOCAL) hunter_gate_user_error("Unexpected LOCAL (already has GLOBAL)") endif() if(_have_filepath) hunter_gate_user_error("Unexpected FILEPATH (already has GLOBAL)") endif() endif() if(HUNTER_GATE_LOCAL) if(_have_global) hunter_gate_user_error("Unexpected GLOBAL (already has LOCAL)") endif() if(_have_filepath) hunter_gate_user_error("Unexpected FILEPATH (already has LOCAL)") endif() endif() if(_have_filepath) if(_have_global) hunter_gate_user_error("Unexpected GLOBAL (already has FILEPATH)") endif() if(HUNTER_GATE_LOCAL) hunter_gate_user_error("Unexpected LOCAL (already has FILEPATH)") endif() endif() hunter_gate_detect_root() # set HUNTER_GATE_ROOT # Beautify path, fix probable problems with windows path slashes get_filename_component( HUNTER_GATE_ROOT "${HUNTER_GATE_ROOT}" ABSOLUTE ) hunter_gate_status_debug("HUNTER_ROOT: ${HUNTER_GATE_ROOT}") if(NOT HUNTER_ALLOW_SPACES_IN_PATH) string(FIND "${HUNTER_GATE_ROOT}" " " _contain_spaces) if(NOT _contain_spaces EQUAL -1) hunter_gate_fatal_error( "HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces." "Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error" "(Use at your own risk!)" ERROR_PAGE "error.spaces.in.hunter.root" ) endif() endif() string( REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+[-_a-z0-9]*" HUNTER_GATE_VERSION "${HUNTER_GATE_URL}" ) string(COMPARE EQUAL "${HUNTER_GATE_VERSION}" "" _is_empty) if(_is_empty) set(HUNTER_GATE_VERSION "unknown") endif() hunter_gate_self( "${HUNTER_GATE_ROOT}" "${HUNTER_GATE_VERSION}" "${HUNTER_GATE_SHA1}" _hunter_self ) set(_master_location "${_hunter_self}/cmake/Hunter") if(EXISTS "${HUNTER_GATE_ROOT}/cmake/Hunter") # Hunter downloaded manually (e.g. by 'git clone') set(_unused "xxxxxxxxxx") set(HUNTER_GATE_SHA1 "${_unused}") set(HUNTER_GATE_VERSION "${_unused}") else() get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE) set(_done_location "${_archive_id_location}/DONE") set(_sha1_location "${_archive_id_location}/SHA1") # Check Hunter already downloaded by HunterGate if(NOT EXISTS "${_done_location}") hunter_gate_download("${_archive_id_location}") endif() if(NOT EXISTS "${_done_location}") hunter_gate_internal_error("hunter_gate_download failed") endif() if(NOT EXISTS "${_sha1_location}") hunter_gate_internal_error("${_sha1_location} not found") endif() file(READ "${_sha1_location}" _sha1_value) string(TOLOWER "${_sha1_value}" _sha1_value_lower) string(TOLOWER "${HUNTER_GATE_SHA1}" _HUNTER_GATE_SHA1_lower) string(COMPARE EQUAL "${_sha1_value_lower}" "${_HUNTER_GATE_SHA1_lower}" _is_equal) if(NOT _is_equal) hunter_gate_internal_error( "Short SHA1 collision:" " ${_sha1_value} (from ${_sha1_location})" " ${HUNTER_GATE_SHA1} (HunterGate)" ) endif() if(NOT EXISTS "${_master_location}") hunter_gate_user_error( "Master file not found:" " ${_master_location}" "try to update Hunter/HunterGate" ) endif() endif() include("${_master_location}") set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) endif() endmacro() scitokens-cpp-1.2.0/vendor/jwt-cpp/cmake/code-coverage.cmake000066400000000000000000000007221511466717700237620ustar00rootroot00000000000000set(COVERAGE_CMAKE "${CMAKE_BINARY_DIR}/cmake/CodeCoverage.cmake") if(NOT EXISTS ${COVERAGE_CMAKE}) set(COVERAGE_URL "https://raw.githubusercontent.com/bilke/cmake-modules/master/CodeCoverage.cmake") file(DOWNLOAD ${COVERAGE_URL} ${COVERAGE_CMAKE}) endif() include(${COVERAGE_CMAKE}) function(setup_coverage TARGET) target_compile_options(${TARGET} PRIVATE -g -O0 -fprofile-arcs -ftest-coverage) target_link_libraries(${TARGET} PRIVATE gcov) endfunction() scitokens-cpp-1.2.0/vendor/jwt-cpp/cmake/jwt-cpp-config.cmake.in000066400000000000000000000012321511466717700245100ustar00rootroot00000000000000@PACKAGE_INIT@ set(JWT_DISABLE_PICOJSON @JWT_DISABLE_PICOJSON@) set(JWT_EXTERNAL_PICOJSON @JWT_EXTERNAL_PICOJSON@) set(JWT_SSL_LIBRARY @JWT_SSL_LIBRARY@) include(CMakeFindDependencyMacro) if(${JWT_SSL_LIBRARY} MATCHES "wolfSSL") find_dependency(PkgConfig REQUIRED) pkg_check_modules(wolfssl REQUIRED IMPORTED_TARGET wolfssl) list(TRANSFORM wolfssl_INCLUDE_DIRS APPEND "/wolfssl") # This is required to access OpenSSL compatibility API else() find_dependency(${JWT_SSL_LIBRARY} REQUIRED) endif() if(NOT JWT_DISABLE_PICOJSON AND JWT_EXTERNAL_PICOJSON) find_dependency(picojson REQUIRED) endif() include("${CMAKE_CURRENT_LIST_DIR}/jwt-cpp-targets.cmake") scitokens-cpp-1.2.0/vendor/jwt-cpp/cmake/private-find-boost-json.cmake000066400000000000000000000015011511466717700257360ustar00rootroot00000000000000if(TARGET boost_json) return() endif() unset(BOOSTJSON_INCLUDE_DIR CACHE) find_path(BOOSTJSON_INCLUDE_DIR "boost/json.hpp" "boost/json/src.hpp") if(EXISTS "${BOOSTJSON_INCLUDE_DIR}/boost/json.hpp") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/private-boost-json.cpp.in" "#include ") configure_file("${CMAKE_CURRENT_BINARY_DIR}/private-boost-json.cpp.in" private-boost-json.cpp COPYONLY) add_library(boost_json "${BOOSTJSON_INCLUDE_DIR}/boost/json.hpp" "${BOOSTJSON_INCLUDE_DIR}/boost/json/src.hpp" "${CMAKE_CURRENT_BINARY_DIR}/private-boost-json.cpp") target_include_directories(boost_json PUBLIC ${BOOSTJSON_INCLUDE_DIR}) target_compile_definitions(boost_json PUBLIC BOOST_JSON_STANDALONE) target_compile_features(boost_json PUBLIC cxx_std_17) endif() scitokens-cpp-1.2.0/vendor/jwt-cpp/cmake/private-find-kazuho-picojson.cmake000066400000000000000000000005011511466717700267630ustar00rootroot00000000000000if(TARGET kazuho_picojson) return() endif() unset(PICOJSON_INCLUDE_DIR CACHE) find_path(PICOJSON_INCLUDE_DIR "picojson/picojson.h") if(EXISTS "${PICOJSON_INCLUDE_DIR}/picojson/picojson.h") add_library(kazuho_picojson INTERFACE) target_include_directories(kazuho_picojson INTERFACE ${PICOJSON_INCLUDE_DIR}) endif() scitokens-cpp-1.2.0/vendor/jwt-cpp/include/000077500000000000000000000000001511466717700206175ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/000077500000000000000000000000001511466717700222035ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/base.h000066400000000000000000000332231511466717700232710ustar00rootroot00000000000000#ifndef JWT_CPP_BASE_H #define JWT_CPP_BASE_H #include #include #include #include #include #include #ifdef __has_cpp_attribute #if __has_cpp_attribute(fallthrough) #define JWT_FALLTHROUGH [[fallthrough]] #endif #endif #ifndef JWT_FALLTHROUGH #define JWT_FALLTHROUGH #endif namespace jwt { /** * \brief character maps when encoding and decoding */ namespace alphabet { /** * \brief valid list of character when working with [Base64](https://datatracker.ietf.org/doc/html/rfc4648#section-4) * * As directed in [X.509 Parameter](https://datatracker.ietf.org/doc/html/rfc7517#section-4.7) certificate chains are * base64-encoded as per [Section 4 of RFC4648](https://datatracker.ietf.org/doc/html/rfc4648#section-4) */ struct base64 { static const std::array& data() { static constexpr std::array data{ {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}}; return data; } static const std::array& rdata() { static constexpr std::array rdata{{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }}; return rdata; } static const std::string& fill() { static const std::string fill{"="}; return fill; } }; /** * \brief valid list of character when working with [Base64URL](https://tools.ietf.org/html/rfc4648#section-5) * * As directed by [RFC 7519 Terminology](https://datatracker.ietf.org/doc/html/rfc7519#section-2) set the definition of Base64URL * encoding as that in [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515#section-2) that states: * * > Base64 encoding using the URL- and filename-safe character set defined in * > [Section 5 of RFC 4648 RFC4648](https://tools.ietf.org/html/rfc4648#section-5), with all trailing '=' characters omitted */ struct base64url { static const std::array& data() { static constexpr std::array data{ {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}}; return data; } static const std::array& rdata() { static constexpr std::array rdata{{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }}; return rdata; } static const std::string& fill() { static const std::string fill{"%3d"}; return fill; } }; namespace helper { /** * \brief A General purpose base64url alphabet respecting the * [URI Case Normalization](https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.1) * * This is useful in situations outside of JWT encoding/decoding and is provided as a helper */ struct base64url_percent_encoding { static const std::array& data() { static constexpr std::array data{ {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}}; return data; } static const std::array& rdata() { static constexpr std::array rdata{{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }}; return rdata; } static const std::vector& fill() { static const std::vector fill{"%3D", "%3d"}; return fill; } }; } // namespace helper inline uint32_t index(const std::array& rdata, char symbol) { auto index = rdata[static_cast(symbol)]; if (index <= -1) { throw std::runtime_error("Invalid input: not within alphabet"); } return static_cast(index); } } // namespace alphabet /** * \brief A collection of fellable functions for working with base64 and base64url */ namespace base { namespace details { struct padding { size_t count = 0; size_t length = 0; padding() = default; padding(size_t count, size_t length) : count(count), length(length) {} padding operator+(const padding& p) { return padding(count + p.count, length + p.length); } friend bool operator==(const padding& lhs, const padding& rhs) { return lhs.count == rhs.count && lhs.length == rhs.length; } }; inline padding count_padding(const std::string& base, const std::vector& fills) { for (const auto& fill : fills) { if (base.size() < fill.size()) continue; // Does the end of the input exactly match the fill pattern? if (base.substr(base.size() - fill.size()) == fill) { return padding{1, fill.length()} + count_padding(base.substr(0, base.size() - fill.size()), fills); } } return {}; } inline std::string encode(const std::string& bin, const std::array& alphabet, const std::string& fill) { size_t size = bin.size(); std::string res; // clear incomplete bytes size_t fast_size = size - size % 3; for (size_t i = 0; i < fast_size;) { uint32_t octet_a = static_cast(bin[i++]); uint32_t octet_b = static_cast(bin[i++]); uint32_t octet_c = static_cast(bin[i++]); uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; res += alphabet[(triple >> 3 * 6) & 0x3F]; res += alphabet[(triple >> 2 * 6) & 0x3F]; res += alphabet[(triple >> 1 * 6) & 0x3F]; res += alphabet[(triple >> 0 * 6) & 0x3F]; } if (fast_size == size) return res; size_t mod = size % 3; uint32_t octet_a = fast_size < size ? static_cast(bin[fast_size++]) : 0; uint32_t octet_b = fast_size < size ? static_cast(bin[fast_size++]) : 0; uint32_t octet_c = fast_size < size ? static_cast(bin[fast_size++]) : 0; uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; switch (mod) { case 1: res += alphabet[(triple >> 3 * 6) & 0x3F]; res += alphabet[(triple >> 2 * 6) & 0x3F]; res += fill; res += fill; break; case 2: res += alphabet[(triple >> 3 * 6) & 0x3F]; res += alphabet[(triple >> 2 * 6) & 0x3F]; res += alphabet[(triple >> 1 * 6) & 0x3F]; res += fill; break; default: break; } return res; } inline std::string decode(const std::string& base, const std::array& rdata, const std::vector& fill) { const auto pad = count_padding(base, fill); if (pad.count > 2) throw std::runtime_error("Invalid input: too much fill"); const size_t size = base.size() - pad.length; if ((size + pad.count) % 4 != 0) throw std::runtime_error("Invalid input: incorrect total size"); size_t out_size = size / 4 * 3; std::string res; res.reserve(out_size); auto get_sextet = [&](size_t offset) { return alphabet::index(rdata, base[offset]); }; size_t fast_size = size - size % 4; for (size_t i = 0; i < fast_size;) { uint32_t sextet_a = get_sextet(i++); uint32_t sextet_b = get_sextet(i++); uint32_t sextet_c = get_sextet(i++); uint32_t sextet_d = get_sextet(i++); uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6); res += static_cast((triple >> 2 * 8) & 0xFFU); res += static_cast((triple >> 1 * 8) & 0xFFU); res += static_cast((triple >> 0 * 8) & 0xFFU); } if (pad.count == 0) return res; uint32_t triple = (get_sextet(fast_size) << 3 * 6) + (get_sextet(fast_size + 1) << 2 * 6); switch (pad.count) { case 1: triple |= (get_sextet(fast_size + 2) << 1 * 6); res += static_cast((triple >> 2 * 8) & 0xFFU); res += static_cast((triple >> 1 * 8) & 0xFFU); break; case 2: res += static_cast((triple >> 2 * 8) & 0xFFU); break; default: break; } return res; } inline std::string decode(const std::string& base, const std::array& rdata, const std::string& fill) { return decode(base, rdata, std::vector{fill}); } inline std::string pad(const std::string& base, const std::string& fill) { std::string padding; switch (base.size() % 4) { case 1: padding += fill; JWT_FALLTHROUGH; case 2: padding += fill; JWT_FALLTHROUGH; case 3: padding += fill; JWT_FALLTHROUGH; default: break; } return base + padding; } inline std::string trim(const std::string& base, const std::string& fill) { auto pos = base.find(fill); return base.substr(0, pos); } } // namespace details /** * \brief Generic base64 encoding * * A Generic base64 encode function that supports any "alphabet" * such as jwt::alphabet::base64 * * \code * const auto b64 = jwt::base::encode("example_data") * \endcode */ template std::string encode(const std::string& bin) { return details::encode(bin, T::data(), T::fill()); } /** * \brief Generic base64 decoding * * A Generic base64 decoding function that supports any "alphabet" * such as jwt::alphabet::base64 * * \code * const auto b64 = jwt::base::decode("ZXhhbXBsZV9kYXRh") * \endcode */ template std::string decode(const std::string& base) { return details::decode(base, T::rdata(), T::fill()); } /** * \brief Generic base64 padding * * A Generic base64 pad function that supports any "alphabet" * such as jwt::alphabet::base64 * * \code * const auto b64 = jwt::base::pad("ZXhhbXBsZV9kYQ") * \endcode */ template std::string pad(const std::string& base) { return details::pad(base, T::fill()); } /** * \brief Generic base64 trimming * * A Generic base64 trim function that supports any "alphabet" * such as jwt::alphabet::base64 * * \code * const auto b64 = jwt::base::trim("ZXhhbXBsZV9kYQ==") * \endcode */ template std::string trim(const std::string& base) { return details::trim(base, T::fill()); } } // namespace base } // namespace jwt #endif scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/jwt.h000066400000000000000000004630211511466717700231660ustar00rootroot00000000000000#ifndef JWT_CPP_JWT_H #define JWT_CPP_JWT_H #ifndef JWT_DISABLE_PICOJSON #ifndef PICOJSON_USE_INT64 #define PICOJSON_USE_INT64 #endif #include "picojson/picojson.h" #endif #ifndef JWT_DISABLE_BASE64 #include "base.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __cplusplus >= 201402L #ifdef __has_include #if __has_include() #include #endif #endif #endif #if OPENSSL_VERSION_NUMBER >= 0x30000000L // 3.0.0 #define JWT_OPENSSL_3_0 #include #elif OPENSSL_VERSION_NUMBER >= 0x10101000L // 1.1.1 #define JWT_OPENSSL_1_1_1 #elif OPENSSL_VERSION_NUMBER >= 0x10100000L // 1.1.0 #define JWT_OPENSSL_1_1_0 #elif OPENSSL_VERSION_NUMBER >= 0x10000000L // 1.0.0 #define JWT_OPENSSL_1_0_0 #endif #if defined(LIBRESSL_VERSION_NUMBER) #if LIBRESSL_VERSION_NUMBER >= 0x3050300fL #define JWT_OPENSSL_1_1_0 #else #define JWT_OPENSSL_1_0_0 #endif #endif #if defined(LIBWOLFSSL_VERSION_HEX) #define JWT_OPENSSL_1_1_1 #endif #ifndef JWT_CLAIM_EXPLICIT #define JWT_CLAIM_EXPLICIT explicit #endif /** * \brief JSON Web Token. * * 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) */ namespace jwt { /** * Default system time point in UTC */ using date = std::chrono::system_clock::time_point; /** * \brief Everything related to error codes issued by the library */ namespace error { struct signature_verification_exception : public std::system_error { using system_error::system_error; }; struct signature_generation_exception : public std::system_error { using system_error::system_error; }; struct rsa_exception : public std::system_error { using system_error::system_error; }; struct ecdsa_exception : public std::system_error { using system_error::system_error; }; struct token_verification_exception : public std::system_error { using system_error::system_error; }; /** * \brief Errors related to processing of RSA signatures */ enum class rsa_error { ok = 0, cert_load_failed = 10, get_key_failed, write_key_failed, write_cert_failed, convert_to_pem_failed, load_key_bio_write, load_key_bio_read, create_mem_bio_failed, no_key_provided, set_rsa_failed, create_context_failed }; /** * \brief Error category for RSA errors */ inline std::error_category& rsa_error_category() { class rsa_error_cat : public std::error_category { public: const char* name() const noexcept override { return "rsa_error"; }; std::string message(int ev) const override { switch (static_cast(ev)) { case rsa_error::ok: return "no error"; case rsa_error::cert_load_failed: return "error loading cert into memory"; case rsa_error::get_key_failed: return "error getting key from certificate"; case rsa_error::write_key_failed: return "error writing key data in PEM format"; case rsa_error::write_cert_failed: return "error writing cert data in PEM format"; case rsa_error::convert_to_pem_failed: return "failed to convert key to pem"; case rsa_error::load_key_bio_write: return "failed to load key: bio write failed"; case rsa_error::load_key_bio_read: return "failed to load key: bio read failed"; case rsa_error::create_mem_bio_failed: return "failed to create memory bio"; case rsa_error::no_key_provided: return "at least one of public or private key need to be present"; case rsa_error::set_rsa_failed: return "set modulus and exponent to RSA failed"; case rsa_error::create_context_failed: return "failed to create context"; default: return "unknown RSA error"; } } }; static rsa_error_cat cat; return cat; } /** * \brief Converts JWT-CPP errors into generic STL error_codes */ inline std::error_code make_error_code(rsa_error e) { return {static_cast(e), rsa_error_category()}; } /** * \brief Errors related to processing of RSA signatures */ enum class ecdsa_error { ok = 0, load_key_bio_write = 10, load_key_bio_read, create_mem_bio_failed, no_key_provided, invalid_key_size, invalid_key, create_context_failed, cert_load_failed, get_key_failed, write_key_failed, write_cert_failed, convert_to_pem_failed, unknown_curve, set_ecdsa_failed }; /** * \brief Error category for ECDSA errors */ inline std::error_category& ecdsa_error_category() { class ecdsa_error_cat : public std::error_category { public: const char* name() const noexcept override { return "ecdsa_error"; }; std::string message(int ev) const override { switch (static_cast(ev)) { case ecdsa_error::ok: return "no error"; case ecdsa_error::load_key_bio_write: return "failed to load key: bio write failed"; case ecdsa_error::load_key_bio_read: return "failed to load key: bio read failed"; case ecdsa_error::create_mem_bio_failed: return "failed to create memory bio"; case ecdsa_error::no_key_provided: return "at least one of public or private key need to be present"; case ecdsa_error::invalid_key_size: return "invalid key size"; case ecdsa_error::invalid_key: return "invalid key"; case ecdsa_error::create_context_failed: return "failed to create context"; case ecdsa_error::cert_load_failed: return "error loading cert into memory"; case ecdsa_error::get_key_failed: return "error getting key from certificate"; case ecdsa_error::write_key_failed: return "error writing key data in PEM format"; case ecdsa_error::write_cert_failed: return "error writing cert data in PEM format"; case ecdsa_error::convert_to_pem_failed: return "failed to convert key to pem"; case ecdsa_error::unknown_curve: return "unknown curve"; case ecdsa_error::set_ecdsa_failed: return "set parameters to ECDSA failed"; default: return "unknown ECDSA error"; } } }; static ecdsa_error_cat cat; return cat; } /** * \brief Converts JWT-CPP errors into generic STL error_codes */ inline std::error_code make_error_code(ecdsa_error e) { return {static_cast(e), ecdsa_error_category()}; } /** * \brief Errors related to verification of signatures */ enum class signature_verification_error { ok = 0, invalid_signature = 10, create_context_failed, verifyinit_failed, verifyupdate_failed, verifyfinal_failed, get_key_failed, set_rsa_pss_saltlen_failed, signature_encoding_failed }; /** * \brief Error category for verification errors */ inline std::error_category& signature_verification_error_category() { class verification_error_cat : public std::error_category { public: const char* name() const noexcept override { return "signature_verification_error"; }; std::string message(int ev) const override { switch (static_cast(ev)) { case signature_verification_error::ok: return "no error"; case signature_verification_error::invalid_signature: return "invalid signature"; case signature_verification_error::create_context_failed: return "failed to verify signature: could not create context"; case signature_verification_error::verifyinit_failed: return "failed to verify signature: VerifyInit failed"; case signature_verification_error::verifyupdate_failed: return "failed to verify signature: VerifyUpdate failed"; case signature_verification_error::verifyfinal_failed: return "failed to verify signature: VerifyFinal failed"; case signature_verification_error::get_key_failed: return "failed to verify signature: Could not get key"; case signature_verification_error::set_rsa_pss_saltlen_failed: return "failed to verify signature: EVP_PKEY_CTX_set_rsa_pss_saltlen failed"; case signature_verification_error::signature_encoding_failed: return "failed to verify signature: i2d_ECDSA_SIG failed"; default: return "unknown signature verification error"; } } }; static verification_error_cat cat; return cat; } /** * \brief Converts JWT-CPP errors into generic STL error_codes */ inline std::error_code make_error_code(signature_verification_error e) { return {static_cast(e), signature_verification_error_category()}; } /** * \brief Errors related to signature generation errors */ enum class signature_generation_error { ok = 0, hmac_failed = 10, create_context_failed, signinit_failed, signupdate_failed, signfinal_failed, ecdsa_do_sign_failed, digestinit_failed, digestupdate_failed, digestfinal_failed, rsa_padding_failed, rsa_private_encrypt_failed, get_key_failed, set_rsa_pss_saltlen_failed, signature_decoding_failed }; /** * \brief Error category for signature generation errors */ inline std::error_category& signature_generation_error_category() { class signature_generation_error_cat : public std::error_category { public: const char* name() const noexcept override { return "signature_generation_error"; }; std::string message(int ev) const override { switch (static_cast(ev)) { case signature_generation_error::ok: return "no error"; case signature_generation_error::hmac_failed: return "hmac failed"; case signature_generation_error::create_context_failed: return "failed to create signature: could not create context"; case signature_generation_error::signinit_failed: return "failed to create signature: SignInit failed"; case signature_generation_error::signupdate_failed: return "failed to create signature: SignUpdate failed"; case signature_generation_error::signfinal_failed: return "failed to create signature: SignFinal failed"; case signature_generation_error::ecdsa_do_sign_failed: return "failed to generate ecdsa signature"; case signature_generation_error::digestinit_failed: return "failed to create signature: DigestInit failed"; case signature_generation_error::digestupdate_failed: return "failed to create signature: DigestUpdate failed"; case signature_generation_error::digestfinal_failed: return "failed to create signature: DigestFinal failed"; case signature_generation_error::rsa_padding_failed: return "failed to create signature: EVP_PKEY_CTX_set_rsa_padding failed"; case signature_generation_error::rsa_private_encrypt_failed: return "failed to create signature: RSA_private_encrypt failed"; case signature_generation_error::get_key_failed: return "failed to generate signature: Could not get key"; case signature_generation_error::set_rsa_pss_saltlen_failed: return "failed to create signature: EVP_PKEY_CTX_set_rsa_pss_saltlen failed"; case signature_generation_error::signature_decoding_failed: return "failed to create signature: d2i_ECDSA_SIG failed"; default: return "unknown signature generation error"; } } }; static signature_generation_error_cat cat = {}; return cat; } /** * \brief Converts JWT-CPP errors into generic STL error_codes */ inline std::error_code make_error_code(signature_generation_error e) { 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, missing_claim, claim_type_missmatch, claim_value_missmatch, 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 { public: const char* name() const noexcept override { return "token_verification_error"; }; std::string message(int ev) const override { switch (static_cast(ev)) { case token_verification_error::ok: return "no error"; case token_verification_error::wrong_algorithm: return "wrong algorithm"; case token_verification_error::missing_claim: return "decoded JWT is missing required claim(s)"; case token_verification_error::claim_type_missmatch: return "claim type does not match expected type"; case token_verification_error::claim_value_missmatch: return "claim value does not match expected value"; case token_verification_error::token_expired: return "token expired"; case token_verification_error::audience_missmatch: return "token doesn't contain the required audience"; default: return "unknown token verification error"; } } }; static token_verification_error_cat cat = {}; return cat; } /** * \brief Converts JWT-CPP errors into generic STL error_codes */ inline std::error_code make_error_code(token_verification_error e) { return {static_cast(e), token_verification_error_category()}; } /** * \brief Raises an exception if any JWT-CPP error codes are active */ inline void throw_if_error(std::error_code ec) { if (ec) { if (ec.category() == rsa_error_category()) throw rsa_exception(ec); if (ec.category() == ecdsa_error_category()) throw ecdsa_exception(ec); if (ec.category() == signature_verification_error_category()) throw signature_verification_exception(ec); if (ec.category() == signature_generation_error_category()) throw signature_generation_exception(ec); if (ec.category() == token_verification_error_category()) throw token_verification_exception(ec); } } } // namespace error } // namespace jwt namespace std { template<> struct is_error_code_enum : true_type {}; template<> struct is_error_code_enum : true_type {}; template<> struct is_error_code_enum : true_type {}; template<> struct is_error_code_enum : true_type {}; template<> struct is_error_code_enum : true_type {}; } // namespace std namespace jwt { /** * \brief A collection for working with certificates * * These _helpers_ are usefully when working with certificates OpenSSL APIs. * For example, when dealing with JWKS (JSON Web Key Set)[https://tools.ietf.org/html/rfc7517] * you maybe need to extract the modulus and exponent of an RSA Public Key. */ namespace helper { /** * \brief Handle class for EVP_PKEY structures * * Starting from OpenSSL 1.1.0, EVP_PKEY has internal reference counting. This handle class allows * jwt-cpp to leverage that and thus safe an allocation for the control block in std::shared_ptr. * The handle uses shared_ptr as a fallback on older versions. The behaviour should be identical between both. */ class evp_pkey_handle { public: /** * \brief Creates a null key pointer. */ constexpr evp_pkey_handle() noexcept = default; #ifdef JWT_OPENSSL_1_0_0 /** * \brief Construct a new handle. The handle takes ownership of the key. * \param key The key to store */ explicit evp_pkey_handle(EVP_PKEY* key) { m_key = std::shared_ptr(key, EVP_PKEY_free); } EVP_PKEY* get() const noexcept { return m_key.get(); } bool operator!() const noexcept { return m_key == nullptr; } explicit operator bool() const noexcept { return m_key != nullptr; } private: std::shared_ptr m_key{nullptr}; #else /** * \brief Construct a new handle. The handle takes ownership of the key. * \param key The key to store */ explicit constexpr evp_pkey_handle(EVP_PKEY* key) noexcept : m_key{key} {} evp_pkey_handle(const evp_pkey_handle& other) : m_key{other.m_key} { if (m_key != nullptr && EVP_PKEY_up_ref(m_key) != 1) throw std::runtime_error("EVP_PKEY_up_ref failed"); } // C++11 requires the body of a constexpr constructor to be empty #if __cplusplus >= 201402L constexpr #endif evp_pkey_handle(evp_pkey_handle&& other) noexcept : m_key{other.m_key} { other.m_key = nullptr; } evp_pkey_handle& operator=(const evp_pkey_handle& other) { if (&other == this) return *this; decrement_ref_count(m_key); m_key = other.m_key; increment_ref_count(m_key); return *this; } evp_pkey_handle& operator=(evp_pkey_handle&& other) noexcept { if (&other == this) return *this; decrement_ref_count(m_key); m_key = other.m_key; other.m_key = nullptr; return *this; } evp_pkey_handle& operator=(EVP_PKEY* key) { decrement_ref_count(m_key); m_key = key; increment_ref_count(m_key); return *this; } ~evp_pkey_handle() noexcept { decrement_ref_count(m_key); } EVP_PKEY* get() const noexcept { return m_key; } bool operator!() const noexcept { return m_key == nullptr; } explicit operator bool() const noexcept { return m_key != nullptr; } private: EVP_PKEY* m_key{nullptr}; static void increment_ref_count(EVP_PKEY* key) { if (key != nullptr && EVP_PKEY_up_ref(key) != 1) throw std::runtime_error("EVP_PKEY_up_ref failed"); } static void decrement_ref_count(EVP_PKEY* key) noexcept { if (key != nullptr) EVP_PKEY_free(key); } #endif }; inline std::unique_ptr make_mem_buf_bio() { return std::unique_ptr(BIO_new(BIO_s_mem()), BIO_free_all); } inline std::unique_ptr make_mem_buf_bio(const std::string& data) { return std::unique_ptr( #if OPENSSL_VERSION_NUMBER <= 0x10100003L BIO_new_mem_buf(const_cast(data.data()), static_cast(data.size())), BIO_free_all #else BIO_new_mem_buf(data.data(), static_cast(data.size())), BIO_free_all #endif ); } template std::string write_bio_to_string(std::unique_ptr& bio_out, std::error_code& ec) { char* ptr = nullptr; auto len = BIO_get_mem_data(bio_out.get(), &ptr); if (len <= 0 || ptr == nullptr) { ec = error_category::convert_to_pem_failed; return {}; } return {ptr, static_cast(len)}; } inline std::unique_ptr make_evp_md_ctx() { return #ifdef JWT_OPENSSL_1_0_0 std::unique_ptr(EVP_MD_CTX_create(), &EVP_MD_CTX_destroy); #else std::unique_ptr(EVP_MD_CTX_new(), &EVP_MD_CTX_free); #endif } /** * \brief Extract the public key of a pem certificate * * \tparam error_category jwt::error enum category to match with the keys being used * \param certstr String containing the certificate encoded as pem * \param pw Password used to decrypt certificate (leave empty if not encrypted) * \param ec error_code for error_detection (gets cleared if no error occurred) */ template std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw, std::error_code& ec) { ec.clear(); auto certbio = make_mem_buf_bio(certstr); auto keybio = make_mem_buf_bio(); if (!certbio || !keybio) { ec = error_category::create_mem_bio_failed; return {}; } std::unique_ptr cert( PEM_read_bio_X509(certbio.get(), nullptr, nullptr, const_cast(pw.c_str())), X509_free); if (!cert) { ec = error_category::cert_load_failed; return {}; } std::unique_ptr key(X509_get_pubkey(cert.get()), EVP_PKEY_free); if (!key) { ec = error_category::get_key_failed; return {}; } if (PEM_write_bio_PUBKEY(keybio.get(), key.get()) == 0) { ec = error_category::write_key_failed; return {}; } return write_bio_to_string(keybio, ec); } /** * \brief Extract the public key of a pem certificate * * \tparam error_category jwt::error enum category to match with the keys being used * \param certstr String containing the certificate encoded as pem * \param pw Password used to decrypt certificate (leave empty if not encrypted) * \throw templated error_category's type exception if an error occurred */ template std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw = "") { std::error_code ec; auto res = extract_pubkey_from_cert(certstr, pw, ec); error::throw_if_error(ec); return res; } /** * \brief Convert the certificate provided as DER to PEM. * * \param cert_der_str String containing the certificate encoded as base64 DER * \param ec error_code for error_detection (gets cleared if no error occurs) */ inline std::string convert_der_to_pem(const std::string& cert_der_str, std::error_code& ec) { ec.clear(); auto c_str = reinterpret_cast(cert_der_str.c_str()); std::unique_ptr cert( d2i_X509(NULL, &c_str, static_cast(cert_der_str.size())), X509_free); auto certbio = make_mem_buf_bio(); if (!cert || !certbio) { ec = error::rsa_error::create_mem_bio_failed; return {}; } if (!PEM_write_bio_X509(certbio.get(), cert.get())) { ec = error::rsa_error::write_cert_failed; return {}; } return write_bio_to_string(certbio, 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). * * \tparam Decode is callable, 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 occurs) */ 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 decoded_str = decode(cert_base64_der_str); return convert_der_to_pem(decoded_str, 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) * * \tparam Decode is callable, 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; } /** * \brief Convert the certificate provided as DER to PEM. * * \param cert_der_str String containing the DER certificate * \throw rsa_exception if an error occurred */ inline std::string convert_der_to_pem(const std::string& cert_der_str) { std::error_code ec; auto res = convert_der_to_pem(cert_der_str, 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 occurs) */ 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) { std::error_code ec; auto res = convert_base64_der_to_pem(cert_base64_der_str, ec); error::throw_if_error(ec); return res; } #endif /** * \brief Load a public key from a string. * * The string should contain a pem encoded certificate or public key * * \tparam error_category jwt::error enum category to match with the keys being used * \param key String containing the certificate encoded as pem * \param password Password used to decrypt certificate (leave empty if not encrypted) * \param ec error_code for error_detection (gets cleared if no error occurs) */ template evp_pkey_handle load_public_key_from_string(const std::string& key, const std::string& password, std::error_code& ec) { ec.clear(); auto pubkey_bio = make_mem_buf_bio(); if (!pubkey_bio) { ec = error_category::create_mem_bio_failed; return {}; } if (key.substr(0, 27) == "-----BEGIN CERTIFICATE-----") { auto epkey = helper::extract_pubkey_from_cert(key, password, ec); if (ec) return {}; const int len = static_cast(epkey.size()); if (BIO_write(pubkey_bio.get(), epkey.data(), len) != len) { ec = error_category::load_key_bio_write; return {}; } } else { const int len = static_cast(key.size()); if (BIO_write(pubkey_bio.get(), key.data(), len) != len) { ec = error_category::load_key_bio_write; return {}; } } evp_pkey_handle pkey(PEM_read_bio_PUBKEY( pubkey_bio.get(), nullptr, nullptr, (void*)password.data())); // NOLINT(google-readability-casting) requires `const_cast` if (!pkey) ec = error_category::load_key_bio_read; return pkey; } /** * \brief Load a public key from a string. * * The string should contain a pem encoded certificate or public key * * \tparam error_category jwt::error enum category to match with the keys being used * \param key String containing the certificate encoded as pem * \param password Password used to decrypt certificate (leave empty if not encrypted) * \throw Templated error_category's type exception if an error occurred */ template inline evp_pkey_handle load_public_key_from_string(const std::string& key, const std::string& password = "") { std::error_code ec; auto res = load_public_key_from_string(key, password, ec); error::throw_if_error(ec); return res; } /** * \brief Load a private key from a string. * * \tparam error_category jwt::error enum category to match with the keys being used * \param key String containing a private key as pem * \param password Password used to decrypt key (leave empty if not encrypted) * \param ec error_code for error_detection (gets cleared if no error occurs) */ template inline evp_pkey_handle load_private_key_from_string(const std::string& key, const std::string& password, std::error_code& ec) { ec.clear(); auto private_key_bio = make_mem_buf_bio(); if (!private_key_bio) { ec = error_category::create_mem_bio_failed; return {}; } const int len = static_cast(key.size()); if (BIO_write(private_key_bio.get(), key.data(), len) != len) { ec = error_category::load_key_bio_write; return {}; } evp_pkey_handle pkey( PEM_read_bio_PrivateKey(private_key_bio.get(), nullptr, nullptr, const_cast(password.c_str()))); if (!pkey) ec = error_category::load_key_bio_read; return pkey; } /** * \brief Load a private key from a string. * * \tparam error_category jwt::error enum category to match with the keys being used * \param key String containing a private key as pem * \param password Password used to decrypt key (leave empty if not encrypted) * \throw Templated error_category's type exception if an error occurred */ template inline evp_pkey_handle load_private_key_from_string(const std::string& key, const std::string& password = "") { std::error_code ec; auto res = load_private_key_from_string(key, password, ec); error::throw_if_error(ec); return res; } /** * \brief Load a public key from a string. * * The string should contain a pem encoded certificate or public key * * \deprecated Use the templated version helper::load_private_key_from_string with error::ecdsa_error * * \param key String containing the certificate encoded as pem * \param password Password used to decrypt certificate (leave empty if not encrypted) * \param ec error_code for error_detection (gets cleared if no error occurs) */ inline evp_pkey_handle load_public_ec_key_from_string(const std::string& key, const std::string& password, std::error_code& ec) { return load_public_key_from_string(key, password, ec); } /** * Convert a OpenSSL BIGNUM to a std::string * \param bn BIGNUM to convert * \return bignum as string */ inline #ifdef JWT_OPENSSL_1_0_0 std::string bn2raw(BIGNUM* bn) #else std::string bn2raw(const BIGNUM* bn) #endif { std::string res(BN_num_bytes(bn), '\0'); BN_bn2bin(bn, (unsigned char*)res.data()); // NOLINT(google-readability-casting) requires `const_cast` return res; } /** * Convert an std::string to a OpenSSL BIGNUM * \param raw String to convert * \param ec error_code for error_detection (gets cleared if no error occurs) * \return BIGNUM representation */ inline std::unique_ptr raw2bn(const std::string& raw, std::error_code& ec) { auto bn = BN_bin2bn(reinterpret_cast(raw.data()), static_cast(raw.size()), nullptr); // https://www.openssl.org/docs/man1.1.1/man3/BN_bin2bn.html#RETURN-VALUES if (!bn) { ec = error::rsa_error::set_rsa_failed; return {nullptr, BN_free}; } return {bn, BN_free}; } /** * Convert an std::string to a OpenSSL BIGNUM * \param raw String to convert * \return BIGNUM representation */ inline std::unique_ptr raw2bn(const std::string& raw) { std::error_code ec; auto res = raw2bn(raw, ec); error::throw_if_error(ec); return res; } /** * \brief Load a public key from a string. * * The string should contain a pem encoded certificate or public key * * \deprecated Use the templated version helper::load_private_key_from_string with error::ecdsa_error * * \param key String containing the certificate or key encoded as pem * \param password Password used to decrypt certificate or key (leave empty if not encrypted) * \throw ecdsa_exception if an error occurred */ inline evp_pkey_handle load_public_ec_key_from_string(const std::string& key, const std::string& password = "") { std::error_code ec; auto res = load_public_key_from_string(key, password, ec); error::throw_if_error(ec); return res; } /** * \brief Load a private key from a string. * * \deprecated Use the templated version helper::load_private_key_from_string with error::ecdsa_error * * \param key String containing a private key as pem * \param password Password used to decrypt key (leave empty if not encrypted) * \param ec error_code for error_detection (gets cleared if no error occurs) */ inline evp_pkey_handle load_private_ec_key_from_string(const std::string& key, const std::string& password, std::error_code& ec) { return load_private_key_from_string(key, password, ec); } /** * \brief create public key from modulus and exponent. This is defined in * [RFC 7518 Section 6.3](https://www.rfc-editor.org/rfc/rfc7518#section-6.3) * Using the required "n" (Modulus) Parameter and "e" (Exponent) Parameter. * * \tparam Decode is callable, 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 modulus string containing base64url encoded modulus * \param exponent string containing base64url encoded exponent * \param decode The function to decode the RSA parameters * \param ec error_code for error_detection (gets cleared if no error occur * \return public key in PEM format */ template std::string create_public_key_from_rsa_components(const std::string& modulus, const std::string& exponent, Decode decode, std::error_code& ec) { ec.clear(); auto decoded_modulus = decode(modulus); auto decoded_exponent = decode(exponent); auto n = helper::raw2bn(decoded_modulus, ec); if (ec) return {}; auto e = helper::raw2bn(decoded_exponent, ec); if (ec) return {}; #if defined(JWT_OPENSSL_3_0) // OpenSSL deprecated mutable keys and there is a new way for making them // https://mta.openssl.org/pipermail/openssl-users/2021-July/013994.html // https://www.openssl.org/docs/man3.1/man3/OSSL_PARAM_BLD_new.html#Example-2 std::unique_ptr param_bld(OSSL_PARAM_BLD_new(), OSSL_PARAM_BLD_free); if (!param_bld) { ec = error::rsa_error::create_context_failed; return {}; } if (OSSL_PARAM_BLD_push_BN(param_bld.get(), "n", n.get()) != 1 || OSSL_PARAM_BLD_push_BN(param_bld.get(), "e", e.get()) != 1) { ec = error::rsa_error::set_rsa_failed; return {}; } std::unique_ptr params(OSSL_PARAM_BLD_to_param(param_bld.get()), OSSL_PARAM_free); if (!params) { ec = error::rsa_error::set_rsa_failed; return {}; } std::unique_ptr ctx( EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr), EVP_PKEY_CTX_free); if (!ctx) { ec = error::rsa_error::create_context_failed; return {}; } // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html#EXAMPLES // Error codes based on https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_fromdata_init.html#RETURN-VALUES EVP_PKEY* pkey = NULL; if (EVP_PKEY_fromdata_init(ctx.get()) <= 0 || EVP_PKEY_fromdata(ctx.get(), &pkey, EVP_PKEY_KEYPAIR, params.get()) <= 0) { // It's unclear if this can fail after allocating but free it anyways // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html EVP_PKEY_free(pkey); ec = error::rsa_error::cert_load_failed; return {}; } // Transfer ownership so we get ref counter and cleanup evp_pkey_handle rsa(pkey); #else std::unique_ptr rsa(RSA_new(), RSA_free); #if defined(JWT_OPENSSL_1_1_1) || defined(JWT_OPENSSL_1_1_0) // After this RSA_free will also free the n and e big numbers // See https://github.com/Thalhammer/jwt-cpp/pull/298#discussion_r1282619186 if (RSA_set0_key(rsa.get(), n.get(), e.get(), nullptr) == 1) { // This can only fail we passed in NULL for `n` or `e` // https://github.com/openssl/openssl/blob/d6e4056805f54bb1a0ef41fa3a6a35b70c94edba/crypto/rsa/rsa_lib.c#L396 // So to make sure there is no memory leak, we hold the references n.release(); e.release(); } else { ec = error::rsa_error::set_rsa_failed; return {}; } #elif defined(JWT_OPENSSL_1_0_0) rsa->e = e.release(); rsa->n = n.release(); rsa->d = nullptr; #endif #endif auto pub_key_bio = make_mem_buf_bio(); if (!pub_key_bio) { ec = error::rsa_error::create_mem_bio_failed; return {}; } auto write_pem_to_bio = #if defined(JWT_OPENSSL_3_0) // https://www.openssl.org/docs/man3.1/man3/PEM_write_bio_RSA_PUBKEY.html &PEM_write_bio_PUBKEY; #else &PEM_write_bio_RSA_PUBKEY; #endif if (write_pem_to_bio(pub_key_bio.get(), rsa.get()) != 1) { ec = error::rsa_error::load_key_bio_write; return {}; } return write_bio_to_string(pub_key_bio, ec); } /** * Create public key from modulus and exponent. This is defined in * [RFC 7518 Section 6.3](https://www.rfc-editor.org/rfc/rfc7518#section-6.3) * Using the required "n" (Modulus) Parameter and "e" (Exponent) Parameter. * * \tparam Decode is callable, 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 modulus string containing base64url encoded modulus * \param exponent string containing base64url encoded exponent * \param decode The function to decode the RSA parameters * \return public key in PEM format */ template std::string create_public_key_from_rsa_components(const std::string& modulus, const std::string& exponent, Decode decode) { std::error_code ec; auto res = create_public_key_from_rsa_components(modulus, exponent, decode, ec); error::throw_if_error(ec); return res; } #ifndef JWT_DISABLE_BASE64 /** * Create public key from modulus and exponent. This is defined in * [RFC 7518 Section 6.3](https://www.rfc-editor.org/rfc/rfc7518#section-6.3) * Using the required "n" (Modulus) Parameter and "e" (Exponent) Parameter. * * \param modulus string containing base64 encoded modulus * \param exponent string containing base64 encoded exponent * \param ec error_code for error_detection (gets cleared if no error occur * \return public key in PEM format */ inline std::string create_public_key_from_rsa_components(const std::string& modulus, const std::string& exponent, std::error_code& ec) { auto decode = [](const std::string& token) { return base::decode(base::pad(token)); }; return create_public_key_from_rsa_components(modulus, exponent, std::move(decode), ec); } /** * Create public key from modulus and exponent. This is defined in * [RFC 7518 Section 6.3](https://www.rfc-editor.org/rfc/rfc7518#section-6.3) * Using the required "n" (Modulus) Parameter and "e" (Exponent) Parameter. * * \param modulus string containing base64url encoded modulus * \param exponent string containing base64url encoded exponent * \return public key in PEM format */ inline std::string create_public_key_from_rsa_components(const std::string& modulus, const std::string& exponent) { std::error_code ec; auto res = create_public_key_from_rsa_components(modulus, exponent, ec); error::throw_if_error(ec); return res; } #endif /** * \brief Load a private key from a string. * * \deprecated Use the templated version helper::load_private_key_from_string with error::ecdsa_error * * \param key String containing a private key as pem * \param password Password used to decrypt key (leave empty if not encrypted) * \throw ecdsa_exception if an error occurred */ inline evp_pkey_handle load_private_ec_key_from_string(const std::string& key, const std::string& password = "") { std::error_code ec; auto res = load_private_key_from_string(key, password, ec); error::throw_if_error(ec); return res; } #if defined(JWT_OPENSSL_3_0) /** * \brief Convert a curve name to a group name. * * \param curve string containing curve name * \param ec error_code for error_detection * \return group name */ inline std::string curve2group(const std::string curve, std::error_code& ec) { if (curve == "P-256") { return "prime256v1"; } else if (curve == "P-384") { return "secp384r1"; } else if (curve == "P-521") { return "secp521r1"; } else { ec = jwt::error::ecdsa_error::unknown_curve; return {}; } } #else /** * \brief Convert a curve name to an ID. * * \param curve string containing curve name * \param ec error_code for error_detection * \return ID */ inline int curve2nid(const std::string curve, std::error_code& ec) { if (curve == "P-256") { return NID_X9_62_prime256v1; } else if (curve == "P-384") { return NID_secp384r1; } else if (curve == "P-521") { return NID_secp521r1; } else { ec = jwt::error::ecdsa_error::unknown_curve; return {}; } } #endif /** * Create public key from curve name and coordinates. This is defined in * [RFC 7518 Section 6.2](https://www.rfc-editor.org/rfc/rfc7518#section-6.2) * Using the required "crv" (Curve), "x" (X Coordinate) and "y" (Y Coordinate) Parameters. * * \tparam Decode is callable, 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 curve string containing curve name * \param x string containing base64url encoded x coordinate * \param y string containing base64url encoded y coordinate * \param decode The function to decode the RSA parameters * \param ec error_code for error_detection (gets cleared if no error occur * \return public key in PEM format */ template std::string create_public_key_from_ec_components(const std::string& curve, const std::string& x, const std::string& y, Decode decode, std::error_code& ec) { ec.clear(); auto decoded_x = decode(x); auto decoded_y = decode(y); #if defined(JWT_OPENSSL_3_0) // OpenSSL deprecated mutable keys and there is a new way for making them // https://mta.openssl.org/pipermail/openssl-users/2021-July/013994.html // https://www.openssl.org/docs/man3.1/man3/OSSL_PARAM_BLD_new.html#Example-2 std::unique_ptr param_bld(OSSL_PARAM_BLD_new(), OSSL_PARAM_BLD_free); if (!param_bld) { ec = error::ecdsa_error::create_context_failed; return {}; } std::string group = helper::curve2group(curve, ec); if (ec) return {}; // https://github.com/openssl/openssl/issues/16270#issuecomment-895734092 std::string pub = std::string("\x04").append(decoded_x).append(decoded_y); if (OSSL_PARAM_BLD_push_utf8_string(param_bld.get(), "group", group.data(), group.size()) != 1 || OSSL_PARAM_BLD_push_octet_string(param_bld.get(), "pub", pub.data(), pub.size()) != 1) { ec = error::ecdsa_error::set_ecdsa_failed; return {}; } std::unique_ptr params(OSSL_PARAM_BLD_to_param(param_bld.get()), OSSL_PARAM_free); if (!params) { ec = error::ecdsa_error::set_ecdsa_failed; return {}; } std::unique_ptr ctx( EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr), EVP_PKEY_CTX_free); if (!ctx) { ec = error::ecdsa_error::create_context_failed; return {}; } // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html#EXAMPLES // Error codes based on https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_fromdata_init.html#RETURN-VALUES EVP_PKEY* pkey = NULL; if (EVP_PKEY_fromdata_init(ctx.get()) <= 0 || EVP_PKEY_fromdata(ctx.get(), &pkey, EVP_PKEY_KEYPAIR, params.get()) <= 0) { // It's unclear if this can fail after allocating but free it anyways // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html EVP_PKEY_free(pkey); ec = error::ecdsa_error::cert_load_failed; return {}; } // Transfer ownership so we get ref counter and cleanup evp_pkey_handle ecdsa(pkey); #else int nid = helper::curve2nid(curve, ec); if (ec) return {}; auto qx = helper::raw2bn(decoded_x, ec); if (ec) return {}; auto qy = helper::raw2bn(decoded_y, ec); if (ec) return {}; std::unique_ptr ecgroup(EC_GROUP_new_by_curve_name(nid), EC_GROUP_free); if (!ecgroup) { ec = error::ecdsa_error::set_ecdsa_failed; return {}; } EC_GROUP_set_asn1_flag(ecgroup.get(), OPENSSL_EC_NAMED_CURVE); std::unique_ptr ecpoint(EC_POINT_new(ecgroup.get()), EC_POINT_free); if (!ecpoint || EC_POINT_set_affine_coordinates_GFp(ecgroup.get(), ecpoint.get(), qx.get(), qy.get(), nullptr) != 1) { ec = error::ecdsa_error::set_ecdsa_failed; return {}; } std::unique_ptr ecdsa(EC_KEY_new(), EC_KEY_free); if (!ecdsa || EC_KEY_set_group(ecdsa.get(), ecgroup.get()) != 1 || EC_KEY_set_public_key(ecdsa.get(), ecpoint.get()) != 1) { ec = error::ecdsa_error::set_ecdsa_failed; return {}; } #endif auto pub_key_bio = make_mem_buf_bio(); if (!pub_key_bio) { ec = error::ecdsa_error::create_mem_bio_failed; return {}; } auto write_pem_to_bio = #if defined(JWT_OPENSSL_3_0) // https://www.openssl.org/docs/man3.1/man3/PEM_write_bio_EC_PUBKEY.html &PEM_write_bio_PUBKEY; #else &PEM_write_bio_EC_PUBKEY; #endif if (write_pem_to_bio(pub_key_bio.get(), ecdsa.get()) != 1) { ec = error::ecdsa_error::load_key_bio_write; return {}; } return write_bio_to_string(pub_key_bio, ec); } /** * Create public key from curve name and coordinates. This is defined in * [RFC 7518 Section 6.2](https://www.rfc-editor.org/rfc/rfc7518#section-6.2) * Using the required "crv" (Curve), "x" (X Coordinate) and "y" (Y Coordinate) Parameters. * * \tparam Decode is callable, 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 curve string containing curve name * \param x string containing base64url encoded x coordinate * \param y string containing base64url encoded y coordinate * \param decode The function to decode the RSA parameters * \return public key in PEM format */ template std::string create_public_key_from_ec_components(const std::string& curve, const std::string& x, const std::string& y, Decode decode) { std::error_code ec; auto res = create_public_key_from_ec_components(curve, x, y, decode, ec); error::throw_if_error(ec); return res; } #ifndef JWT_DISABLE_BASE64 /** * Create public key from curve name and coordinates. This is defined in * [RFC 7518 Section 6.2](https://www.rfc-editor.org/rfc/rfc7518#section-6.2) * Using the required "crv" (Curve), "x" (X Coordinate) and "y" (Y Coordinate) Parameters. * * \param curve string containing curve name * \param x string containing base64url encoded x coordinate * \param y string containing base64url encoded y coordinate * \param ec error_code for error_detection (gets cleared if no error occur * \return public key in PEM format */ inline std::string create_public_key_from_ec_components(const std::string& curve, const std::string& x, const std::string& y, std::error_code& ec) { auto decode = [](const std::string& token) { return base::decode(base::pad(token)); }; return create_public_key_from_ec_components(curve, x, y, std::move(decode), ec); } /** * Create public key from curve name and coordinates. This is defined in * [RFC 7518 Section 6.2](https://www.rfc-editor.org/rfc/rfc7518#section-6.2) * Using the required "crv" (Curve), "x" (X Coordinate) and "y" (Y Coordinate) Parameters. * * \param curve string containing curve name * \param x string containing base64url encoded x coordinate * \param y string containing base64url encoded y coordinate * \return public key in PEM format */ inline std::string create_public_key_from_ec_components(const std::string& curve, const std::string& x, const std::string& y) { std::error_code ec; auto res = create_public_key_from_ec_components(curve, x, y, ec); error::throw_if_error(ec); return res; } #endif } // namespace helper /** * \brief Various cryptographic algorithms when working with JWT * * JWT (JSON Web Tokens) signatures are typically used as the payload for a JWS (JSON Web Signature) or * JWE (JSON Web Encryption). Both of these use various cryptographic as specified by * [RFC7518](https://tools.ietf.org/html/rfc7518) and are exposed through the a [JOSE * Header](https://tools.ietf.org/html/rfc7515#section-4) which points to one of the JWA [JSON Web * Algorithms](https://tools.ietf.org/html/rfc7518#section-3.1) */ namespace algorithm { /** * \brief "none" algorithm. * * Returns and empty signature and checks if the given signature is empty. * See [RFC 7518 Section 3.6](https://datatracker.ietf.org/doc/html/rfc7518#section-3.6) * for more information. */ struct none { /** * \brief Return an empty string */ std::string sign(const std::string& /*unused*/, std::error_code& ec) const { ec.clear(); return {}; } /** * \brief Check if the given signature is empty. * * JWT's with "none" algorithm should not contain a signature. * \param signature Signature data to verify * \param ec error_code filled with details about the error */ void verify(const std::string& /*unused*/, const std::string& signature, std::error_code& ec) const { ec.clear(); if (!signature.empty()) { ec = error::signature_verification_error::invalid_signature; } } /// Get algorithm name std::string name() const { return "none"; } }; /** * \brief Base class for HMAC family of algorithms */ struct hmacsha { /** * Construct new hmac algorithm * * \param key Key to use for HMAC * \param md Pointer to hash function * \param name Name of the algorithm */ hmacsha(std::string key, const EVP_MD* (*md)(), std::string name) : secret(std::move(key)), md(md), alg_name(std::move(name)) {} /** * Sign jwt data * * \param data The data to sign * \param ec error_code filled with details on error * \return HMAC signature for the given data */ std::string sign(const std::string& data, std::error_code& ec) const { ec.clear(); std::string res(static_cast(EVP_MAX_MD_SIZE), '\0'); auto len = static_cast(res.size()); if (HMAC(md(), secret.data(), static_cast(secret.size()), reinterpret_cast(data.data()), static_cast(data.size()), (unsigned char*)res.data(), // NOLINT(google-readability-casting) requires `const_cast` &len) == nullptr) { ec = error::signature_generation_error::hmac_failed; return {}; } res.resize(len); return res; } /** * Check if signature is valid * * \param data The data to check signature against * \param signature Signature provided by the jwt * \param ec Filled with details about failure. */ void verify(const std::string& data, const std::string& signature, std::error_code& ec) const { ec.clear(); auto res = sign(data, ec); if (ec) return; bool matched = true; for (size_t i = 0; i < std::min(res.size(), signature.size()); i++) if (res[i] != signature[i]) matched = false; if (res.size() != signature.size()) matched = false; if (!matched) { ec = error::signature_verification_error::invalid_signature; return; } } /** * Returns the algorithm name provided to the constructor * * \return algorithm's name */ std::string name() const { return alg_name; } private: /// HMAC secret const std::string secret; /// HMAC hash generator const EVP_MD* (*md)(); /// algorithm's name const std::string alg_name; }; /** * \brief Base class for RSA family of algorithms */ struct rsa { /** * Construct new rsa algorithm * * \param public_key RSA public key in PEM format * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. * \param md Pointer to hash function * \param name Name of the algorithm */ rsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD* (*md)(), std::string name) : md(md), alg_name(std::move(name)) { if (!private_key.empty()) { pkey = helper::load_private_key_from_string(private_key, private_key_password); } else if (!public_key.empty()) { pkey = helper::load_public_key_from_string(public_key, public_key_password); } else throw error::rsa_exception(error::rsa_error::no_key_provided); } /** * Sign jwt data * \param data The data to sign * \param ec error_code filled with details on error * \return RSA signature for the given data */ std::string sign(const std::string& data, std::error_code& ec) const { ec.clear(); auto ctx = helper::make_evp_md_ctx(); if (!ctx) { ec = error::signature_generation_error::create_context_failed; return {}; } if (!EVP_SignInit(ctx.get(), md())) { ec = error::signature_generation_error::signinit_failed; return {}; } std::string res(EVP_PKEY_size(pkey.get()), '\0'); unsigned int len = 0; if (!EVP_SignUpdate(ctx.get(), data.data(), data.size())) { ec = error::signature_generation_error::signupdate_failed; return {}; } if (EVP_SignFinal(ctx.get(), (unsigned char*)res.data(), &len, pkey.get()) == 0) { ec = error::signature_generation_error::signfinal_failed; return {}; } res.resize(len); return res; } /** * Check if signature is valid * * \param data The data to check signature against * \param signature Signature provided by the jwt * \param ec Filled with details on failure */ void verify(const std::string& data, const std::string& signature, std::error_code& ec) const { ec.clear(); auto ctx = helper::make_evp_md_ctx(); if (!ctx) { ec = error::signature_verification_error::create_context_failed; return; } if (!EVP_VerifyInit(ctx.get(), md())) { ec = error::signature_verification_error::verifyinit_failed; return; } if (!EVP_VerifyUpdate(ctx.get(), data.data(), data.size())) { ec = error::signature_verification_error::verifyupdate_failed; return; } auto res = EVP_VerifyFinal(ctx.get(), reinterpret_cast(signature.data()), static_cast(signature.size()), pkey.get()); if (res != 1) { ec = error::signature_verification_error::verifyfinal_failed; return; } } /** * Returns the algorithm name provided to the constructor * \return algorithm's name */ std::string name() const { return alg_name; } private: /// OpenSSL structure containing converted keys helper::evp_pkey_handle pkey; /// Hash generator const EVP_MD* (*md)(); /// algorithm's name const std::string alg_name; }; /** * \brief Base class for ECDSA family of algorithms */ struct ecdsa { /** * Construct new ecdsa algorithm * * \param public_key ECDSA public key in PEM format * \param private_key ECDSA private key or empty string if not available. If empty, signing will always fail * \param public_key_password Password to decrypt public key pem * \param private_key_password Password to decrypt private key pem * \param md Pointer to hash function * \param name Name of the algorithm * \param siglen The bit length of the signature */ ecdsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD* (*md)(), std::string name, size_t siglen) : md(md), alg_name(std::move(name)), signature_length(siglen) { if (!private_key.empty()) { pkey = helper::load_private_ec_key_from_string(private_key, private_key_password); check_private_key(pkey.get()); } else if (!public_key.empty()) { pkey = helper::load_public_ec_key_from_string(public_key, public_key_password); check_public_key(pkey.get()); } else { throw error::ecdsa_exception(error::ecdsa_error::no_key_provided); } if (!pkey) throw error::ecdsa_exception(error::ecdsa_error::invalid_key); size_t keysize = EVP_PKEY_bits(pkey.get()); if (keysize != signature_length * 4 && (signature_length != 132 || keysize != 521)) throw error::ecdsa_exception(error::ecdsa_error::invalid_key_size); } /** * Sign jwt data * \param data The data to sign * \param ec error_code filled with details on error * \return ECDSA signature for the given data */ std::string sign(const std::string& data, std::error_code& ec) const { ec.clear(); auto ctx = helper::make_evp_md_ctx(); if (!ctx) { ec = error::signature_generation_error::create_context_failed; return {}; } if (!EVP_DigestSignInit(ctx.get(), nullptr, md(), nullptr, pkey.get())) { ec = error::signature_generation_error::signinit_failed; return {}; } if (!EVP_DigestUpdate(ctx.get(), data.data(), data.size())) { ec = error::signature_generation_error::digestupdate_failed; return {}; } size_t len = 0; if (!EVP_DigestSignFinal(ctx.get(), nullptr, &len)) { ec = error::signature_generation_error::signfinal_failed; return {}; } std::string res(len, '\0'); if (!EVP_DigestSignFinal(ctx.get(), (unsigned char*)res.data(), &len)) { ec = error::signature_generation_error::signfinal_failed; return {}; } res.resize(len); return der_to_p1363_signature(res, ec); } /** * Check if signature is valid * \param data The data to check signature against * \param signature Signature provided by the jwt * \param ec Filled with details on error */ void verify(const std::string& data, const std::string& signature, std::error_code& ec) const { ec.clear(); std::string der_signature = p1363_to_der_signature(signature, ec); if (ec) { return; } auto ctx = helper::make_evp_md_ctx(); if (!ctx) { ec = error::signature_verification_error::create_context_failed; return; } if (!EVP_DigestVerifyInit(ctx.get(), nullptr, md(), nullptr, pkey.get())) { ec = error::signature_verification_error::verifyinit_failed; return; } if (!EVP_DigestUpdate(ctx.get(), data.data(), data.size())) { ec = error::signature_verification_error::verifyupdate_failed; return; } #if OPENSSL_VERSION_NUMBER < 0x10002000L unsigned char* der_sig_data = reinterpret_cast(const_cast(der_signature.data())); #else const unsigned char* der_sig_data = reinterpret_cast(der_signature.data()); #endif auto res = EVP_DigestVerifyFinal(ctx.get(), der_sig_data, static_cast(der_signature.length())); if (res == 0) { ec = error::signature_verification_error::invalid_signature; return; } if (res == -1) { ec = error::signature_verification_error::verifyfinal_failed; return; } } /** * Returns the algorithm name provided to the constructor * \return algorithm's name */ std::string name() const { return alg_name; } private: static void check_public_key(EVP_PKEY* pkey) { #ifdef JWT_OPENSSL_3_0 std::unique_ptr ctx( EVP_PKEY_CTX_new_from_pkey(nullptr, pkey, nullptr), EVP_PKEY_CTX_free); if (!ctx) { throw error::ecdsa_exception(error::ecdsa_error::create_context_failed); } if (EVP_PKEY_public_check(ctx.get()) != 1) { throw error::ecdsa_exception(error::ecdsa_error::invalid_key); } #else std::unique_ptr eckey(EVP_PKEY_get1_EC_KEY(pkey), EC_KEY_free); if (!eckey) { throw error::ecdsa_exception(error::ecdsa_error::invalid_key); } if (EC_KEY_check_key(eckey.get()) == 0) throw error::ecdsa_exception(error::ecdsa_error::invalid_key); #endif } static void check_private_key(EVP_PKEY* pkey) { #ifdef JWT_OPENSSL_3_0 std::unique_ptr ctx( EVP_PKEY_CTX_new_from_pkey(nullptr, pkey, nullptr), EVP_PKEY_CTX_free); if (!ctx) { throw error::ecdsa_exception(error::ecdsa_error::create_context_failed); } if (EVP_PKEY_private_check(ctx.get()) != 1) { throw error::ecdsa_exception(error::ecdsa_error::invalid_key); } #else std::unique_ptr eckey(EVP_PKEY_get1_EC_KEY(pkey), EC_KEY_free); if (!eckey) { throw error::ecdsa_exception(error::ecdsa_error::invalid_key); } if (EC_KEY_check_key(eckey.get()) == 0) throw error::ecdsa_exception(error::ecdsa_error::invalid_key); #endif } std::string der_to_p1363_signature(const std::string& der_signature, std::error_code& ec) const { const unsigned char* possl_signature = reinterpret_cast(der_signature.data()); std::unique_ptr sig( d2i_ECDSA_SIG(nullptr, &possl_signature, static_cast(der_signature.length())), ECDSA_SIG_free); if (!sig) { ec = error::signature_generation_error::signature_decoding_failed; return {}; } #ifdef JWT_OPENSSL_1_0_0 auto rr = helper::bn2raw(sig->r); auto rs = helper::bn2raw(sig->s); #else const BIGNUM* r; const BIGNUM* s; ECDSA_SIG_get0(sig.get(), &r, &s); auto rr = helper::bn2raw(r); auto rs = helper::bn2raw(s); #endif if (rr.size() > signature_length / 2 || rs.size() > signature_length / 2) throw std::logic_error("bignum size exceeded expected length"); rr.insert(0, signature_length / 2 - rr.size(), '\0'); rs.insert(0, signature_length / 2 - rs.size(), '\0'); return rr + rs; } std::string p1363_to_der_signature(const std::string& signature, std::error_code& ec) const { ec.clear(); auto r = helper::raw2bn(signature.substr(0, signature.size() / 2), ec); if (ec) return {}; auto s = helper::raw2bn(signature.substr(signature.size() / 2), ec); if (ec) return {}; ECDSA_SIG* psig; #ifdef JWT_OPENSSL_1_0_0 ECDSA_SIG sig; sig.r = r.get(); sig.s = s.get(); psig = &sig; #else std::unique_ptr sig(ECDSA_SIG_new(), ECDSA_SIG_free); if (!sig) { ec = error::signature_verification_error::create_context_failed; return {}; } ECDSA_SIG_set0(sig.get(), r.release(), s.release()); psig = sig.get(); #endif int length = i2d_ECDSA_SIG(psig, nullptr); if (length < 0) { ec = error::signature_verification_error::signature_encoding_failed; return {}; } std::string der_signature(length, '\0'); unsigned char* psbuffer = (unsigned char*)der_signature.data(); length = i2d_ECDSA_SIG(psig, &psbuffer); if (length < 0) { ec = error::signature_verification_error::signature_encoding_failed; return {}; } der_signature.resize(length); return der_signature; } /// OpenSSL struct containing keys helper::evp_pkey_handle pkey; /// Hash generator function const EVP_MD* (*md)(); /// algorithm's name const std::string alg_name; /// Length of the resulting signature const size_t signature_length; }; #if !defined(JWT_OPENSSL_1_0_0) && !defined(JWT_OPENSSL_1_1_0) /** * \brief Base class for EdDSA family of algorithms * * https://tools.ietf.org/html/rfc8032 * * The EdDSA algorithms were introduced in [OpenSSL v1.1.1](https://www.openssl.org/news/openssl-1.1.1-notes.html), * so these algorithms are only available when building against this version or higher. */ struct eddsa { /** * Construct new eddsa algorithm * \param public_key EdDSA public key in PEM format * \param private_key EdDSA private key or empty string if not available. If empty, signing will always * fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password * to decrypt private key pem. * \param name Name of the algorithm */ eddsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, const std::string& private_key_password, std::string name) : alg_name(std::move(name)) { if (!private_key.empty()) { pkey = helper::load_private_key_from_string(private_key, private_key_password); } else if (!public_key.empty()) { pkey = helper::load_public_key_from_string(public_key, public_key_password); } else throw error::ecdsa_exception(error::ecdsa_error::load_key_bio_read); } /** * Sign jwt data * \param data The data to sign * \param ec error_code filled with details on error * \return EdDSA signature for the given data */ std::string sign(const std::string& data, std::error_code& ec) const { ec.clear(); auto ctx = helper::make_evp_md_ctx(); if (!ctx) { ec = error::signature_generation_error::create_context_failed; return {}; } if (!EVP_DigestSignInit(ctx.get(), nullptr, nullptr, nullptr, pkey.get())) { ec = error::signature_generation_error::signinit_failed; return {}; } size_t len = EVP_PKEY_size(pkey.get()); std::string res(len, '\0'); // LibreSSL is the special kid in the block, as it does not support EVP_DigestSign. // OpenSSL on the otherhand does not support using EVP_DigestSignUpdate for eddsa, which is why we end up with this // mess. #if defined(LIBRESSL_VERSION_NUMBER) || defined(LIBWOLFSSL_VERSION_HEX) ERR_clear_error(); if (EVP_DigestSignUpdate(ctx.get(), reinterpret_cast(data.data()), data.size()) != 1) { std::cout << ERR_error_string(ERR_get_error(), NULL) << std::endl; ec = error::signature_generation_error::signupdate_failed; return {}; } if (EVP_DigestSignFinal(ctx.get(), reinterpret_cast(&res[0]), &len) != 1) { ec = error::signature_generation_error::signfinal_failed; return {}; } #else if (EVP_DigestSign(ctx.get(), reinterpret_cast(&res[0]), &len, reinterpret_cast(data.data()), data.size()) != 1) { ec = error::signature_generation_error::signfinal_failed; return {}; } #endif res.resize(len); return res; } /** * Check if signature is valid * \param data The data to check signature against * \param signature Signature provided by the jwt * \param ec Filled with details on error */ void verify(const std::string& data, const std::string& signature, std::error_code& ec) const { ec.clear(); auto ctx = helper::make_evp_md_ctx(); if (!ctx) { ec = error::signature_verification_error::create_context_failed; return; } if (!EVP_DigestVerifyInit(ctx.get(), nullptr, nullptr, nullptr, pkey.get())) { ec = error::signature_verification_error::verifyinit_failed; return; } // LibreSSL is the special kid in the block, as it does not support EVP_DigestVerify. // OpenSSL on the otherhand does not support using EVP_DigestVerifyUpdate for eddsa, which is why we end up with this // mess. #if defined(LIBRESSL_VERSION_NUMBER) || defined(LIBWOLFSSL_VERSION_HEX) if (EVP_DigestVerifyUpdate(ctx.get(), reinterpret_cast(data.data()), data.size()) != 1) { ec = error::signature_verification_error::verifyupdate_failed; return; } if (EVP_DigestVerifyFinal(ctx.get(), reinterpret_cast(signature.data()), signature.size()) != 1) { ec = error::signature_verification_error::verifyfinal_failed; return; } #else auto res = EVP_DigestVerify(ctx.get(), reinterpret_cast(signature.data()), signature.size(), reinterpret_cast(data.data()), data.size()); if (res != 1) { ec = error::signature_verification_error::verifyfinal_failed; return; } #endif } /** * Returns the algorithm name provided to the constructor * \return algorithm's name */ std::string name() const { return alg_name; } private: /// OpenSSL struct containing keys helper::evp_pkey_handle pkey; /// algorithm's name const std::string alg_name; }; #endif /** * \brief Base class for PSS-RSA family of algorithms */ struct pss { /** * Construct new pss algorithm * \param public_key RSA public key in PEM format * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. * \param md Pointer to hash function * \param name Name of the algorithm */ pss(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD* (*md)(), std::string name) : md(md), alg_name(std::move(name)) { if (!private_key.empty()) { pkey = helper::load_private_key_from_string(private_key, private_key_password); } else if (!public_key.empty()) { pkey = helper::load_public_key_from_string(public_key, public_key_password); } else throw error::rsa_exception(error::rsa_error::no_key_provided); } /** * Sign jwt data * \param data The data to sign * \param ec error_code filled with details on error * \return ECDSA signature for the given data */ std::string sign(const std::string& data, std::error_code& ec) const { ec.clear(); auto md_ctx = helper::make_evp_md_ctx(); if (!md_ctx) { ec = error::signature_generation_error::create_context_failed; return {}; } EVP_PKEY_CTX* ctx = nullptr; if (EVP_DigestSignInit(md_ctx.get(), &ctx, md(), nullptr, pkey.get()) != 1) { ec = error::signature_generation_error::signinit_failed; return {}; } if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0) { ec = error::signature_generation_error::rsa_padding_failed; return {}; } // wolfSSL does not require EVP_PKEY_CTX_set_rsa_pss_saltlen. The default behavior // sets the salt length to the hash length. Unlike OpenSSL which exposes this functionality. #ifndef LIBWOLFSSL_VERSION_HEX if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, -1) <= 0) { ec = error::signature_generation_error::set_rsa_pss_saltlen_failed; return {}; } #endif if (EVP_DigestUpdate(md_ctx.get(), data.data(), data.size()) != 1) { ec = error::signature_generation_error::digestupdate_failed; return {}; } size_t size = EVP_PKEY_size(pkey.get()); std::string res(size, 0x00); if (EVP_DigestSignFinal( md_ctx.get(), (unsigned char*)res.data(), // NOLINT(google-readability-casting) requires `const_cast` &size) <= 0) { ec = error::signature_generation_error::signfinal_failed; return {}; } return res; } /** * Check if signature is valid * \param data The data to check signature against * \param signature Signature provided by the jwt * \param ec Filled with error details */ void verify(const std::string& data, const std::string& signature, std::error_code& ec) const { ec.clear(); auto md_ctx = helper::make_evp_md_ctx(); if (!md_ctx) { ec = error::signature_verification_error::create_context_failed; return; } EVP_PKEY_CTX* ctx = nullptr; if (EVP_DigestVerifyInit(md_ctx.get(), &ctx, md(), nullptr, pkey.get()) != 1) { ec = error::signature_verification_error::verifyinit_failed; return; } if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0) { ec = error::signature_generation_error::rsa_padding_failed; return; } // wolfSSL does not require EVP_PKEY_CTX_set_rsa_pss_saltlen. The default behavior // sets the salt length to the hash length. Unlike OpenSSL which exposes this functionality. #ifndef LIBWOLFSSL_VERSION_HEX if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, -1) <= 0) { ec = error::signature_verification_error::set_rsa_pss_saltlen_failed; return; } #endif if (EVP_DigestUpdate(md_ctx.get(), data.data(), data.size()) != 1) { ec = error::signature_verification_error::verifyupdate_failed; return; } if (EVP_DigestVerifyFinal(md_ctx.get(), (unsigned char*)signature.data(), signature.size()) <= 0) { ec = error::signature_verification_error::verifyfinal_failed; return; } } /** * Returns the algorithm name provided to the constructor * \return algorithm's name */ std::string name() const { return alg_name; } private: /// OpenSSL structure containing keys helper::evp_pkey_handle pkey; /// Hash generator function const EVP_MD* (*md)(); /// algorithm's name const std::string alg_name; }; /** * HS256 algorithm */ struct hs256 : public hmacsha { /** * Construct new instance of algorithm * \param key HMAC signing key */ explicit hs256(std::string key) : hmacsha(std::move(key), EVP_sha256, "HS256") {} }; /** * HS384 algorithm */ struct hs384 : public hmacsha { /** * Construct new instance of algorithm * \param key HMAC signing key */ explicit hs384(std::string key) : hmacsha(std::move(key), EVP_sha384, "HS384") {} }; /** * HS512 algorithm */ struct hs512 : public hmacsha { /** * Construct new instance of algorithm * \param key HMAC signing key */ explicit hs512(std::string key) : hmacsha(std::move(key), EVP_sha512, "HS512") {} }; /** * RS256 algorithm. * * This data structure is used to describe the RSA256 and can be used to verify JWTs */ struct rs256 : public rsa { /** * \brief Construct new instance of algorithm * * \param public_key RSA public key in PEM format * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. */ explicit rs256(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "RS256") {} }; /** * RS384 algorithm */ struct rs384 : public rsa { /** * Construct new instance of algorithm * \param public_key RSA public key in PEM format * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. */ explicit rs384(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "RS384") {} }; /** * RS512 algorithm */ struct rs512 : public rsa { /** * Construct new instance of algorithm * \param public_key RSA public key in PEM format * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. */ explicit rs512(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "RS512") {} }; /** * ES256 algorithm */ struct es256 : public ecdsa { /** * Construct new instance of algorithm * \param public_key ECDSA public key in PEM format * \param private_key ECDSA private key or empty string if not available. If empty, signing will always * fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password * to decrypt private key pem. */ explicit es256(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "ES256", 64) {} }; /** * ES384 algorithm */ struct es384 : public ecdsa { /** * Construct new instance of algorithm * \param public_key ECDSA public key in PEM format * \param private_key ECDSA private key or empty string if not available. If empty, signing will always * fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password * to decrypt private key pem. */ explicit es384(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "ES384", 96) {} }; /** * ES512 algorithm */ struct es512 : public ecdsa { /** * Construct new instance of algorithm * \param public_key ECDSA public key in PEM format * \param private_key ECDSA private key or empty string if not available. If empty, signing will always * fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password * to decrypt private key pem. */ explicit es512(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "ES512", 132) {} }; /** * ES256K algorithm */ struct es256k : public ecdsa { /** * Construct new instance of algorithm * \param public_key ECDSA public key in PEM format * \param private_key ECDSA private key or empty string if not available. If empty, signing will always * fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. */ explicit es256k(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "ES256K", 64) {} }; #if !defined(JWT_OPENSSL_1_0_0) && !defined(JWT_OPENSSL_1_1_0) /** * Ed25519 algorithm * * https://en.wikipedia.org/wiki/EdDSA#Ed25519 * * Requires at least OpenSSL 1.1.1. */ struct ed25519 : public eddsa { /** * Construct new instance of algorithm * \param public_key Ed25519 public key in PEM format * \param private_key Ed25519 private key or empty string if not available. If empty, signing will always * fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password * to decrypt private key pem. */ explicit ed25519(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : eddsa(public_key, private_key, public_key_password, private_key_password, "EdDSA") {} }; /** * Ed448 algorithm * * https://en.wikipedia.org/wiki/EdDSA#Ed448 * * Requires at least OpenSSL 1.1.1. */ struct ed448 : public eddsa { /** * Construct new instance of algorithm * \param public_key Ed448 public key in PEM format * \param private_key Ed448 private key or empty string if not available. If empty, signing will always * fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password * to decrypt private key pem. */ explicit ed448(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : eddsa(public_key, private_key, public_key_password, private_key_password, "EdDSA") {} }; #endif /** * PS256 algorithm */ struct ps256 : public pss { /** * Construct new instance of algorithm * \param public_key RSA public key in PEM format * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. */ explicit ps256(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "PS256") {} }; /** * PS384 algorithm */ struct ps384 : public pss { /** * Construct new instance of algorithm * \param public_key RSA public key in PEM format * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. */ explicit ps384(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "PS384") {} }; /** * PS512 algorithm */ struct ps512 : public pss { /** * Construct new instance of algorithm * \param public_key RSA public key in PEM format * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. */ explicit ps512(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "PS512") {} }; } // namespace algorithm /** * \brief JSON Abstractions for working with any library */ namespace json { /** * \brief Categories for the various JSON types used in JWTs * * This enum is to abstract the third party underlying types and allows the library * to identify the different structures and reason about them without needing a "concept" * to capture that defintion to compare against a concrete type. */ enum class type { boolean, integer, number, string, array, object }; } // namespace json namespace details { #ifdef __cpp_lib_void_t template using void_t = std::void_t; #else // https://en.cppreference.com/w/cpp/types/void_t template struct make_void { using type = void; }; template using void_t = typename make_void::type; #endif #ifdef __cpp_lib_experimental_detect template class _Op, typename... _Args> using is_detected = std::experimental::is_detected<_Op, _Args...>; #else struct nonesuch { nonesuch() = delete; ~nonesuch() = delete; nonesuch(nonesuch const&) = delete; nonesuch(nonesuch const&&) = delete; void operator=(nonesuch const&) = delete; void operator=(nonesuch&&) = delete; }; // https://en.cppreference.com/w/cpp/experimental/is_detected template class Op, class... Args> struct detector { using value = std::false_type; using type = Default; }; template class Op, class... Args> struct detector>, Op, Args...> { using value = std::true_type; using type = Op; }; template class Op, class... Args> using is_detected = typename detector::value; #endif template using is_signature = typename std::is_same; template class Op, typename Signature> struct is_function_signature_detected { using type = Op; static constexpr auto value = is_detected::value && std::is_function::value && is_signature::value; }; template struct supports_get_type { template using get_type_t = decltype(T::get_type); static constexpr auto value = is_function_signature_detected::value; // Internal assertions for better feedback static_assert(value, "traits implementation must provide `jwt::json::type get_type(const value_type&)`"); }; #define JWT_CPP_JSON_TYPE_TYPE(TYPE) json_##TYPE_type #define JWT_CPP_AS_TYPE_T(TYPE) as_##TYPE_t #define JWT_CPP_SUPPORTS_AS(TYPE) \ template \ struct supports_as_##TYPE { \ template \ using JWT_CPP_AS_TYPE_T(TYPE) = decltype(T::as_##TYPE); \ \ static constexpr auto value = \ is_function_signature_detected::value; \ \ static_assert(value, "traits implementation must provide `" #TYPE "_type as_" #TYPE "(const value_type&)`"); \ } JWT_CPP_SUPPORTS_AS(object); JWT_CPP_SUPPORTS_AS(array); JWT_CPP_SUPPORTS_AS(string); JWT_CPP_SUPPORTS_AS(number); JWT_CPP_SUPPORTS_AS(integer); JWT_CPP_SUPPORTS_AS(boolean); #undef JWT_CPP_JSON_TYPE_TYPE #undef JWT_CPP_AS_TYPE_T #undef JWT_CPP_SUPPORTS_AS template struct is_valid_traits { static constexpr auto value = supports_get_type::value && supports_as_object::value && supports_as_array::value && supports_as_string::value && supports_as_number::value && supports_as_integer::value && supports_as_boolean::value; }; template struct is_valid_json_value { static constexpr auto value = std::is_default_constructible::value && std::is_constructible::value && // a more generic is_copy_constructible std::is_move_constructible::value && std::is_assignable::value && std::is_copy_assignable::value && std::is_move_assignable::value; // TODO(prince-chrismc): Stream operators }; // https://stackoverflow.com/a/53967057/8480874 template struct is_iterable : std::false_type {}; template struct is_iterable())), decltype(std::end(std::declval())), #if __cplusplus > 201402L decltype(std::cbegin(std::declval())), decltype(std::cend(std::declval())) #else decltype(std::begin(std::declval())), decltype(std::end(std::declval())) #endif >> : std::true_type { }; #if __cplusplus > 201703L template inline constexpr bool is_iterable_v = is_iterable::value; #endif template using is_count_signature = typename std::is_integral().count( std::declval()))>; template struct is_subcription_operator_signature : std::false_type {}; template struct is_subcription_operator_signature< object_type, string_type, void_t().operator[](std::declval()))>> : std::true_type { // TODO(prince-chrismc): I am not convienced this is meaningful anymore static_assert( value, "object_type must implementate the subscription operator '[]' taking string_type as an argument"); }; template using is_at_const_signature = typename std::is_same().at(std::declval())), const value_type&>; template struct is_valid_json_object { template using mapped_type_t = typename T::mapped_type; template using key_type_t = typename T::key_type; template using iterator_t = typename T::iterator; template using const_iterator_t = typename T::const_iterator; static constexpr auto value = std::is_constructible::value && is_detected::value && std::is_same::value && is_detected::value && (std::is_same::value || std::is_constructible::value) && is_detected::value && is_detected::value && is_iterable::value && is_count_signature::value && is_subcription_operator_signature::value && is_at_const_signature::value; }; template struct is_valid_json_array { template using value_type_t = typename T::value_type; using front_base_type = typename std::decay().front())>::type; static constexpr auto value = std::is_constructible::value && is_iterable::value && is_detected::value && std::is_same::value && std::is_same::value; }; template using is_substr_start_end_index_signature = typename std::is_same().substr(std::declval(), std::declval())), string_type>; template using is_substr_start_index_signature = typename std::is_same().substr(std::declval())), string_type>; template using is_std_operate_plus_signature = typename std::is_same(), std::declval())), string_type>; template struct is_valid_json_string { static constexpr auto substr = is_substr_start_end_index_signature::value && is_substr_start_index_signature::value; static_assert(substr, "string_type must have a substr method taking only a start index and an overload " "taking a start and end index, both must return a string_type"); static constexpr auto operator_plus = is_std_operate_plus_signature::value; static_assert(operator_plus, "string_type must have a '+' operator implemented which returns the concatenated string"); static constexpr auto value = std::is_constructible::value && substr && operator_plus; }; template struct is_valid_json_number { static constexpr auto value = std::is_floating_point::value && std::is_constructible::value; }; template struct is_valid_json_integer { static constexpr auto value = std::is_signed::value && !std::is_floating_point::value && std::is_constructible::value; }; template struct is_valid_json_boolean { static constexpr auto value = std::is_convertible::value && std::is_constructible::value; }; template struct is_valid_json_types { // Internal assertions for better feedback static_assert(is_valid_json_value::value, "value_type must meet basic requirements, default constructor, copyable, moveable"); static_assert(is_valid_json_object::value, "object_type must be a string_type to value_type container"); static_assert(is_valid_json_array::value, "array_type must be a container of value_type"); static constexpr auto value = is_valid_json_value::value && is_valid_json_object::value && is_valid_json_array::value && is_valid_json_string::value && is_valid_json_number::value && is_valid_json_integer::value && is_valid_json_boolean::value; }; } // namespace details /** * \brief a class to store a generic JSON value as claim * * \tparam json_traits : JSON implementation traits * * \see [RFC 7519: JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519) */ template class basic_claim { /** * The reason behind this is to provide an expressive abstraction without * over complicating the API. For more information take the time to read * https://github.com/nlohmann/json/issues/774. It maybe be expanded to * support custom string types. */ static_assert(std::is_same::value || std::is_convertible::value || std::is_constructible::value, "string_type must be a std::string, convertible to a std::string, or construct a std::string."); static_assert( details::is_valid_json_types::value, "must satisfy json container requirements"); static_assert(details::is_valid_traits::value, "traits must satisfy requirements"); typename json_traits::value_type val; public: /** * Order list of strings */ using set_t = std::set; basic_claim() = default; basic_claim(const basic_claim&) = default; basic_claim(basic_claim&&) = default; basic_claim& operator=(const basic_claim&) = default; basic_claim& operator=(basic_claim&&) = default; ~basic_claim() = default; JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::string_type s) : val(std::move(s)) {} JWT_CLAIM_EXPLICIT basic_claim(const date& d) : val(typename json_traits::integer_type(std::chrono::system_clock::to_time_t(d))) {} JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::array_type a) : val(std::move(a)) {} JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::value_type v) : val(std::move(v)) {} JWT_CLAIM_EXPLICIT basic_claim(const set_t& s) : val(typename json_traits::array_type(s.begin(), s.end())) {} template basic_claim(Iterator begin, Iterator end) : val(typename json_traits::array_type(begin, end)) {} /** * Get wrapped JSON value * \return Wrapped JSON value */ typename json_traits::value_type to_json() const { return val; } /** * Parse input stream into underlying JSON value * \return input stream */ std::istream& operator>>(std::istream& is) { return is >> val; } /** * Serialize claim to output stream from wrapped JSON value * \return output stream */ std::ostream& operator<<(std::ostream& os) { return os << val; } /** * Get type of contained JSON value * \return Type * \throw std::logic_error An internal error occurred */ json::type get_type() const { return json_traits::get_type(val); } /** * Get the contained JSON value as a string * \return content as string * \throw std::bad_cast Content was not a string */ typename json_traits::string_type as_string() const { return json_traits::as_string(val); } /** * \brief Get the contained JSON value as a date * * If the value is a decimal, it is rounded up to the closest integer * * \return content as date * \throw std::bad_cast Content was not a date */ date as_date() const { using std::chrono::system_clock; if (get_type() == json::type::number) return system_clock::from_time_t(std::round(as_number())); return system_clock::from_time_t(as_integer()); } /** * Get the contained JSON value as an array * \return content as array * \throw std::bad_cast Content was not an array */ typename json_traits::array_type as_array() const { return json_traits::as_array(val); } /** * Get the contained JSON value as a set of strings * \return content as set of strings * \throw std::bad_cast Content was not an array of string */ set_t as_set() const { set_t res; for (const auto& e : json_traits::as_array(val)) { res.insert(json_traits::as_string(e)); } return res; } /** * Get the contained JSON value as an integer * \return content as int * \throw std::bad_cast Content was not an int */ typename json_traits::integer_type as_integer() const { return json_traits::as_integer(val); } /** * Get the contained JSON value as a bool * \return content as bool * \throw std::bad_cast Content was not a bool */ typename json_traits::boolean_type as_boolean() const { return json_traits::as_boolean(val); } /** * Get the contained JSON value as a number * \return content as double * \throw std::bad_cast Content was not a number */ typename json_traits::number_type as_number() const { return json_traits::as_number(val); } }; namespace error { /** * Attempt to parse JSON was unsuccessful */ struct invalid_json_exception : public std::runtime_error { invalid_json_exception() : runtime_error("invalid json") {} }; /** * Attempt to access claim was unsuccessful */ struct claim_not_present_exception : public std::out_of_range { claim_not_present_exception() : out_of_range("claim not found") {} }; } // namespace error namespace details { template struct map_of_claims { typename json_traits::object_type claims; using basic_claim_t = basic_claim; using iterator = typename json_traits::object_type::iterator; using const_iterator = typename json_traits::object_type::const_iterator; map_of_claims() = default; map_of_claims(const map_of_claims&) = default; map_of_claims(map_of_claims&&) = default; map_of_claims& operator=(const map_of_claims&) = default; map_of_claims& operator=(map_of_claims&&) = default; map_of_claims(typename json_traits::object_type json) : claims(std::move(json)) {} iterator begin() { return claims.begin(); } iterator end() { return claims.end(); } const_iterator cbegin() const { return claims.begin(); } const_iterator cend() const { return claims.end(); } const_iterator begin() const { return claims.begin(); } const_iterator end() const { return claims.end(); } /** * \brief Parse a JSON string into a map of claims * * The implication is that a "map of claims" is identic to a JSON object * * \param str JSON data to be parse as an object * \return content as JSON object */ static typename json_traits::object_type parse_claims(const typename json_traits::string_type& str) { typename json_traits::value_type val; if (!json_traits::parse(val, str)) throw error::invalid_json_exception(); return json_traits::as_object(val); }; /** * Check if a claim is present in the map * \return true if claim was present, false otherwise */ bool has_claim(const typename json_traits::string_type& name) const noexcept { return claims.count(name) != 0; } /** * Get a claim by name * * \param name the name of the desired claim * \return Requested claim * \throw jwt::error::claim_not_present_exception if the claim was not present */ basic_claim_t get_claim(const typename json_traits::string_type& name) const { if (!has_claim(name)) throw error::claim_not_present_exception(); return basic_claim_t{claims.at(name)}; } }; } // namespace details /** * Base class that represents a token payload. * Contains Convenience accessors for common claims. */ template class payload { protected: details::map_of_claims payload_claims; public: using basic_claim_t = basic_claim; /** * Check if issuer is present ("iss") * \return true if present, false otherwise */ bool has_issuer() const noexcept { return has_payload_claim("iss"); } /** * Check if subject is present ("sub") * \return true if present, false otherwise */ bool has_subject() const noexcept { return has_payload_claim("sub"); } /** * Check if audience is present ("aud") * \return true if present, false otherwise */ bool has_audience() const noexcept { return has_payload_claim("aud"); } /** * Check if expires is present ("exp") * \return true if present, false otherwise */ bool has_expires_at() const noexcept { return has_payload_claim("exp"); } /** * Check if not before is present ("nbf") * \return true if present, false otherwise */ bool has_not_before() const noexcept { return has_payload_claim("nbf"); } /** * Check if issued at is present ("iat") * \return true if present, false otherwise */ bool has_issued_at() const noexcept { return has_payload_claim("iat"); } /** * Check if token id is present ("jti") * \return true if present, false otherwise */ bool has_id() const noexcept { return has_payload_claim("jti"); } /** * Get issuer claim * \return issuer as string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_issuer() const { return get_payload_claim("iss").as_string(); } /** * Get subject claim * \return subject as string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_subject() const { return get_payload_claim("sub").as_string(); } /** * Get audience claim * \return audience as a set of strings * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a set (Should not happen in a valid token) */ typename basic_claim_t::set_t get_audience() const { auto aud = get_payload_claim("aud"); if (aud.get_type() == json::type::string) return {aud.as_string()}; return aud.as_set(); } /** * Get expires claim * \return expires as a date in utc * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a date (Should not happen in a valid token) */ date get_expires_at() const { return get_payload_claim("exp").as_date(); } /** * Get not valid before claim * \return nbf date in utc * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a date (Should not happen in a valid token) */ date get_not_before() const { return get_payload_claim("nbf").as_date(); } /** * Get issued at claim * \return issued at as date in utc * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a date (Should not happen in a valid token) */ date get_issued_at() const { return get_payload_claim("iat").as_date(); } /** * Get id claim * \return id as string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_id() const { return get_payload_claim("jti").as_string(); } /** * Check if a payload claim is present * \return true if claim was present, false otherwise */ bool has_payload_claim(const typename json_traits::string_type& name) const noexcept { return payload_claims.has_claim(name); } /** * Get payload claim * \return Requested claim * \throw std::runtime_error If claim was not present */ basic_claim_t get_payload_claim(const typename json_traits::string_type& name) const { return payload_claims.get_claim(name); } }; /** * Base class that represents a token header. * Contains Convenience accessors for common claims. */ template class header { protected: details::map_of_claims header_claims; public: using basic_claim_t = basic_claim; /** * Check if algorithm is present ("alg") * \return true if present, false otherwise */ bool has_algorithm() const noexcept { return has_header_claim("alg"); } /** * Check if type is present ("typ") * \return true if present, false otherwise */ bool has_type() const noexcept { return has_header_claim("typ"); } /** * Check if content type is present ("cty") * \return true if present, false otherwise */ bool has_content_type() const noexcept { return has_header_claim("cty"); } /** * Check if key id is present ("kid") * \return true if present, false otherwise */ bool has_key_id() const noexcept { return has_header_claim("kid"); } /** * Get algorithm claim * \return algorithm as string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_algorithm() const { return get_header_claim("alg").as_string(); } /** * Get type claim * \return type as a string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_type() const { return get_header_claim("typ").as_string(); } /** * Get content type claim * \return content type as string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_content_type() const { return get_header_claim("cty").as_string(); } /** * Get key id claim * \return key id as string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_key_id() const { return get_header_claim("kid").as_string(); } /** * Check if a header claim is present * \return true if claim was present, false otherwise */ bool has_header_claim(const typename json_traits::string_type& name) const noexcept { return header_claims.has_claim(name); } /** * Get header claim * \return Requested claim * \throw std::runtime_error If claim was not present */ basic_claim_t get_header_claim(const typename json_traits::string_type& name) const { return header_claims.get_claim(name); } }; /** * Class containing all information about a decoded token */ template class decoded_jwt : public header, public payload { protected: /// Unmodified token, as passed to constructor typename json_traits::string_type token; /// Header part decoded from base64 typename json_traits::string_type header; /// Unmodified header part in base64 typename json_traits::string_type header_base64; /// Payload part decoded from base64 typename json_traits::string_type payload; /// Unmodified payload part in base64 typename json_traits::string_type payload_base64; /// Signature part decoded from base64 typename json_traits::string_type signature; /// Unmodified signature part in base64 typename json_traits::string_type signature_base64; public: using basic_claim_t = basic_claim; #ifndef JWT_DISABLE_BASE64 /** * \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 */ JWT_CLAIM_EXPLICIT decoded_jwt(const typename json_traits::string_type& token) : decoded_jwt(token, [](const typename json_traits::string_type& str) { return base::decode(base::pad(str)); }) {} #endif /** * \brief Parses a given token * * \tparam Decode is callable, 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 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 */ template decoded_jwt(const typename json_traits::string_type& token, Decode decode) : token(token) { auto hdr_end = token.find('.'); if (hdr_end == json_traits::string_type::npos) throw std::invalid_argument("invalid token supplied"); auto payload_end = token.find('.', hdr_end + 1); if (payload_end == json_traits::string_type::npos) throw std::invalid_argument("invalid token supplied"); header_base64 = token.substr(0, hdr_end); payload_base64 = token.substr(hdr_end + 1, payload_end - hdr_end - 1); signature_base64 = token.substr(payload_end + 1); header = decode(header_base64); payload = decode(payload_base64); signature = decode(signature_base64); this->header_claims = details::map_of_claims::parse_claims(header); this->payload_claims = details::map_of_claims::parse_claims(payload); } /** * Get token string, as passed to constructor * \return token as passed to constructor */ const typename json_traits::string_type& get_token() const noexcept { return token; } /** * Get header part as json string * \return header part after base64 decoding */ const typename json_traits::string_type& get_header() const noexcept { return header; } /** * Get payload part as json string * \return payload part after base64 decoding */ const typename json_traits::string_type& get_payload() const noexcept { return payload; } /** * Get signature part as json string * \return signature part after base64 decoding */ const typename json_traits::string_type& get_signature() const noexcept { return signature; } /** * Get header part as base64 string * \return header part before base64 decoding */ const typename json_traits::string_type& get_header_base64() const noexcept { return header_base64; } /** * Get payload part as base64 string * \return payload part before base64 decoding */ const typename json_traits::string_type& get_payload_base64() const noexcept { return payload_base64; } /** * Get signature part as base64 string * \return signature part before base64 decoding */ const typename json_traits::string_type& get_signature_base64() const noexcept { return signature_base64; } /** * Get all payload as JSON object * \return map of claims */ typename json_traits::object_type get_payload_json() const { return this->payload_claims.claims; } /** * Get all header as JSON object * \return map of claims */ typename json_traits::object_type get_header_json() const { return this->header_claims.claims; } /** * Get a payload claim by name * * \param name the name of the desired claim * \return Requested claim * \throw jwt::error::claim_not_present_exception if the claim was not present */ basic_claim_t get_payload_claim(const typename json_traits::string_type& name) const { return this->payload_claims.get_claim(name); } /** * Get a header claim by name * * \param name the name of the desired claim * \return Requested claim * \throw jwt::error::claim_not_present_exception if the claim was not present */ basic_claim_t get_header_claim(const typename json_traits::string_type& name) const { return this->header_claims.get_claim(name); } }; /** * Builder class to build and sign a new token * Use jwt::create() to get an instance of this class. */ template class builder { typename json_traits::object_type header_claims; typename json_traits::object_type payload_claims; /// Instance of clock type Clock clock; public: /** * Constructor for building a new builder instance * \param c Clock instance */ JWT_CLAIM_EXPLICIT builder(Clock c) : clock(c) {} /** * Set a header claim. * \param id Name of the claim * \param c Claim to add * \return *this to allow for method chaining */ builder& set_header_claim(const typename json_traits::string_type& id, typename json_traits::value_type c) { header_claims[id] = std::move(c); return *this; } /** * Set a header claim. * \param id Name of the claim * \param c Claim to add * \return *this to allow for method chaining */ builder& set_header_claim(const typename json_traits::string_type& id, basic_claim c) { header_claims[id] = c.to_json(); return *this; } /** * Set a payload claim. * \param id Name of the claim * \param c Claim to add * \return *this to allow for method chaining */ builder& set_payload_claim(const typename json_traits::string_type& id, typename json_traits::value_type c) { payload_claims[id] = std::move(c); return *this; } /** * Set a payload claim. * \param id Name of the claim * \param c Claim to add * \return *this to allow for method chaining */ builder& set_payload_claim(const typename json_traits::string_type& id, basic_claim c) { payload_claims[id] = c.to_json(); return *this; } /** * \brief Set algorithm claim * You normally don't need to do this, as the algorithm is automatically set if you don't change it. * * \param str Name of algorithm * \return *this to allow for method chaining */ builder& set_algorithm(typename json_traits::string_type str) { return set_header_claim("alg", typename json_traits::value_type(str)); } /** * Set type claim * \param str Type to set * \return *this to allow for method chaining */ builder& set_type(typename json_traits::string_type str) { return set_header_claim("typ", typename json_traits::value_type(str)); } /** * Set content type claim * \param str Type to set * \return *this to allow for method chaining */ builder& set_content_type(typename json_traits::string_type str) { return set_header_claim("cty", typename json_traits::value_type(str)); } /** * \brief Set key id claim * * \param str Key id to set * \return *this to allow for method chaining */ builder& set_key_id(typename json_traits::string_type str) { return set_header_claim("kid", typename json_traits::value_type(str)); } /** * Set issuer claim * \param str Issuer to set * \return *this to allow for method chaining */ builder& set_issuer(typename json_traits::string_type str) { return set_payload_claim("iss", typename json_traits::value_type(str)); } /** * Set subject claim * \param str Subject to set * \return *this to allow for method chaining */ builder& set_subject(typename json_traits::string_type str) { return set_payload_claim("sub", typename json_traits::value_type(str)); } /** * Set audience claim * \param a Audience set * \return *this to allow for method chaining */ builder& set_audience(typename json_traits::array_type a) { return set_payload_claim("aud", typename json_traits::value_type(a)); } /** * Set audience claim * \param aud Single audience * \return *this to allow for method chaining */ builder& set_audience(typename json_traits::string_type aud) { return set_payload_claim("aud", typename json_traits::value_type(aud)); } /** * Set expires at claim * \param d Expires time * \return *this to allow for method chaining */ builder& set_expires_at(const date& d) { return set_payload_claim("exp", basic_claim(d)); } /** * Set expires at claim to @p d from the current moment * \param d token expiration timeout * \return *this to allow for method chaining */ template builder& set_expires_in(const std::chrono::duration& d) { return set_payload_claim("exp", basic_claim(clock.now() + d)); } /** * Set not before claim * \param d First valid time * \return *this to allow for method chaining */ builder& set_not_before(const date& d) { return set_payload_claim("nbf", basic_claim(d)); } /** * Set issued at claim * \param d Issued at time, should be current time * \return *this to allow for method chaining */ builder& set_issued_at(const date& d) { return set_payload_claim("iat", basic_claim(d)); } /** * Set issued at claim to the current moment * \return *this to allow for method chaining */ builder& set_issued_now() { return set_issued_at(clock.now()); } /** * Set id claim * \param str ID to set * \return *this to allow for method chaining */ builder& set_id(const typename json_traits::string_type& str) { return set_payload_claim("jti", typename json_traits::value_type(str)); } /** * Sign token and return result * \tparam Algo Callable method which takes a string_type and return the signed input as a string_type * \tparam Encode Callable method which takes a string_type and base64url safe encodes it, * MUST return the result with no padding; trim the result. * \param algo Instance of an algorithm to sign the token with * \param encode Callable to transform the serialized json to base64 with no padding * \return Final token as a string * * \note If the 'alg' header in not set in the token it will be set to `algo.name()` */ template typename json_traits::string_type sign(const Algo& algo, Encode encode) const { std::error_code ec; auto res = sign(algo, encode, ec); error::throw_if_error(ec); return res; } #ifndef JWT_DISABLE_BASE64 /** * Sign token and return result * * using the `jwt::base` functions provided * * \param algo Instance of an algorithm to sign the token with * \return Final token as a string */ template typename json_traits::string_type sign(const Algo& algo) const { std::error_code ec; auto res = sign(algo, ec); error::throw_if_error(ec); return res; } #endif /** * Sign token and return result * \tparam Algo Callable method which takes a string_type and return the signed input as a string_type * \tparam Encode Callable method which takes a string_type and base64url safe encodes it, * MUST return the result with no padding; trim the result. * \param algo Instance of an algorithm to sign the token with * \param encode Callable to transform the serialized json to base64 with no padding * \param ec error_code filled with details on error * \return Final token as a string * * \note If the 'alg' header in not set in the token it will be set to `algo.name()` */ template typename json_traits::string_type sign(const Algo& algo, Encode encode, std::error_code& ec) const { // make a copy such that a builder can be re-used typename json_traits::object_type obj_header = header_claims; if (header_claims.count("alg") == 0) obj_header["alg"] = typename json_traits::value_type(algo.name()); const auto header = encode(json_traits::serialize(typename json_traits::value_type(obj_header))); const auto payload = encode(json_traits::serialize(typename json_traits::value_type(payload_claims))); const auto token = header + "." + payload; auto signature = algo.sign(token, ec); if (ec) return {}; return token + "." + encode(signature); } #ifndef JWT_DISABLE_BASE64 /** * Sign token and return result * * using the `jwt::base` functions provided * * \param algo Instance of an algorithm to sign the token with * \param ec error_code filled with details on error * \return Final token as a string */ template typename json_traits::string_type sign(const Algo& algo, std::error_code& ec) const { return sign( algo, [](const typename json_traits::string_type& data) { return base::trim(base::encode(data)); }, ec); } #endif }; namespace verify_ops { /** * This is the base container which holds the token that need to be verified */ template struct verify_context { verify_context(date ctime, const decoded_jwt& j, size_t l) : current_time(ctime), jwt(j), default_leeway(l) {} /// Current time, retrieved from the verifiers clock and cached for performance and consistency date current_time; /// The jwt passed to the verifier const decoded_jwt& jwt; /// The configured default leeway for this verification size_t default_leeway{0}; /// The claim key to apply this comparison on typename json_traits::string_type claim_key{}; /** * \brief Helper method to get a claim from the jwt in this context * \param in_header check JWT header or payload sections * \param ec std::error_code which will indicate if any error occure * \return basic_claim if it was present otherwise empty */ basic_claim get_claim(bool in_header, std::error_code& ec) const { if (in_header) { if (!jwt.has_header_claim(claim_key)) { ec = error::token_verification_error::missing_claim; return {}; } return jwt.get_header_claim(claim_key); } else { if (!jwt.has_payload_claim(claim_key)) { ec = error::token_verification_error::missing_claim; return {}; } return jwt.get_payload_claim(claim_key); } } /** * Helper method to get a claim of a specific type from the jwt in this context * \param in_header check JWT header or payload sections * \param t the expected type of the claim * \param ec std::error_code which will indicate if any error occure * \return basic_claim if it was present otherwise empty */ basic_claim get_claim(bool in_header, json::type t, std::error_code& ec) const { auto c = get_claim(in_header, ec); if (ec) return {}; if (c.get_type() != t) { ec = error::token_verification_error::claim_type_missmatch; return {}; } return c; } /** * \brief Helper method to get a payload claim from the jwt * \param ec std::error_code which will indicate if any error occure * \return basic_claim if it was present otherwise empty */ basic_claim get_claim(std::error_code& ec) const { return get_claim(false, ec); } /** * \brief Helper method to get a payload claim of a specific type from the jwt * \param t the expected type of the claim * \param ec std::error_code which will indicate if any error occure * \return basic_claim if it was present otherwise empty */ basic_claim get_claim(json::type t, std::error_code& ec) const { return get_claim(false, t, ec); } }; /** * This is the default operation and does case sensitive matching */ template struct equals_claim { const basic_claim expected; void operator()(const verify_context& ctx, std::error_code& ec) const { auto jc = ctx.get_claim(in_header, expected.get_type(), ec); if (ec) return; const bool matches = [&]() { switch (expected.get_type()) { case json::type::boolean: return expected.as_boolean() == jc.as_boolean(); case json::type::integer: return expected.as_integer() == jc.as_integer(); case json::type::number: return expected.as_number() == jc.as_number(); case json::type::string: return expected.as_string() == jc.as_string(); case json::type::array: case json::type::object: return json_traits::serialize(expected.to_json()) == json_traits::serialize(jc.to_json()); default: throw std::logic_error("internal error, should be unreachable"); } }(); if (!matches) { ec = error::token_verification_error::claim_value_missmatch; return; } } }; /** * Checks that the current time is before the time specified in the given * claim. This is identical to how the "exp" check works. */ template struct date_before_claim { const size_t leeway; void operator()(const verify_context& ctx, std::error_code& ec) const { auto jc = ctx.get_claim(in_header, json::type::integer, ec); if (ec) return; auto c = jc.as_date(); if (ctx.current_time > c + std::chrono::seconds(leeway)) { ec = error::token_verification_error::token_expired; } } }; /** * Checks that the current time is after the time specified in the given * claim. This is identical to how the "nbf" and "iat" check works. */ template struct date_after_claim { const size_t leeway; void operator()(const verify_context& ctx, std::error_code& ec) const { auto jc = ctx.get_claim(in_header, json::type::integer, ec); if (ec) return; auto c = jc.as_date(); if (ctx.current_time < c - std::chrono::seconds(leeway)) { ec = error::token_verification_error::token_expired; } } }; /** * Checks if the given set is a subset of the set inside the token. * If the token value is a string it is treated as a set with a single element. * The comparison is case sensitive. */ template struct is_subset_claim { const typename basic_claim::set_t expected; void operator()(const verify_context& ctx, std::error_code& ec) const { auto c = ctx.get_claim(in_header, ec); if (ec) return; if (c.get_type() == json::type::string) { if (expected.size() != 1 || *expected.begin() != c.as_string()) { ec = error::token_verification_error::audience_missmatch; return; } } else if (c.get_type() == json::type::array) { auto jc = c.as_set(); for (auto& e : expected) { if (jc.find(e) == jc.end()) { ec = error::token_verification_error::audience_missmatch; return; } } } else { ec = error::token_verification_error::claim_type_missmatch; return; } } }; /** * Checks if the claim is a string and does an case insensitive comparison. */ template struct insensitive_string_claim { const typename json_traits::string_type expected; std::locale locale; insensitive_string_claim(const typename json_traits::string_type& e, std::locale loc) : expected(to_lower_unicode(e, loc)), locale(loc) {} void operator()(const verify_context& ctx, std::error_code& ec) const { const auto c = ctx.get_claim(in_header, json::type::string, ec); if (ec) return; if (to_lower_unicode(c.as_string(), locale) != expected) { ec = error::token_verification_error::claim_value_missmatch; } } static std::string to_lower_unicode(const std::string& str, const std::locale& loc) { std::mbstate_t state = std::mbstate_t(); const char* in_next = str.data(); const char* in_end = str.data() + str.size(); std::wstring wide; wide.reserve(str.size()); while (in_next != in_end) { wchar_t wc; std::size_t result = std::mbrtowc(&wc, in_next, in_end - in_next, &state); if (result == static_cast(-1)) { throw std::runtime_error("encoding error: " + std::string(std::strerror(errno))); } else if (result == static_cast(-2)) { throw std::runtime_error("conversion error: next bytes constitute an incomplete, but so far " "valid, multibyte character."); } in_next += result; wide.push_back(wc); } auto& f = std::use_facet>(loc); f.tolower(&wide[0], &wide[0] + wide.size()); std::string out; out.reserve(wide.size()); for (wchar_t wc : wide) { char mb[MB_LEN_MAX]; std::size_t n = std::wcrtomb(mb, wc, &state); if (n != static_cast(-1)) out.append(mb, n); } return out; } }; } // namespace verify_ops /** * Verifier class used to check if a decoded token contains all claims required by your application and has a valid * signature. */ template class verifier { public: using basic_claim_t = basic_claim; /** * \brief Verification function data structure. * * This gets passed the current verifier, a reference to the decoded jwt, a reference to the key of this claim, * as well as a reference to an error_code. * The function checks if the actual value matches certain rules (e.g. equality to value x) and sets the error_code if * it does not. Once a non zero error_code is encountered the verification stops and this error_code becomes the result * returned from verify */ using verify_check_fn_t = std::function&, std::error_code& ec)>; private: struct algo_base { virtual ~algo_base() = default; virtual void verify(const std::string& data, const std::string& sig, std::error_code& ec) = 0; }; template struct algo : public algo_base { T alg; explicit algo(T a) : alg(a) {} void verify(const std::string& data, const std::string& sig, std::error_code& ec) override { alg.verify(data, sig, ec); } }; /// Required claims std::unordered_map claims; /// Leeway time for exp, nbf and iat size_t default_leeway = 0; /// Instance of clock type Clock clock; /// Supported algorithms std::unordered_map> algs; public: /** * Constructor for building a new verifier instance * \param c Clock instance */ explicit verifier(Clock c) : clock(c) { claims["exp"] = [](const verify_ops::verify_context& ctx, std::error_code& ec) { if (!ctx.jwt.has_expires_at()) return; auto exp = ctx.jwt.get_expires_at(); if (ctx.current_time > exp + std::chrono::seconds(ctx.default_leeway)) { ec = error::token_verification_error::token_expired; } }; claims["iat"] = [](const verify_ops::verify_context& ctx, std::error_code& ec) { if (!ctx.jwt.has_issued_at()) return; auto iat = ctx.jwt.get_issued_at(); if (ctx.current_time < iat - std::chrono::seconds(ctx.default_leeway)) { ec = error::token_verification_error::token_expired; } }; claims["nbf"] = [](const verify_ops::verify_context& ctx, std::error_code& ec) { if (!ctx.jwt.has_not_before()) return; auto nbf = ctx.jwt.get_not_before(); if (ctx.current_time < nbf - std::chrono::seconds(ctx.default_leeway)) { ec = error::token_verification_error::token_expired; } }; } /** * Set default leeway to use. * \param leeway Default leeway to use if not specified otherwise * \return *this to allow chaining */ verifier& leeway(size_t leeway) { default_leeway = leeway; return *this; } /** * Set leeway for expires at. * If not specified the default leeway will be used. * \param leeway Set leeway to use for expires at. * \return *this to allow chaining */ verifier& expires_at_leeway(size_t leeway) { claims["exp"] = verify_ops::date_before_claim{leeway}; return *this; } /** * Set leeway for not before. * If not specified the default leeway will be used. * \param leeway Set leeway to use for not before. * \return *this to allow chaining */ verifier& not_before_leeway(size_t leeway) { claims["nbf"] = verify_ops::date_after_claim{leeway}; return *this; } /** * Set leeway for issued at. * If not specified the default leeway will be used. * \param leeway Set leeway to use for issued at. * \return *this to allow chaining */ verifier& issued_at_leeway(size_t leeway) { claims["iat"] = verify_ops::date_after_claim{leeway}; return *this; } /** * Set an type to check for. * * According to [RFC 7519 Section 5.1](https://datatracker.ietf.org/doc/html/rfc7519#section-5.1), * This parameter is ignored by JWT implementations; any processing of this parameter is performed by the JWT application. * Check is case sensitive. * * \param type Type Header Parameter to check for. * \param locale Localization functionality to use when comparing * \return *this to allow chaining */ verifier& with_type(const typename json_traits::string_type& type, std::locale locale = std::locale{}) { return with_claim("typ", verify_ops::insensitive_string_claim{type, std::move(locale)}); } /** * Set an issuer to check for. * Check is case sensitive. * \param iss Issuer to check for. * \return *this to allow chaining */ verifier& with_issuer(const typename json_traits::string_type& iss) { return with_claim("iss", basic_claim_t(iss)); } /** * Set a subject to check for. * Check is case sensitive. * \param sub Subject to check for. * \return *this to allow chaining */ verifier& with_subject(const typename json_traits::string_type& sub) { return with_claim("sub", basic_claim_t(sub)); } /** * Set an audience to check for. * If any of the specified audiences is not present in the token the check fails. * \param aud Audience to check for. * \return *this to allow chaining */ verifier& with_audience(const typename basic_claim_t::set_t& aud) { claims["aud"] = verify_ops::is_subset_claim{aud}; return *this; } /** * Set an audience to check for. * If the specified audiences is not present in the token the check fails. * \param aud Audience to check for. * \return *this to allow chaining */ verifier& with_audience(const typename json_traits::string_type& aud) { typename basic_claim_t::set_t s; s.insert(aud); return with_audience(s); } /** * Set an id to check for. * Check is case sensitive. * \param id ID to check for. * \return *this to allow chaining */ verifier& with_id(const typename json_traits::string_type& id) { return with_claim("jti", basic_claim_t(id)); } /** * Specify a claim to check for using the specified operation. * This is helpful for implementating application specific authentication checks * such as the one seen in partial-claim-verifier.cpp * * \snippet{trimleft} partial-claim-verifier.cpp verifier check custom claim * * \param name Name of the claim to check for * \param fn Function to use for verifying the claim * \return *this to allow chaining */ verifier& with_claim(const typename json_traits::string_type& name, verify_check_fn_t fn) { claims[name] = fn; return *this; } /** * Specify a claim to check for equality (both type & value). * See the private-claims.cpp example. * * \snippet{trimleft} private-claims.cpp verify exact claim * * \param name Name of the claim to check for * \param c Claim to check for * \return *this to allow chaining */ verifier& with_claim(const typename json_traits::string_type& name, basic_claim_t c) { return with_claim(name, verify_ops::equals_claim{c}); } /** * \brief Add an algorithm available for checking. * * This is used to handle incomming tokens for predefined algorithms * which the authorization server is provided. For example a small system * where only a single RSA key-pair is used to sign tokens * * \snippet{trimleft} example/rsa-verify.cpp allow rsa algorithm * * \tparam Algorithm any algorithm such as those provided by jwt::algorithm * \param alg Algorithm to allow * \return *this to allow chaining */ template verifier& allow_algorithm(Algorithm alg) { algs[alg.name()] = std::make_shared>(alg); return *this; } /** * Verify the given token. * \param jwt Token to check * \throw token_verification_exception Verification failed */ void verify(const decoded_jwt& jwt) const { std::error_code ec; verify(jwt, ec); error::throw_if_error(ec); } /** * Verify the given token. * \param jwt Token to check * \param ec error_code filled with details on error */ void verify(const decoded_jwt& jwt, std::error_code& ec) const { ec.clear(); const typename json_traits::string_type data = jwt.get_header_base64() + "." + jwt.get_payload_base64(); const typename json_traits::string_type sig = jwt.get_signature(); const std::string algo = jwt.get_algorithm(); if (algs.count(algo) == 0) { ec = error::token_verification_error::wrong_algorithm; return; } algs.at(algo)->verify(data, sig, ec); if (ec) return; verify_ops::verify_context ctx{clock.now(), jwt, default_leeway}; for (auto& c : claims) { ctx.claim_key = c.first; c.second(ctx, ec); if (ec) return; } } }; /** * \brief JSON Web Key * * https://tools.ietf.org/html/rfc7517 * * A JSON object that represents a cryptographic key. The members of * the object represent properties of the key, including its value. */ template class jwk { using basic_claim_t = basic_claim; const details::map_of_claims jwk_claims; public: JWT_CLAIM_EXPLICIT jwk(const typename json_traits::string_type& str) : jwk_claims(details::map_of_claims::parse_claims(str)) {} JWT_CLAIM_EXPLICIT jwk(const typename json_traits::value_type& json) : jwk_claims(json_traits::as_object(json)) {} /** * Get key type claim * * This returns the general type (e.g. RSA or EC), not a specific algorithm value. * \return key type as string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_key_type() const { return get_jwk_claim("kty").as_string(); } /** * Get public key usage claim * \return usage parameter as string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_use() const { return get_jwk_claim("use").as_string(); } /** * Get key operation types claim * \return key operation types as a set of strings * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename basic_claim_t::set_t get_key_operations() const { return get_jwk_claim("key_ops").as_set(); } /** * Get algorithm claim * \return algorithm as string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_algorithm() const { return get_jwk_claim("alg").as_string(); } /** * Get key id claim * \return key id as string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_key_id() const { return get_jwk_claim("kid").as_string(); } /** * \brief Get curve claim * * https://www.rfc-editor.org/rfc/rfc7518.html#section-6.2.1.1 * https://www.iana.org/assignments/jose/jose.xhtml#table-web-key-elliptic-curve * * \return curve as string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_curve() const { return get_jwk_claim("crv").as_string(); } /** * Get x5c claim * \return x5c as an array * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a array (Should not happen in a valid token) */ typename json_traits::array_type get_x5c() const { return get_jwk_claim("x5c").as_array(); }; /** * Get X509 URL claim * \return x5u as string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_x5u() const { return get_jwk_claim("x5u").as_string(); }; /** * Get X509 thumbprint claim * \return x5t as string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_x5t() const { return get_jwk_claim("x5t").as_string(); }; /** * Get X509 SHA256 thumbprint claim * \return x5t#S256 as string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_x5t_sha256() const { return get_jwk_claim("x5t#S256").as_string(); }; /** * Get x5c claim as a string * \return x5c as an string * \throw std::runtime_error If claim was not present * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) */ typename json_traits::string_type get_x5c_key_value() const { auto x5c_array = get_jwk_claim("x5c").as_array(); if (x5c_array.size() == 0) throw error::claim_not_present_exception(); return json_traits::as_string(x5c_array.front()); }; /** * Check if a key type is present ("kty") * \return true if present, false otherwise */ bool has_key_type() const noexcept { return has_jwk_claim("kty"); } /** * Check if a public key usage indication is present ("use") * \return true if present, false otherwise */ bool has_use() const noexcept { return has_jwk_claim("use"); } /** * Check if a key operations parameter is present ("key_ops") * \return true if present, false otherwise */ bool has_key_operations() const noexcept { return has_jwk_claim("key_ops"); } /** * Check if algorithm is present ("alg") * \return true if present, false otherwise */ bool has_algorithm() const noexcept { return has_jwk_claim("alg"); } /** * Check if curve is present ("crv") * \return true if present, false otherwise */ bool has_curve() const noexcept { return has_jwk_claim("crv"); } /** * Check if key id is present ("kid") * \return true if present, false otherwise */ bool has_key_id() const noexcept { return has_jwk_claim("kid"); } /** * Check if X509 URL is present ("x5u") * \return true if present, false otherwise */ bool has_x5u() const noexcept { return has_jwk_claim("x5u"); } /** * Check if X509 Chain is present ("x5c") * \return true if present, false otherwise */ bool has_x5c() const noexcept { return has_jwk_claim("x5c"); } /** * Check if a X509 thumbprint is present ("x5t") * \return true if present, false otherwise */ bool has_x5t() const noexcept { return has_jwk_claim("x5t"); } /** * Check if a X509 SHA256 thumbprint is present ("x5t#S256") * \return true if present, false otherwise */ bool has_x5t_sha256() const noexcept { return has_jwk_claim("x5t#S256"); } /** * Check if a jwk claim is present * \return true if claim was present, false otherwise */ bool has_jwk_claim(const typename json_traits::string_type& name) const noexcept { return jwk_claims.has_claim(name); } /** * Get jwk claim by name * \return Requested claim * \throw std::runtime_error If claim was not present */ basic_claim_t get_jwk_claim(const typename json_traits::string_type& name) const { return jwk_claims.get_claim(name); } /** * Check if the jwk has any claims * \return true is any claim is present */ bool empty() const noexcept { return jwk_claims.empty(); } /** * Get all jwk claims * \return Map of claims */ typename json_traits::object_type get_claims() const { return this->jwk_claims.claims; } }; /** * \brief JWK Set * * https://tools.ietf.org/html/rfc7517 * * A JSON object that represents a set of JWKs. The JSON object MUST * have a "keys" member, which is an array of JWKs. * * This container takes a JWKs and simplifies it to a vector of JWKs */ template class jwks { public: /// JWK instance template specialization using jwks_t = jwk; /// Type specialization for the vector of JWK using jwks_vector_t = std::vector; using iterator = typename jwks_vector_t::iterator; using const_iterator = typename jwks_vector_t::const_iterator; /** * Default constructor producing an empty object without any keys */ jwks() = default; /** * Parses a string buffer to extract the JWKS. * \param str buffer containing JSON object representing a JWKS * \throw error::invalid_json_exception or underlying JSON implation error if the JSON is * invalid with regards to the JWKS specification */ JWT_CLAIM_EXPLICIT jwks(const typename json_traits::string_type& str) { typename json_traits::value_type parsed_val; if (!json_traits::parse(parsed_val, str)) throw error::invalid_json_exception(); const details::map_of_claims jwks_json = json_traits::as_object(parsed_val); if (!jwks_json.has_claim("keys")) throw error::invalid_json_exception(); auto jwk_list = jwks_json.get_claim("keys").as_array(); std::transform(jwk_list.begin(), jwk_list.end(), std::back_inserter(jwk_claims), [](const typename json_traits::value_type& val) { return jwks_t{val}; }); } iterator begin() { return jwk_claims.begin(); } iterator end() { return jwk_claims.end(); } const_iterator cbegin() const { return jwk_claims.begin(); } const_iterator cend() const { return jwk_claims.end(); } const_iterator begin() const { return jwk_claims.begin(); } const_iterator end() const { return jwk_claims.end(); } /** * Check if a jwk with the kid is present * \return true if jwk was present, false otherwise */ bool has_jwk(const typename json_traits::string_type& key_id) const noexcept { return find_by_kid(key_id) != end(); } /** * Get jwk * \return Requested jwk by key_id * \throw std::runtime_error If jwk was not present */ jwks_t get_jwk(const typename json_traits::string_type& key_id) const { const auto maybe = find_by_kid(key_id); if (maybe == end()) throw error::claim_not_present_exception(); return *maybe; } private: jwks_vector_t jwk_claims; const_iterator find_by_kid(const typename json_traits::string_type& key_id) const noexcept { return std::find_if(cbegin(), cend(), [key_id](const jwks_t& jwk) { if (!jwk.has_key_id()) { return false; } return jwk.get_key_id() == key_id; }); } }; /** * Create a verifier using the given clock * \param c Clock instance to use * \return verifier instance */ template verifier verify(Clock c) { return verifier(c); } /** * Create a builder using the given clock * \param c Clock instance to use * \return builder instance */ template builder create(Clock c) { return builder(c); } /** * Default clock class using std::chrono::system_clock as a backend. */ struct default_clock { /** * Gets the current system time * \return time_point of the host system */ date now() const { return date::clock::now(); } }; /** * Create a verifier using the default_clock. * * * * \param c Clock instance to use * \return verifier instance */ template verifier verify(default_clock c = {}) { return verifier(c); } /** * Return a builder instance to create a new token */ template builder create(default_clock c = {}) { return builder(c); } /** * \brief Decode a token. This can be used to to help access important feild like 'x5c' * for verifying tokens. See associated example rsa-verify.cpp for more details. * * \tparam json_traits JSON implementation traits * \tparam Decode is callable, 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 function that will pad and base64url decode the token * \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 typename json_traits::string_type& token, Decode decode) { return decoded_jwt(token, decode); } /** * Decode a token. This can be used to to help access important feild like 'x5c' * for verifying tokens. See associated example rsa-verify.cpp for more details. * * \tparam json_traits JSON implementation traits * \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 */ template decoded_jwt decode(const typename json_traits::string_type& token) { return decoded_jwt(token); } /** * Parse a single JSON Web Key * \tparam json_traits JSON implementation traits * \param jwk_ string buffer containing the JSON object * \return Decoded jwk */ template jwk parse_jwk(const typename json_traits::string_type& jwk_) { return jwk(jwk_); } /** * Parse a JSON Web Key Set. This can be used to to help access * important feild like 'x5c' for verifying tokens. See example * jwks-verify.cpp for more information. * * \tparam json_traits JSON implementation traits * \param jwks_ string buffer containing the JSON object * \return Parsed JSON object containing the data of the JWK SET string * \throw std::runtime_error Token is not in correct format */ template jwks parse_jwks(const typename json_traits::string_type& jwks_) { return jwks(jwks_); } } // namespace jwt template std::istream& operator>>(std::istream& is, jwt::basic_claim& c) { return c.operator>>(is); } template std::ostream& operator<<(std::ostream& os, const jwt::basic_claim& c) { return os << c.to_json(); } #ifndef JWT_DISABLE_PICOJSON #include "traits/kazuho-picojson/defaults.h" #endif #endif scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/000077500000000000000000000000001511466717700235115ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/boost-json/000077500000000000000000000000001511466717700256065ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/boost-json/defaults.h000066400000000000000000000052701511466717700275720ustar00rootroot00000000000000#ifndef JWT_CPP_BOOST_JSON_DEFAULTS_H #define JWT_CPP_BOOST_JSON_DEFAULTS_H #ifndef JWT_DISABLE_PICOJSON #define JWT_DISABLE_PICOJSON #endif #include "traits.h" namespace jwt { /** * \brief a class to store a generic [Boost.JSON](https://github.com/boostorg/json) value as claim * * This type is the specialization of the \ref basic_claim class which * uses the standard template types. */ 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 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); } /** * 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::boost_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::boost_json::string_type& token) { return jwks(token); } /** * This type is the specialization of the \ref verify_ops::verify_context class which * uses the standard template types. */ using verify_context = verify_ops::verify_context; } // namespace jwt #endif // JWT_CPP_BOOST_JSON_DEFAULTS_H scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/boost-json/traits.h000066400000000000000000000051471511466717700272740ustar00rootroot00000000000000#ifndef JWT_CPP_BOOSTJSON_TRAITS_H #define JWT_CPP_BOOSTJSON_TRAITS_H #define JWT_DISABLE_PICOJSON #include "jwt-cpp/jwt.h" #include // if not boost JSON standalone then error... namespace jwt { /** * \brief Namespace containing all the json_trait implementations for a jwt::basic_claim. */ namespace traits { namespace json = boost::json; /// basic_claim's JSON trait implementation for Boost.JSON struct boost_json { using value_type = json::value; using object_type = json::object; using array_type = json::array; using string_type = std::string; using number_type = double; using integer_type = std::int64_t; using boolean_type = bool; static jwt::json::type get_type(const value_type& val) { using jwt::json::type; if (val.kind() == json::kind::bool_) return type::boolean; if (val.kind() == json::kind::int64) return type::integer; if (val.kind() == json::kind::uint64) // boost internally tracks two types of integers return type::integer; if (val.kind() == json::kind::double_) return type::number; if (val.kind() == json::kind::string) return type::string; if (val.kind() == json::kind::array) return type::array; if (val.kind() == json::kind::object) return type::object; throw std::logic_error("invalid type"); } static object_type as_object(const value_type& val) { if (val.kind() != json::kind::object) throw std::bad_cast(); return val.get_object(); } static array_type as_array(const value_type& val) { if (val.kind() != json::kind::array) throw std::bad_cast(); return val.get_array(); } static string_type as_string(const value_type& val) { if (val.kind() != json::kind::string) throw std::bad_cast(); return string_type{val.get_string()}; } static integer_type as_integer(const value_type& val) { switch (val.kind()) { case json::kind::int64: return val.get_int64(); case json::kind::uint64: return static_cast(val.get_uint64()); default: throw std::bad_cast(); } } static boolean_type as_boolean(const value_type& val) { if (val.kind() != json::kind::bool_) throw std::bad_cast(); return val.get_bool(); } static number_type as_number(const value_type& val) { if (val.kind() != json::kind::double_) throw std::bad_cast(); return val.get_double(); } static bool parse(value_type& val, string_type str) { val = json::parse(str); return true; } static std::string serialize(const value_type& val) { return json::serialize(val); } }; } // namespace traits } // namespace jwt #endif // JWT_CPP_BOOSTJSON_TRAITS_H scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/danielaparker-jsoncons/000077500000000000000000000000001511466717700301455ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/danielaparker-jsoncons/defaults.h000066400000000000000000000056431511466717700321350ustar00rootroot00000000000000#ifndef JWT_CPP_DANIELAPARKER_JSONCONS_DEFAULTS_H #define JWT_CPP_DANIELAPARKER_JSONCONS_DEFAULTS_H #ifndef JWT_DISABLE_PICOJSON #define JWT_DISABLE_PICOJSON #endif #include "traits.h" namespace jwt { /** * \brief a class to store a generic [jsoncons](https://github.com/danielaparker/jsoncons) value as claim * * This type is the specialization of the \ref basic_claim class which * uses the standard template types. */ 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 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); } /** * 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::danielaparker_jsoncons::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::danielaparker_jsoncons::string_type& token) { return jwks(token); } /** * This type is the specialization of the \ref verify_ops::verify_context class which * uses the standard template types. */ using verify_context = verify_ops::verify_context; } // namespace jwt #endif // JWT_CPP_DANIELAPARKER_JSONCONS_DEFAULTS_H scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/danielaparker-jsoncons/traits.h000066400000000000000000000115021511466717700316230ustar00rootroot00000000000000#define JWT_DISABLE_PICOJSON #define JSONCONS_NO_DEPRECATED #include "jwt-cpp/jwt.h" #include "jsoncons/json.hpp" #include namespace jwt { /** * \brief Namespace containing all the json_trait implementations for a jwt::basic_claim. */ namespace traits { /// basic_claim's JSON trait implementation for jsoncons. struct danielaparker_jsoncons { // Needs at least https://github.com/danielaparker/jsoncons/commit/28c56b90ec7337f98a5b8942574590111a5e5831 static_assert(jsoncons::version().minor >= 167, "A higher version of jsoncons is required!"); using json = jsoncons::json; using value_type = json; struct object_type : json::object { // Add missing C++11 member types // https://github.com/danielaparker/jsoncons/commit/1b1ceeb572f9a2db6d37cff47ac78a4f14e072e2#commitcomment-45391411 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 json::object& o) : json::object(o) {} object_type(object_type&&) = default; explicit object_type(json::object&& o) : json::object(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 mapped_type& operator[](const key_type& key) { // https://github.com/microsoft/STL/blob/2914b4301c59dc7ffc09d16ac6f7979fde2b7f2c/stl/inc/map#L325 return try_emplace(key).first->value(); } // Add missing C++11 element access const mapped_type& at(const key_type& key) const { auto target = find(key); if (target != end()) return target->value(); throw std::out_of_range("invalid key"); } // Add missing C++11 lookup method size_type count(const key_type& key) const { struct compare { bool operator()(const value_type& val, const key_type& key) const { return val.key() < key; } bool operator()(const key_type& key, const value_type& val) const { return key < val.key(); } }; // https://en.cppreference.com/w/cpp/algorithm/binary_search#Complexity if (std::binary_search(this->begin(), this->end(), key, compare{})) return 1; return 0; } }; class array_type : public json::array { public: using json::array::array; explicit array_type(const json::array& a) : json::array(a) {} explicit array_type(json::array&& a) : json::array(a) {} value_type const& front() const { return this->operator[](0U); } }; using string_type = std::string; // current limitation of traits implementation using number_type = double; using integer_type = int64_t; using boolean_type = bool; static jwt::json::type get_type(const json& val) { using jwt::json::type; if (val.type() == jsoncons::json_type::bool_value) return type::boolean; if (val.type() == jsoncons::json_type::int64_value) return type::integer; if (val.type() == jsoncons::json_type::uint64_value) return type::integer; if (val.type() == jsoncons::json_type::half_value) return type::number; if (val.type() == jsoncons::json_type::double_value) return type::number; if (val.type() == jsoncons::json_type::string_value) return type::string; if (val.type() == jsoncons::json_type::array_value) return type::array; if (val.type() == jsoncons::json_type::object_value) return type::object; throw std::logic_error("invalid type"); } static object_type as_object(const json& val) { if (val.type() != jsoncons::json_type::object_value) throw std::bad_cast(); return object_type(val.object_value()); } static array_type as_array(const json& val) { if (val.type() != jsoncons::json_type::array_value) throw std::bad_cast(); return array_type(val.array_value()); } static string_type as_string(const json& val) { if (val.type() != jsoncons::json_type::string_value) throw std::bad_cast(); return val.as_string(); } static number_type as_number(const json& val) { if (get_type(val) != jwt::json::type::number) throw std::bad_cast(); return val.as_double(); } static integer_type as_integer(const json& val) { if (get_type(val) != jwt::json::type::integer) throw std::bad_cast(); return val.as(); } static boolean_type as_boolean(const json& val) { if (val.type() != jsoncons::json_type::bool_value) throw std::bad_cast(); return val.as_bool(); } static bool parse(json& val, const std::string& str) { val = json::parse(str); return true; } static std::string serialize(const json& val) { std::ostringstream os; os << jsoncons::print(val); return os.str(); } }; } // namespace traits } // namespace jwt scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/defaults.h.mustache000066400000000000000000000055301511466717700273040ustar00rootroot00000000000000#ifndef JWT_CPP_{{traits_name_upper}}_DEFAULTS_H #define JWT_CPP_{{traits_name_upper}}_DEFAULTS_H {{#disable_default_traits}} #ifndef JWT_DISABLE_PICOJSON #define JWT_DISABLE_PICOJSON #endif {{/disable_default_traits}} #include "traits.h" namespace jwt { /** * \brief a class to store a generic [{{library_name}}]({{{library_url}}}) value as claim * * This type is the specialization of the \ref basic_claim class which * uses the standard template types. */ 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 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); } /** * 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::{{traits_name}}::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::{{traits_name}}::string_type& token) { return jwks(token); } /** * This type is the specialization of the \ref verify_ops::verify_context class which * uses the standard template types. */ using verify_context = verify_ops::verify_context; } // namespace jwt #endif // JWT_CPP_{{traits_name_upper}}_DEFAULTS_H scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/kazuho-picojson/000077500000000000000000000000001511466717700266345ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/kazuho-picojson/defaults.h000066400000000000000000000053251511466717700306210ustar00rootroot00000000000000#ifndef JWT_CPP_KAZUHO_PICOJSON_DEFAULTS_H #define JWT_CPP_KAZUHO_PICOJSON_DEFAULTS_H #include "traits.h" namespace jwt { /** * \brief a class to store a generic [picojson](https://github.com/kazuho/picojson) value as claim * * This type is the specialization of the \ref basic_claim class which * uses the standard template types. */ 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 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); } /** * 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::kazuho_picojson::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::kazuho_picojson::string_type& token) { return jwks(token); } /** * This type is the specialization of the \ref verify_ops::verify_context class which * uses the standard template types. */ using verify_context = verify_ops::verify_context; } // namespace jwt #endif // JWT_CPP_KAZUHO_PICOJSON_DEFAULTS_H scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/kazuho-picojson/traits.h000066400000000000000000000044641511466717700303230ustar00rootroot00000000000000#ifndef JWT_CPP_PICOJSON_TRAITS_H #define JWT_CPP_PICOJSON_TRAITS_H #ifndef PICOJSON_USE_INT64 #define PICOJSON_USE_INT64 #endif #include "picojson/picojson.h" #ifndef JWT_DISABLE_PICOJSON #define JWT_DISABLE_PICOJSON #endif #include "jwt-cpp/jwt.h" namespace jwt { /** * \brief Namespace containing all the json_trait implementations for a jwt::basic_claim. */ namespace traits { /// basic_claim's JSON trait implementation for picojson struct kazuho_picojson { using value_type = picojson::value; using object_type = picojson::object; using array_type = picojson::array; using string_type = std::string; using number_type = double; using integer_type = int64_t; using boolean_type = bool; static json::type get_type(const picojson::value& val) { using json::type; if (val.is()) return type::boolean; if (val.is()) return type::integer; if (val.is()) return type::number; if (val.is()) return type::string; if (val.is()) return type::array; if (val.is()) return type::object; throw std::logic_error("invalid type"); } static picojson::object as_object(const picojson::value& val) { if (!val.is()) throw std::bad_cast(); return val.get(); } static std::string as_string(const picojson::value& val) { if (!val.is()) throw std::bad_cast(); return val.get(); } static picojson::array as_array(const picojson::value& val) { if (!val.is()) throw std::bad_cast(); return val.get(); } static int64_t as_integer(const picojson::value& val) { if (!val.is()) throw std::bad_cast(); return val.get(); } static bool as_boolean(const picojson::value& val) { if (!val.is()) throw std::bad_cast(); return val.get(); } static double as_number(const picojson::value& val) { if (!val.is()) throw std::bad_cast(); return val.get(); } static bool parse(picojson::value& val, const std::string& str) { return picojson::parse(val, str).empty(); } static std::string serialize(const picojson::value& val) { return val.serialize(); } }; } // namespace traits } // namespace jwt #endif scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/nlohmann-json/000077500000000000000000000000001511466717700262725ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/nlohmann-json/defaults.h000066400000000000000000000053721511466717700302610ustar00rootroot00000000000000#ifndef JWT_CPP_NLOHMANN_JSON_DEFAULTS_H #define JWT_CPP_NLOHMANN_JSON_DEFAULTS_H #ifndef JWT_DISABLE_PICOJSON #define JWT_DISABLE_PICOJSON #endif #include "traits.h" namespace jwt { /** * \brief a class to store a generic [JSON for Modern C++](https://github.com/nlohmann/json) value as claim * * This type is the specialization of the \ref basic_claim class which * uses the standard template types. */ 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 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); } /** * 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::nlohmann_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::nlohmann_json::string_type& token) { return jwks(token); } /** * This type is the specialization of the \ref verify_ops::verify_context class which * uses the standard template types. */ using verify_context = verify_ops::verify_context; } // namespace jwt #endif // JWT_CPP_NLOHMANN_JSON_DEFAULTS_H scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/nlohmann-json/traits.h000066400000000000000000000051451511466717700277560ustar00rootroot00000000000000#ifndef JWT_CPP_NLOHMANN_JSON_TRAITS_H #define JWT_CPP_NLOHMANN_JSON_TRAITS_H #include "jwt-cpp/jwt.h" #include "nlohmann/json.hpp" namespace jwt { /** * \brief Namespace containing all the json_trait implementations for a jwt::basic_claim. */ namespace traits { /// basic_claim's JSON trait implementation for Modern C++ JSON struct nlohmann_json { using json = nlohmann::json; using value_type = json; using object_type = json::object_t; using array_type = json::array_t; using string_type = std::string; // current limitation of traits implementation using number_type = json::number_float_t; using integer_type = json::number_integer_t; using boolean_type = json::boolean_t; static jwt::json::type get_type(const json& val) { using jwt::json::type; if (val.type() == json::value_t::boolean) return type::boolean; // nlohmann internally tracks two types of integers if (val.type() == json::value_t::number_integer) return type::integer; if (val.type() == json::value_t::number_unsigned) return type::integer; if (val.type() == json::value_t::number_float) return type::number; if (val.type() == json::value_t::string) return type::string; if (val.type() == json::value_t::array) return type::array; if (val.type() == json::value_t::object) return type::object; throw std::logic_error("invalid type"); } static json::object_t as_object(const json& val) { if (val.type() != json::value_t::object) throw std::bad_cast(); return val.get(); } static std::string as_string(const json& val) { if (val.type() != json::value_t::string) throw std::bad_cast(); return val.get(); } static json::array_t as_array(const json& val) { if (val.type() != json::value_t::array) throw std::bad_cast(); return val.get(); } static int64_t as_integer(const json& val) { switch (val.type()) { case json::value_t::number_integer: case json::value_t::number_unsigned: return val.get(); default: throw std::bad_cast(); } } static bool as_boolean(const json& val) { if (val.type() != json::value_t::boolean) throw std::bad_cast(); return val.get(); } static double as_number(const json& val) { if (val.type() != json::value_t::number_float) throw std::bad_cast(); return val.get(); } static bool parse(json& val, std::string str) { val = json::parse(str.begin(), str.end()); return true; } static std::string serialize(const json& val) { return val.dump(); } }; } // namespace traits } // namespace jwt #endif scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/open-source-parsers-jsoncpp/000077500000000000000000000000001511466717700310775ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h000066400000000000000000000060101511466717700330540ustar00rootroot00000000000000#ifndef JWT_CPP_OPEN_SOURCE_PARSERS_JSONCPP_DEFAULTS_H #define JWT_CPP_OPEN_SOURCE_PARSERS_JSONCPP_DEFAULTS_H #ifndef JWT_DISABLE_PICOJSON #define JWT_DISABLE_PICOJSON #endif #include "traits.h" namespace jwt { /** * \brief a class to store a generic [jsoncpp](https://github.com/open-source-parsers/jsoncpp) value as claim * * This type is the specialization of the \ref basic_claim class which * uses the standard template types. */ 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 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); } /** * 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::open_source_parsers_jsoncpp::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::open_source_parsers_jsoncpp::string_type& token) { return jwks(token); } /** * This type is the specialization of the \ref verify_ops::verify_context class which * uses the standard template types. */ using verify_context = verify_ops::verify_context; } // namespace jwt #endif // JWT_CPP_OPEN_SOURCE_PARSERS_JSONCPP_DEFAULTS_H scitokens-cpp-1.2.0/vendor/jwt-cpp/include/jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h000066400000000000000000000105721511466717700325630ustar00rootroot00000000000000#ifndef JWT_CPP_JSONCPP_TRAITS_H #define JWT_CPP_JSONCPP_TRAITS_H #include "jwt-cpp/jwt.h" #include "json/json.h" namespace jwt { /** * \brief Namespace containing all the json_trait implementations for a jwt::basic_claim. */ namespace traits { /// basic_claim's JSON trait implementation for jsoncpp struct open_source_parsers_jsoncpp { using value_type = Json::Value; using string_type = std::string; class array_type : public Json::Value { public: using value_type = Json::Value; array_type() = default; array_type(const array_type&) = default; explicit array_type(const Json::Value& o) : Json::Value(o) {} array_type(array_type&&) = default; explicit array_type(Json::Value&& o) : Json::Value(o) {} template array_type(Iterator begin, Iterator end) { for (Iterator it = begin; it != end; ++it) { Json::Value value; value = *it; this->append(value); } } ~array_type() = default; array_type& operator=(const array_type& o) = default; array_type& operator=(array_type&& o) noexcept = default; value_type const& front() const { return this->operator[](0U); } }; using number_type = double; using integer_type = Json::Value::Int; using boolean_type = bool; class object_type : public Json::Value { public: using key_type = std::string; using mapped_type = Json::Value; using size_type = size_t; object_type() = default; object_type(const object_type&) = default; explicit object_type(const Json::Value& o) : Json::Value(o) {} object_type(object_type&&) = default; explicit object_type(Json::Value&& o) : Json::Value(o) {} ~object_type() = default; object_type& operator=(const object_type& o) = default; object_type& operator=(object_type&& o) noexcept = default; // Add missing C++11 element access const mapped_type& at(const key_type& key) const { Json::Value const* found = find(key.data(), key.data() + key.length()); if (!found) throw std::out_of_range("invalid key"); return *found; } size_type count(const key_type& key) const { return this->isMember(key) ? 1 : 0; } }; // Translation between the implementation notion of type, to the jwt::json::type equivilant static jwt::json::type get_type(const value_type& val) { using jwt::json::type; if (val.isArray()) return type::array; else if (val.isString()) return type::string; // Order is important https://github.com/Thalhammer/jwt-cpp/pull/320#issuecomment-1865322511 else if (val.isInt()) return type::integer; else if (val.isNumeric()) return type::number; else if (val.isBool()) return type::boolean; else if (val.isObject()) return type::object; throw std::logic_error("invalid type"); } static integer_type as_integer(const value_type& val) { switch (val.type()) { case Json::intValue: return val.asInt64(); case Json::uintValue: return static_cast(val.asUInt64()); default: throw std::bad_cast(); } } static boolean_type as_boolean(const value_type& val) { if (!val.isBool()) throw std::bad_cast(); return val.asBool(); } static number_type as_number(const value_type& val) { if (!val.isNumeric()) throw std::bad_cast(); return val.asDouble(); } static string_type as_string(const value_type& val) { if (!val.isString()) throw std::bad_cast(); return val.asString(); } static object_type as_object(const value_type& val) { if (!val.isObject()) throw std::bad_cast(); return object_type(val); } static array_type as_array(const value_type& val) { if (!val.isArray()) throw std::bad_cast(); return array_type(val); } static bool parse(value_type& val, string_type str) { Json::CharReaderBuilder builder; const std::unique_ptr reader(builder.newCharReader()); return reader->parse(reinterpret_cast(str.c_str()), reinterpret_cast(str.c_str() + str.size()), &val, nullptr); } static string_type serialize(const value_type& val) { Json::StreamWriterBuilder builder; builder["commentStyle"] = "None"; builder["indentation"] = ""; std::unique_ptr writer(builder.newStreamWriter()); return Json::writeString(builder, val); } }; } // namespace traits } // namespace jwt #endif scitokens-cpp-1.2.0/vendor/jwt-cpp/include/picojson/000077500000000000000000000000001511466717700224435ustar00rootroot00000000000000scitokens-cpp-1.2.0/vendor/jwt-cpp/include/picojson/picojson.h000066400000000000000000001023531511466717700244440ustar00rootroot00000000000000/* * Copyright 2009-2010 Cybozu Labs, Inc. * Copyright 2011-2014 Kazuho Oku * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef picojson_h #define picojson_h #include #include #include #include #include #include #include #include #include #include #include #include #include // for isnan/isinf #if __cplusplus >= 201103L #include #else extern "C" { #ifdef _MSC_VER #include #elif defined(__INTEL_COMPILER) #include #else #include #endif } #endif #ifndef PICOJSON_USE_RVALUE_REFERENCE #if (defined(__cpp_rvalue_references) && __cpp_rvalue_references >= 200610) || (defined(_MSC_VER) && _MSC_VER >= 1600) #define PICOJSON_USE_RVALUE_REFERENCE 1 #else #define PICOJSON_USE_RVALUE_REFERENCE 0 #endif #endif // PICOJSON_USE_RVALUE_REFERENCE #ifndef PICOJSON_NOEXCEPT #if PICOJSON_USE_RVALUE_REFERENCE #define PICOJSON_NOEXCEPT noexcept #else #define PICOJSON_NOEXCEPT throw() #endif #endif // experimental support for int64_t (see README.mkdn for detail) #ifdef PICOJSON_USE_INT64 #define __STDC_FORMAT_MACROS #include #if __cplusplus >= 201103L #include #else extern "C" { #include } #endif #endif // to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0 #ifndef PICOJSON_USE_LOCALE #define PICOJSON_USE_LOCALE 1 #endif #if PICOJSON_USE_LOCALE extern "C" { #include } #endif #ifndef PICOJSON_ASSERT #define PICOJSON_ASSERT(e) \ do { \ if (!(e)) \ throw std::runtime_error(#e); \ } while (0) #endif #ifdef _MSC_VER #define SNPRINTF _snprintf_s #pragma warning(push) #pragma warning(disable : 4244) // conversion from int to char #pragma warning(disable : 4127) // conditional expression is constant #pragma warning(disable : 4702) // unreachable code #pragma warning(disable : 4706) // assignment within conditional expression #else #define SNPRINTF snprintf #endif namespace picojson { enum { null_type, boolean_type, number_type, string_type, array_type, object_type #ifdef PICOJSON_USE_INT64 , int64_type #endif }; enum { INDENT_WIDTH = 2, DEFAULT_MAX_DEPTHS = 100 }; struct null {}; class value { public: typedef std::vector array; typedef std::map object; union _storage { bool boolean_; double number_; #ifdef PICOJSON_USE_INT64 int64_t int64_; #endif std::string *string_; array *array_; object *object_; }; protected: int type_; _storage u_; public: value(); value(int type, bool); explicit value(bool b); #ifdef PICOJSON_USE_INT64 explicit value(int64_t i); #endif explicit value(double n); explicit value(const std::string &s); explicit value(const array &a); explicit value(const object &o); #if PICOJSON_USE_RVALUE_REFERENCE explicit value(std::string &&s); explicit value(array &&a); explicit value(object &&o); #endif explicit value(const char *s); value(const char *s, size_t len); ~value(); value(const value &x); value &operator=(const value &x); #if PICOJSON_USE_RVALUE_REFERENCE value(value &&x) PICOJSON_NOEXCEPT; value &operator=(value &&x) PICOJSON_NOEXCEPT; #endif void swap(value &x) PICOJSON_NOEXCEPT; template bool is() const; template const T &get() const; template T &get(); template void set(const T &); #if PICOJSON_USE_RVALUE_REFERENCE template void set(T &&); #endif bool evaluate_as_boolean() const; const value &get(const size_t idx) const; const value &get(const std::string &key) const; value &get(const size_t idx); value &get(const std::string &key); bool contains(const size_t idx) const; bool contains(const std::string &key) const; std::string to_str() const; template void serialize(Iter os, bool prettify = false) const; std::string serialize(bool prettify = false) const; private: template value(const T *); // intentionally defined to block implicit conversion of pointer to bool template static void _indent(Iter os, int indent); template void _serialize(Iter os, int indent) const; std::string _serialize(int indent) const; void clear(); }; typedef value::array array; typedef value::object object; inline value::value() : type_(null_type), u_() { } inline value::value(int type, bool) : type_(type), u_() { switch (type) { #define INIT(p, v) \ case p##type: \ u_.p = v; \ break INIT(boolean_, false); INIT(number_, 0.0); #ifdef PICOJSON_USE_INT64 INIT(int64_, 0); #endif INIT(string_, new std::string()); INIT(array_, new array()); INIT(object_, new object()); #undef INIT default: break; } } inline value::value(bool b) : type_(boolean_type), u_() { u_.boolean_ = b; } #ifdef PICOJSON_USE_INT64 inline value::value(int64_t i) : type_(int64_type), u_() { u_.int64_ = i; } #endif inline value::value(double n) : type_(number_type), u_() { if ( #ifdef _MSC_VER !_finite(n) #elif __cplusplus >= 201103L std::isnan(n) || std::isinf(n) #else isnan(n) || isinf(n) #endif ) { throw std::overflow_error(""); } u_.number_ = n; } inline value::value(const std::string &s) : type_(string_type), u_() { u_.string_ = new std::string(s); } inline value::value(const array &a) : type_(array_type), u_() { u_.array_ = new array(a); } inline value::value(const object &o) : type_(object_type), u_() { u_.object_ = new object(o); } #if PICOJSON_USE_RVALUE_REFERENCE inline value::value(std::string &&s) : type_(string_type), u_() { u_.string_ = new std::string(std::move(s)); } inline value::value(array &&a) : type_(array_type), u_() { u_.array_ = new array(std::move(a)); } inline value::value(object &&o) : type_(object_type), u_() { u_.object_ = new object(std::move(o)); } #endif inline value::value(const char *s) : type_(string_type), u_() { u_.string_ = new std::string(s); } inline value::value(const char *s, size_t len) : type_(string_type), u_() { u_.string_ = new std::string(s, len); } inline void value::clear() { switch (type_) { #define DEINIT(p) \ case p##type: \ delete u_.p; \ break DEINIT(string_); DEINIT(array_); DEINIT(object_); #undef DEINIT default: break; } } inline value::~value() { clear(); } inline value::value(const value &x) : type_(x.type_), u_() { switch (type_) { #define INIT(p, v) \ case p##type: \ u_.p = v; \ break INIT(string_, new std::string(*x.u_.string_)); INIT(array_, new array(*x.u_.array_)); INIT(object_, new object(*x.u_.object_)); #undef INIT default: u_ = x.u_; break; } } inline value &value::operator=(const value &x) { if (this != &x) { value t(x); swap(t); } return *this; } #if PICOJSON_USE_RVALUE_REFERENCE inline value::value(value &&x) PICOJSON_NOEXCEPT : type_(null_type), u_() { swap(x); } inline value &value::operator=(value &&x) PICOJSON_NOEXCEPT { swap(x); return *this; } #endif inline void value::swap(value &x) PICOJSON_NOEXCEPT { std::swap(type_, x.type_); std::swap(u_, x.u_); } #define IS(ctype, jtype) \ template <> inline bool value::is() const { \ return type_ == jtype##_type; \ } IS(null, null) IS(bool, boolean) #ifdef PICOJSON_USE_INT64 IS(int64_t, int64) #endif IS(std::string, string) IS(array, array) IS(object, object) #undef IS template <> inline bool value::is() const { return type_ == number_type #ifdef PICOJSON_USE_INT64 || type_ == int64_type #endif ; } #define GET(ctype, var) \ template <> inline const ctype &value::get() const { \ PICOJSON_ASSERT("type mismatch! call is() before get()" && is()); \ return var; \ } \ template <> inline ctype &value::get() { \ PICOJSON_ASSERT("type mismatch! call is() before get()" && is()); \ return var; \ } GET(bool, u_.boolean_) GET(std::string, *u_.string_) GET(array, *u_.array_) GET(object, *u_.object_) #ifdef PICOJSON_USE_INT64 GET(double, (type_ == int64_type && (const_cast(this)->type_ = number_type, (const_cast(this)->u_.number_ = u_.int64_)), u_.number_)) GET(int64_t, u_.int64_) #else GET(double, u_.number_) #endif #undef GET #define SET(ctype, jtype, setter) \ template <> inline void value::set(const ctype &_val) { \ clear(); \ type_ = jtype##_type; \ setter \ } SET(bool, boolean, u_.boolean_ = _val;) SET(std::string, string, u_.string_ = new std::string(_val);) SET(array, array, u_.array_ = new array(_val);) SET(object, object, u_.object_ = new object(_val);) SET(double, number, u_.number_ = _val;) #ifdef PICOJSON_USE_INT64 SET(int64_t, int64, u_.int64_ = _val;) #endif #undef SET #if PICOJSON_USE_RVALUE_REFERENCE #define MOVESET(ctype, jtype, setter) \ template <> inline void value::set(ctype && _val) { \ clear(); \ type_ = jtype##_type; \ setter \ } MOVESET(std::string, string, u_.string_ = new std::string(std::move(_val));) MOVESET(array, array, u_.array_ = new array(std::move(_val));) MOVESET(object, object, u_.object_ = new object(std::move(_val));) #undef MOVESET #endif inline bool value::evaluate_as_boolean() const { switch (type_) { case null_type: return false; case boolean_type: return u_.boolean_; case number_type: return u_.number_ != 0; #ifdef PICOJSON_USE_INT64 case int64_type: return u_.int64_ != 0; #endif case string_type: return !u_.string_->empty(); default: return true; } } inline const value &value::get(const size_t idx) const { static value s_null; PICOJSON_ASSERT(is()); return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; } inline value &value::get(const size_t idx) { static value s_null; PICOJSON_ASSERT(is()); return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; } inline const value &value::get(const std::string &key) const { static value s_null; PICOJSON_ASSERT(is()); object::const_iterator i = u_.object_->find(key); return i != u_.object_->end() ? i->second : s_null; } inline value &value::get(const std::string &key) { static value s_null; PICOJSON_ASSERT(is()); object::iterator i = u_.object_->find(key); return i != u_.object_->end() ? i->second : s_null; } inline bool value::contains(const size_t idx) const { PICOJSON_ASSERT(is()); return idx < u_.array_->size(); } inline bool value::contains(const std::string &key) const { PICOJSON_ASSERT(is()); object::const_iterator i = u_.object_->find(key); return i != u_.object_->end(); } inline std::string value::to_str() const { switch (type_) { case null_type: return "null"; case boolean_type: return u_.boolean_ ? "true" : "false"; #ifdef PICOJSON_USE_INT64 case int64_type: { char buf[sizeof("-9223372036854775808")]; SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_); return buf; } #endif case number_type: { char buf[256]; double tmp; SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_); #if PICOJSON_USE_LOCALE char *decimal_point = localeconv()->decimal_point; if (strcmp(decimal_point, ".") != 0) { size_t decimal_point_len = strlen(decimal_point); for (char *p = buf; *p != '\0'; ++p) { if (strncmp(p, decimal_point, decimal_point_len) == 0) { return std::string(buf, p) + "." + (p + decimal_point_len); } } } #endif return buf; } case string_type: return *u_.string_; case array_type: return "array"; case object_type: return "object"; default: PICOJSON_ASSERT(0); #ifdef _MSC_VER __assume(0); #endif } return std::string(); } template void copy(const std::string &s, Iter oi) { std::copy(s.begin(), s.end(), oi); } template struct serialize_str_char { Iter oi; void operator()(char c) { switch (c) { #define MAP(val, sym) \ case val: \ copy(sym, oi); \ break MAP('"', "\\\""); MAP('\\', "\\\\"); MAP('/', "\\/"); MAP('\b', "\\b"); MAP('\f', "\\f"); MAP('\n', "\\n"); MAP('\r', "\\r"); MAP('\t', "\\t"); #undef MAP default: if (static_cast(c) < 0x20 || c == 0x7f) { char buf[7]; SNPRINTF(buf, sizeof(buf), "\\u%04x", c & 0xff); copy(buf, buf + 6, oi); } else { *oi++ = c; } break; } } }; template void serialize_str(const std::string &s, Iter oi) { *oi++ = '"'; serialize_str_char process_char = {oi}; std::for_each(s.begin(), s.end(), process_char); *oi++ = '"'; } template void value::serialize(Iter oi, bool prettify) const { return _serialize(oi, prettify ? 0 : -1); } inline std::string value::serialize(bool prettify) const { return _serialize(prettify ? 0 : -1); } template void value::_indent(Iter oi, int indent) { *oi++ = '\n'; for (int i = 0; i < indent * INDENT_WIDTH; ++i) { *oi++ = ' '; } } template void value::_serialize(Iter oi, int indent) const { switch (type_) { case string_type: serialize_str(*u_.string_, oi); break; case array_type: { *oi++ = '['; if (indent != -1) { ++indent; } for (array::const_iterator i = u_.array_->begin(); i != u_.array_->end(); ++i) { if (i != u_.array_->begin()) { *oi++ = ','; } if (indent != -1) { _indent(oi, indent); } i->_serialize(oi, indent); } if (indent != -1) { --indent; if (!u_.array_->empty()) { _indent(oi, indent); } } *oi++ = ']'; break; } case object_type: { *oi++ = '{'; if (indent != -1) { ++indent; } for (object::const_iterator i = u_.object_->begin(); i != u_.object_->end(); ++i) { if (i != u_.object_->begin()) { *oi++ = ','; } if (indent != -1) { _indent(oi, indent); } serialize_str(i->first, oi); *oi++ = ':'; if (indent != -1) { *oi++ = ' '; } i->second._serialize(oi, indent); } if (indent != -1) { --indent; if (!u_.object_->empty()) { _indent(oi, indent); } } *oi++ = '}'; break; } default: copy(to_str(), oi); break; } if (indent == 0) { *oi++ = '\n'; } } inline std::string value::_serialize(int indent) const { std::string s; _serialize(std::back_inserter(s), indent); return s; } template class input { protected: Iter cur_, end_; bool consumed_; int line_; public: input(const Iter &first, const Iter &last) : cur_(first), end_(last), consumed_(false), line_(1) { } int getc() { if (consumed_) { if (*cur_ == '\n') { ++line_; } ++cur_; } if (cur_ == end_) { consumed_ = false; return -1; } consumed_ = true; return *cur_ & 0xff; } void ungetc() { consumed_ = false; } Iter cur() const { if (consumed_) { input *self = const_cast *>(this); self->consumed_ = false; ++self->cur_; } return cur_; } int line() const { return line_; } void skip_ws() { while (1) { int ch = getc(); if (!(ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) { ungetc(); break; } } } bool expect(const int expected) { skip_ws(); if (getc() != expected) { ungetc(); return false; } return true; } bool match(const std::string &pattern) { for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) { if (getc() != *pi) { ungetc(); return false; } } return true; } }; template inline int _parse_quadhex(input &in) { int uni_ch = 0, hex; for (int i = 0; i < 4; i++) { if ((hex = in.getc()) == -1) { return -1; } if ('0' <= hex && hex <= '9') { hex -= '0'; } else if ('A' <= hex && hex <= 'F') { hex -= 'A' - 0xa; } else if ('a' <= hex && hex <= 'f') { hex -= 'a' - 0xa; } else { in.ungetc(); return -1; } uni_ch = uni_ch * 16 + hex; } return uni_ch; } template inline bool _parse_codepoint(String &out, input &in) { int uni_ch; if ((uni_ch = _parse_quadhex(in)) == -1) { return false; } if (0xd800 <= uni_ch && uni_ch <= 0xdfff) { if (0xdc00 <= uni_ch) { // a second 16-bit of a surrogate pair appeared return false; } // first 16-bit of surrogate pair, get the next one if (in.getc() != '\\' || in.getc() != 'u') { in.ungetc(); return false; } int second = _parse_quadhex(in); if (!(0xdc00 <= second && second <= 0xdfff)) { return false; } uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff); uni_ch += 0x10000; } if (uni_ch < 0x80) { out.push_back(static_cast(uni_ch)); } else { if (uni_ch < 0x800) { out.push_back(static_cast(0xc0 | (uni_ch >> 6))); } else { if (uni_ch < 0x10000) { out.push_back(static_cast(0xe0 | (uni_ch >> 12))); } else { out.push_back(static_cast(0xf0 | (uni_ch >> 18))); out.push_back(static_cast(0x80 | ((uni_ch >> 12) & 0x3f))); } out.push_back(static_cast(0x80 | ((uni_ch >> 6) & 0x3f))); } out.push_back(static_cast(0x80 | (uni_ch & 0x3f))); } return true; } template inline bool _parse_string(String &out, input &in) { while (1) { int ch = in.getc(); if (ch < ' ') { in.ungetc(); return false; } else if (ch == '"') { return true; } else if (ch == '\\') { if ((ch = in.getc()) == -1) { return false; } switch (ch) { #define MAP(sym, val) \ case sym: \ out.push_back(val); \ break MAP('"', '\"'); MAP('\\', '\\'); MAP('/', '/'); MAP('b', '\b'); MAP('f', '\f'); MAP('n', '\n'); MAP('r', '\r'); MAP('t', '\t'); #undef MAP case 'u': if (!_parse_codepoint(out, in)) { return false; } break; default: return false; } } else { out.push_back(static_cast(ch)); } } return false; } template inline bool _parse_array(Context &ctx, input &in) { if (!ctx.parse_array_start()) { return false; } size_t idx = 0; if (in.expect(']')) { return ctx.parse_array_stop(idx); } do { if (!ctx.parse_array_item(in, idx)) { return false; } idx++; } while (in.expect(',')); return in.expect(']') && ctx.parse_array_stop(idx); } template inline bool _parse_object(Context &ctx, input &in) { if (!ctx.parse_object_start()) { return false; } if (in.expect('}')) { return ctx.parse_object_stop(); } do { std::string key; if (!in.expect('"') || !_parse_string(key, in) || !in.expect(':')) { return false; } if (!ctx.parse_object_item(in, key)) { return false; } } while (in.expect(',')); return in.expect('}') && ctx.parse_object_stop(); } template inline std::string _parse_number(input &in) { std::string num_str; while (1) { int ch = in.getc(); if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' || ch == 'E') { num_str.push_back(static_cast(ch)); } else if (ch == '.') { #if PICOJSON_USE_LOCALE num_str += localeconv()->decimal_point; #else num_str.push_back('.'); #endif } else { in.ungetc(); break; } } return num_str; } template inline bool _parse(Context &ctx, input &in) { in.skip_ws(); int ch = in.getc(); switch (ch) { #define IS(ch, text, op) \ case ch: \ if (in.match(text) && op) { \ return true; \ } else { \ return false; \ } IS('n', "ull", ctx.set_null()); IS('f', "alse", ctx.set_bool(false)); IS('t', "rue", ctx.set_bool(true)); #undef IS case '"': return ctx.parse_string(in); case '[': return _parse_array(ctx, in); case '{': return _parse_object(ctx, in); default: if (('0' <= ch && ch <= '9') || ch == '-') { double f; char *endp; in.ungetc(); std::string num_str(_parse_number(in)); if (num_str.empty()) { return false; } #ifdef PICOJSON_USE_INT64 { errno = 0; intmax_t ival = strtoimax(num_str.c_str(), &endp, 10); if (errno == 0 && std::numeric_limits::min() <= ival && ival <= std::numeric_limits::max() && endp == num_str.c_str() + num_str.size()) { ctx.set_int64(ival); return true; } } #endif f = strtod(num_str.c_str(), &endp); if (endp == num_str.c_str() + num_str.size()) { ctx.set_number(f); return true; } return false; } break; } in.ungetc(); return false; } class deny_parse_context { public: bool set_null() { return false; } bool set_bool(bool) { return false; } #ifdef PICOJSON_USE_INT64 bool set_int64(int64_t) { return false; } #endif bool set_number(double) { return false; } template bool parse_string(input &) { return false; } bool parse_array_start() { return false; } template bool parse_array_item(input &, size_t) { return false; } bool parse_array_stop(size_t) { return false; } bool parse_object_start() { return false; } template bool parse_object_item(input &, const std::string &) { return false; } }; class default_parse_context { protected: value *out_; size_t depths_; public: default_parse_context(value *out, size_t depths = DEFAULT_MAX_DEPTHS) : out_(out), depths_(depths) { } bool set_null() { *out_ = value(); return true; } bool set_bool(bool b) { *out_ = value(b); return true; } #ifdef PICOJSON_USE_INT64 bool set_int64(int64_t i) { *out_ = value(i); return true; } #endif bool set_number(double f) { *out_ = value(f); return true; } template bool parse_string(input &in) { *out_ = value(string_type, false); return _parse_string(out_->get(), in); } bool parse_array_start() { if (depths_ == 0) return false; --depths_; *out_ = value(array_type, false); return true; } template bool parse_array_item(input &in, size_t) { array &a = out_->get(); a.push_back(value()); default_parse_context ctx(&a.back(), depths_); return _parse(ctx, in); } bool parse_array_stop(size_t) { ++depths_; return true; } bool parse_object_start() { if (depths_ == 0) return false; *out_ = value(object_type, false); return true; } template bool parse_object_item(input &in, const std::string &key) { object &o = out_->get(); default_parse_context ctx(&o[key], depths_); return _parse(ctx, in); } bool parse_object_stop() { ++depths_; return true; } private: default_parse_context(const default_parse_context &); default_parse_context &operator=(const default_parse_context &); }; class null_parse_context { protected: size_t depths_; public: struct dummy_str { void push_back(int) { } }; public: null_parse_context(size_t depths = DEFAULT_MAX_DEPTHS) : depths_(depths) { } bool set_null() { return true; } bool set_bool(bool) { return true; } #ifdef PICOJSON_USE_INT64 bool set_int64(int64_t) { return true; } #endif bool set_number(double) { return true; } template bool parse_string(input &in) { dummy_str s; return _parse_string(s, in); } bool parse_array_start() { if (depths_ == 0) return false; --depths_; return true; } template bool parse_array_item(input &in, size_t) { return _parse(*this, in); } bool parse_array_stop(size_t) { ++depths_; return true; } bool parse_object_start() { if (depths_ == 0) return false; --depths_; return true; } template bool parse_object_item(input &in, const std::string &) { ++depths_; return _parse(*this, in); } bool parse_object_stop() { return true; } private: null_parse_context(const null_parse_context &); null_parse_context &operator=(const null_parse_context &); }; // obsolete, use the version below template inline std::string parse(value &out, Iter &pos, const Iter &last) { std::string err; pos = parse(out, pos, last, &err); return err; } template inline Iter _parse(Context &ctx, const Iter &first, const Iter &last, std::string *err) { input in(first, last); if (!_parse(ctx, in) && err != NULL) { char buf[64]; SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line()); *err = buf; while (1) { int ch = in.getc(); if (ch == -1 || ch == '\n') { break; } else if (ch >= ' ') { err->push_back(static_cast(ch)); } } } return in.cur(); } template inline Iter parse(value &out, const Iter &first, const Iter &last, std::string *err) { default_parse_context ctx(&out); return _parse(ctx, first, last, err); } inline std::string parse(value &out, const std::string &s) { std::string err; parse(out, s.begin(), s.end(), &err); return err; } inline std::string parse(value &out, std::istream &is) { std::string err; parse(out, std::istreambuf_iterator(is.rdbuf()), std::istreambuf_iterator(), &err); return err; } template struct last_error_t { static std::string s; }; template std::string last_error_t::s; inline void set_last_error(const std::string &s) { last_error_t::s = s; } inline const std::string &get_last_error() { return last_error_t::s; } inline bool operator==(const value &x, const value &y) { if (x.is()) return y.is(); #define PICOJSON_CMP(type) \ if (x.is()) \ return y.is() && x.get() == y.get() PICOJSON_CMP(bool); PICOJSON_CMP(double); PICOJSON_CMP(std::string); PICOJSON_CMP(array); PICOJSON_CMP(object); #undef PICOJSON_CMP PICOJSON_ASSERT(0); #ifdef _MSC_VER __assume(0); #endif return false; } inline bool operator!=(const value &x, const value &y) { return !(x == y); } } #if !PICOJSON_USE_RVALUE_REFERENCE namespace std { template <> inline void swap(picojson::value &x, picojson::value &y) { x.swap(y); } } #endif inline std::istream &operator>>(std::istream &is, picojson::value &x) { picojson::set_last_error(std::string()); const std::string err(picojson::parse(x, is)); if (!err.empty()) { picojson::set_last_error(err); is.setstate(std::ios::failbit); } return is; } inline std::ostream &operator<<(std::ostream &os, const picojson::value &x) { x.serialize(std::ostream_iterator(os)); return os; } #ifdef _MSC_VER #pragma warning(pop) #endif #endif