zswag 1.9.1.dev6__tar.gz → 1.9.2.dev21__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/.github/workflows/build-deploy.yml +5 -3
  2. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/.gitignore +2 -0
  3. zswag-1.9.2.dev21/.gitmodules +3 -0
  4. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/CMakeLists.txt +9 -4
  5. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/PKG-INFO +16 -5
  6. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/README.md +15 -4
  7. zswag-1.9.2.dev21/cmake/Dependencies.cmake +121 -0
  8. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/httpcl/include/httpcl/http-settings.hpp +11 -0
  9. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/httpcl/src/http-settings.cpp +58 -0
  10. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag.egg-info/PKG-INFO +16 -5
  11. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag.egg-info/SOURCES.txt +10 -2
  12. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag.egg-info/requires.txt +1 -1
  13. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/CMakeLists.txt +5 -1
  14. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/include/zswagcl/private/openapi-client.hpp +2 -0
  15. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/include/zswagcl/private/openapi-config.hpp +25 -26
  16. zswag-1.9.2.dev21/libs/zswagcl/include/zswagcl/private/openapi-oauth.hpp +60 -0
  17. zswag-1.9.2.dev21/libs/zswagcl/include/zswagcl/private/openapi-security.hpp +56 -0
  18. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/src/openapi-client.cpp +6 -36
  19. zswag-1.9.2.dev21/libs/zswagcl/src/openapi-config.cpp +14 -0
  20. zswag-1.9.2.dev21/libs/zswagcl/src/openapi-oauth.cpp +190 -0
  21. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/src/openapi-parser.cpp +50 -18
  22. zswag-1.9.2.dev21/libs/zswagcl/src/openapi-security.cpp +147 -0
  23. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/test/CMakeLists.txt +3 -1
  24. zswag-1.9.2.dev21/libs/zswagcl/test/src/oauth2-integration-test.cpp +292 -0
  25. zswag-1.9.2.dev21/libs/zswagcl/test/src/oauth2-test.cpp +466 -0
  26. zswag-1.9.2.dev21/libs/zswagcl/test/testdata/oauth2-openapi.yaml +60 -0
  27. zswag-1.9.2.dev21/libs/zswagcl/test/testdata/oauth2-settings.yaml +25 -0
  28. zswag-1.9.1.dev6/cmake/Dependencies.cmake +0 -223
  29. zswag-1.9.1.dev6/cmake/OpenSSL.cmake +0 -172
  30. zswag-1.9.1.dev6/libs/zswagcl/src/openapi-config.cpp +0 -113
  31. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/.clang-format +0 -0
  32. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/.github/pull_request_template.md +0 -0
  33. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/.github/scripts/validate_version.py +0 -0
  34. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/LICENSE +0 -0
  35. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/deploy.sh +0 -0
  36. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/doc/zswag-architecture.mdj +0 -0
  37. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/doc/zswag-architecture.png +0 -0
  38. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/httpcl/CMakeLists.txt +0 -0
  39. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/httpcl/include/httpcl/http-client.hpp +0 -0
  40. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/httpcl/include/httpcl/log.hpp +0 -0
  41. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/httpcl/include/httpcl/uri.hpp +0 -0
  42. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/httpcl/src/http-client.cpp +0 -0
  43. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/httpcl/src/log.cpp +0 -0
  44. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/httpcl/src/uri.cpp +0 -0
  45. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/httpcl/test/CMakeLists.txt +0 -0
  46. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/httpcl/test/src/main.cpp +0 -0
  47. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/httpcl/test/src/uri.cpp +0 -0
  48. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/pyzswagcl/CMakeLists.txt +0 -0
  49. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/pyzswagcl/py-openapi-client.cpp +0 -0
  50. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/pyzswagcl/py-openapi-client.h +0 -0
  51. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/pyzswagcl/py-zswagcl.cpp +0 -0
  52. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/__init__.py +0 -0
  53. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/app.py +0 -0
  54. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/doc.py +0 -0
  55. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/gen.py +0 -0
  56. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/reflect.py +0 -0
  57. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/.gitignore +0 -0
  58. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/CMakeLists.txt +0 -0
  59. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/__init__.py +0 -0
  60. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/calc/__init__.py +0 -0
  61. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/calc/__main__.py +0 -0
  62. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/calc/api.yaml +0 -0
  63. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/calc/calculator.zs +0 -0
  64. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/calc/client.py +0 -0
  65. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/calc/server.py +0 -0
  66. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/client.cpp +0 -0
  67. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/test_nested_service/services.zs +0 -0
  68. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/test_openapi_generator.bash +0 -0
  69. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/test_openapi_generator_1.yaml +0 -0
  70. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/test_openapi_generator_2.yaml +0 -0
  71. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/test_openapi_generator_3.yaml +0 -0
  72. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag/test/test_openapi_generator_base_config.yaml +0 -0
  73. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag.egg-info/dependency_links.txt +0 -0
  74. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswag.egg-info/top_level.txt +0 -0
  75. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/include/zswagcl/export.hpp +0 -0
  76. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/include/zswagcl/oaclient.hpp +0 -0
  77. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/include/zswagcl/private/openapi-parameter-helper.hpp +0 -0
  78. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/include/zswagcl/private/openapi-parser.hpp +0 -0
  79. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/src/base64.cpp +0 -0
  80. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/src/base64.hpp +0 -0
  81. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/src/oaclient.cpp +0 -0
  82. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/src/openapi-parameter-helper.cpp +0 -0
  83. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/test/src/base64.cpp +0 -0
  84. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/test/src/http-service-format.cpp +0 -0
  85. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/test/src/main.cpp +0 -0
  86. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/test/src/oaclient.cpp +0 -0
  87. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/test/src/openapi-parameter-helper.cpp +0 -0
  88. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/test/src/service_client_test.zs +0 -0
  89. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/test/testdata/auth.yaml +0 -0
  90. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/test/testdata/config-template.json +0 -0
  91. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/test/testdata/config-with-auth.json +0 -0
  92. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/libs/zswagcl/test/testdata/dummy.json +0 -0
  93. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/requirements.txt +0 -0
  94. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/setup.cfg +0 -0
  95. {zswag-1.9.1.dev6 → zswag-1.9.2.dev21}/setup.py +0 -0
@@ -48,6 +48,8 @@ jobs:
48
48
  echo "Node at $(which node): $(node -v); npm: $(npm -v)"
49
49
  - name: Checkout code
50
50
  uses: actions/checkout@v4
51
+ with:
52
+ submodules: recursive
51
53
  - name: Configure
52
54
  run: |
53
55
  python3 -m venv venv && . ./venv/bin/activate
@@ -124,8 +126,8 @@ jobs:
124
126
  if: matrix.os == 'windows-latest'
125
127
  working-directory: build
126
128
  run: |
127
- # Install NASM using chocolatey (required for OpenSSL build)
128
- choco install nasm -y
129
+ # Install NASM and jom using chocolatey (required for OpenSSL build)
130
+ choco install nasm jom -y
129
131
  # Add NASM to PATH for current session
130
132
  $env:PATH = "$env:PATH;C:\Program Files\NASM"
131
133
  # Verify NASM installation
@@ -155,7 +157,7 @@ jobs:
155
157
  with:
156
158
  fetch-depth: 0
157
159
  fetch-tags: true
158
-
160
+
159
161
  - name: Setup Python
160
162
  uses: actions/setup-python@v5
161
163
  with:
@@ -28,6 +28,8 @@ share/python-wheels/
28
28
  .installed.cfg
29
29
  *.egg
30
30
  MANIFEST
31
+ cmake-build-*
32
+ venv*
31
33
 
32
34
  # PyInstaller
33
35
  # Usually these files are written by a python script from a template
@@ -0,0 +1,3 @@
1
+ [submodule "cmake/CPM.cmake"]
2
+ path = cmake/CPM.cmake
3
+ url = https://github.com/cpm-cmake/CPM.cmake.git
@@ -10,7 +10,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
10
10
  # without requiring CMakeLists.txt updates between releases.
11
11
  # For official releases, this value should match the git tag.
12
12
  if(NOT DEFINED ZSWAG_VERSION)
13
- set(ZSWAG_VERSION 1.9.1)
13
+ set(ZSWAG_VERSION 1.10.0)
14
14
  endif()
15
15
 
16
16
  option(ZSWAG_BUILD_WHEELS "Enable zswag whl-output to WHEEL_DEPLOY_DIRECTORY." ON)
@@ -41,9 +41,14 @@ set (CMAKE_LIBRARY_OUTPUT_DIRECTORY "${ZSWAG_DEPLOY_DIR}")
41
41
  set (CMAKE_RUNTIME_OUTPUT_DIRECTORY "${ZSWAG_DEPLOY_DIR}")
42
42
 
43
43
  ##############
44
- # Dependencies via FetchContent
45
-
46
- # Include centralized dependency management
44
+ # Dependencies via CPM (CMake Package Manager)
45
+
46
+ set(CPM_DOWNLOAD_ALL ON)
47
+ set(CPM_USE_LOCAL_PACKAGES OFF)
48
+ # Include CPM.cmake
49
+ if (NOT CPM_INITIALIZED)
50
+ add_subdirectory(cmake/CPM.cmake)
51
+ endif ()
47
52
  include(cmake/Dependencies.cmake)
48
53
 
49
54
  ##############
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zswag
3
- Version: 1.9.1.dev6
3
+ Version: 1.9.2.dev21
4
4
  Summary: OpenAPI service transport layer for zserio.
5
5
  Home-page: https://github.com/klebert-engineering/zswag
6
6
  Author: Klebert Engineering
@@ -705,11 +705,13 @@ http-settings:
705
705
  basic-auth: # Basic auth credentials for matching requests.
706
706
  user: johndoe
707
707
  keychain: keychain-service-string
708
+ password: cleartext-password # alternative to keychain
708
709
  proxy: # Proxy settings for matching requests.
709
710
  host: localhost
710
711
  port: 8888
711
712
  user: test
712
713
  keychain: ...
714
+ password: cleartext-password # alternative to keychain
713
715
  cookies: # Additional Cookies for matching requests.
714
716
  key: value
715
717
  headers: # Additional Headers for matching requests.
@@ -717,6 +719,14 @@ http-settings:
717
719
  query: # Additional Query parameters for matching requests.
718
720
  key: value
719
721
  api-key: value # API Key as required by OpenAPI config - see description below.
722
+ oauth2:
723
+ clientId: my-client-id # For OAuth2 client-credentials auth
724
+ clientSecretKeychain: keychain-service-string # Prefer a keychain entry over cleartext
725
+ clientSecret: cleartext-secret # discouraged, use keychain
726
+ tokenUrl: https://issuer.example.com/oauth/token # optional override; defaults to tokenUrl from OpenAPI
727
+ refreshUrl: https://issuer.example.com/oauth/token # optional override; defaults to refreshUrl from OpenAPI, then tokenUrl
728
+ audience: https://api.example.com/ # Optional (some providers require it)
729
+ scope: ["orders.read", ...] # Optional scope override; defaults to OpenAPI setting per operation
720
730
  ```
721
731
 
722
732
  **Note:** For `proxy` configs, the credentials are optional.
@@ -965,10 +975,11 @@ constructor in C++/Python with the relevant detail.
965
975
 
966
976
  #### Component Support
967
977
 
968
- | Feature | C++ Client | Python Client | OAServer | zswag.gen |
969
- | ------------------ | ---------- | ------------- | -------- | --------- |
970
- | `HTTP Basic-Auth` `HTTP Bearer-Auth` `Cookie API-Key` `Header API-Key` `Query API-Key` | ✔️ | ✔️ | ✔️(**) | ✔️ |
971
- | `OpenID Connect` `OAuth2` | ❌️ | ❌️ | ✔️(**) | ❌️ |
978
+ | Feature | C++ Client | Python Client | OAServer | zswag.gen |
979
+ |----------------------------------------------------------------------------------------| ---------- | ------------- | -------- | --------- |
980
+ | `HTTP Basic-Auth` `HTTP Bearer-Auth` `Cookie API-Key` `Header API-Key` `Query API-Key` | ✔️ | ✔️ | ✔️(**) | ✔️ |
981
+ | `OAuth2[clientCredentials]` | ✔️ | ✔️ | ✔️(**) | ✔️ |
982
+ | `OpenID Connect` `OAuth2[authorizationCode]` `OAuth2[implicit]` `OAuth2[password]` | ❌️ | ❌️ | ✔️(**) | ❌️ |
972
983
 
973
984
  **(\*\*)**: The server support for all authentication schemes depends on your
974
985
  configuration of the WSGI server (Apache/Nginx/...) which wraps the zswag Flask app.
@@ -690,11 +690,13 @@ http-settings:
690
690
  basic-auth: # Basic auth credentials for matching requests.
691
691
  user: johndoe
692
692
  keychain: keychain-service-string
693
+ password: cleartext-password # alternative to keychain
693
694
  proxy: # Proxy settings for matching requests.
694
695
  host: localhost
695
696
  port: 8888
696
697
  user: test
697
698
  keychain: ...
699
+ password: cleartext-password # alternative to keychain
698
700
  cookies: # Additional Cookies for matching requests.
699
701
  key: value
700
702
  headers: # Additional Headers for matching requests.
@@ -702,6 +704,14 @@ http-settings:
702
704
  query: # Additional Query parameters for matching requests.
703
705
  key: value
704
706
  api-key: value # API Key as required by OpenAPI config - see description below.
707
+ oauth2:
708
+ clientId: my-client-id # For OAuth2 client-credentials auth
709
+ clientSecretKeychain: keychain-service-string # Prefer a keychain entry over cleartext
710
+ clientSecret: cleartext-secret # discouraged, use keychain
711
+ tokenUrl: https://issuer.example.com/oauth/token # optional override; defaults to tokenUrl from OpenAPI
712
+ refreshUrl: https://issuer.example.com/oauth/token # optional override; defaults to refreshUrl from OpenAPI, then tokenUrl
713
+ audience: https://api.example.com/ # Optional (some providers require it)
714
+ scope: ["orders.read", ...] # Optional scope override; defaults to OpenAPI setting per operation
705
715
  ```
706
716
 
707
717
  **Note:** For `proxy` configs, the credentials are optional.
@@ -950,10 +960,11 @@ constructor in C++/Python with the relevant detail.
950
960
 
951
961
  #### Component Support
952
962
 
953
- | Feature | C++ Client | Python Client | OAServer | zswag.gen |
954
- | ------------------ | ---------- | ------------- | -------- | --------- |
955
- | `HTTP Basic-Auth` `HTTP Bearer-Auth` `Cookie API-Key` `Header API-Key` `Query API-Key` | ✔️ | ✔️ | ✔️(**) | ✔️ |
956
- | `OpenID Connect` `OAuth2` | ❌️ | ❌️ | ✔️(**) | ❌️ |
963
+ | Feature | C++ Client | Python Client | OAServer | zswag.gen |
964
+ |----------------------------------------------------------------------------------------| ---------- | ------------- | -------- | --------- |
965
+ | `HTTP Basic-Auth` `HTTP Bearer-Auth` `Cookie API-Key` `Header API-Key` `Query API-Key` | ✔️ | ✔️ | ✔️(**) | ✔️ |
966
+ | `OAuth2[clientCredentials]` | ✔️ | ✔️ | ✔️(**) | ✔️ |
967
+ | `OpenID Connect` `OAuth2[authorizationCode]` `OAuth2[implicit]` `OAuth2[password]` | ❌️ | ❌️ | ✔️(**) | ❌️ |
957
968
 
958
969
  **(\*\*)**: The server support for all authentication schemes depends on your
959
970
  configuration of the WSGI server (Apache/Nginx/...) which wraps the zswag Flask app.
@@ -0,0 +1,121 @@
1
+ # Dependencies.cmake
2
+ # Centralized dependency management using CPM (CMake Package Manager)
3
+ # CPM provides better version management and diagnostics compared to raw FetchContent
4
+
5
+ # =============================================================================
6
+ # Core dependencies
7
+ # =============================================================================
8
+
9
+ # zlib
10
+ CPMAddPackage(
11
+ URI "gh:madler/zlib@1.3.1"
12
+ OPTIONS
13
+ "ZLIB_BUILD_EXAMPLES OFF"
14
+ "BUILD_TESTING OFF")
15
+ if(zlib_ADDED)
16
+ set_target_properties(zlib PROPERTIES EXCLUDE_FROM_ALL TRUE)
17
+ set_target_properties(zlibstatic PROPERTIES EXCLUDE_FROM_ALL TRUE)
18
+ # Create ZLIB::ZLIB alias if it doesn't exist
19
+ if(NOT TARGET ZLIB::ZLIB)
20
+ if(TARGET zlib)
21
+ add_library(ZLIB::ZLIB ALIAS zlib)
22
+ elseif(TARGET zlibstatic)
23
+ add_library(ZLIB::ZLIB ALIAS zlibstatic)
24
+ endif()
25
+ endif()
26
+ endif()
27
+
28
+ # fmt
29
+ CPMAddPackage(
30
+ URI "gh:fmtlib/fmt#11.1.3"
31
+ OPTIONS "FMT_HEADER_ONLY OFF"
32
+ )
33
+
34
+ # spdlog
35
+ CPMAddPackage(
36
+ URI "gh:gabime/spdlog@1.15.3"
37
+ OPTIONS
38
+ "SPDLOG_BUILD_TESTS OFF"
39
+ "SPDLOG_BUILD_EXAMPLE OFF"
40
+ "SPDLOG_FMT_EXTERNAL ON"
41
+ )
42
+
43
+ # yaml-cpp
44
+ CPMAddPackage(
45
+ URI "gh:jbeder/yaml-cpp#aa8d4e4750ec9fe9f8cc680eb90f1b15955c817e"
46
+ OPTIONS
47
+ "YAML_CPP_BUILD_TESTS OFF"
48
+ "YAML_CPP_BUILD_TOOLS OFF"
49
+ "YAML_CPP_BUILD_CONTRIB OFF"
50
+ )
51
+
52
+ # stx
53
+ CPMAddPackage("gh:Klebert-Engineering/stx@1.0.0")
54
+
55
+ # httplib
56
+ CPMAddPackage(
57
+ URI "gh:yhirose/cpp-httplib@0.15.3"
58
+ OPTIONS
59
+ "HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN OFF"
60
+ "HTTPLIB_INSTALL OFF"
61
+ )
62
+
63
+ # OpenSSL
64
+ set (OPENSSL_VERSION openssl-3.5.2)
65
+ CPMAddPackage("gh:klebert-engineering/openssl-cmake@1.0.0")
66
+
67
+ # pybind11 (only needed when building wheels)
68
+ if(ZSWAG_BUILD_WHEELS)
69
+ CPMAddPackage("gh:pybind/pybind11@2.13.6")
70
+ endif()
71
+
72
+ # python-cmake-wheel (only needed when building wheels)
73
+ if(ZSWAG_BUILD_WHEELS)
74
+ CPMAddPackage("gh:Klebert-Engineering/python-cmake-wheel@1.0.1")
75
+ endif()
76
+
77
+ # keychain
78
+ if(ZSWAG_KEYCHAIN_SUPPORT)
79
+ CPMAddPackage("gh:hrantzsch/keychain@1.3.1")
80
+ endif()
81
+
82
+ # Catch2 (for testing)
83
+ if(ZSWAG_ENABLE_TESTING)
84
+ CPMAddPackage(
85
+ URI "gh:catchorg/Catch2@3.8.1"
86
+ OPTIONS
87
+ "CATCH_INSTALL_DOCS OFF"
88
+ "CATCH_INSTALL_EXTRAS OFF"
89
+ )
90
+ endif()
91
+
92
+ # zserio-cmake-helper
93
+ set(ZSERIO_VERSION "2.16.1")
94
+ CPMAddPackage("gh:Klebert-Engineering/zserio-cmake-helper@1.1.4")
95
+ # Add zserio C++ runtime target
96
+ if(NOT TARGET ZserioCppRuntime)
97
+ add_zserio_cpp_runtime()
98
+ endif()
99
+
100
+ # =============================================================================
101
+ # Configure httplib with OpenSSL and zlib support
102
+ # =============================================================================
103
+
104
+ if (TARGET httplib)
105
+ target_compile_definitions(httplib INTERFACE CPPHTTPLIB_OPENSSL_SUPPORT)
106
+ target_link_libraries(httplib INTERFACE OpenSSL::SSL OpenSSL::Crypto ZLIB::ZLIB)
107
+ endif()
108
+
109
+ # =============================================================================
110
+ # Configure python-cmake-wheel integration
111
+ # =============================================================================
112
+
113
+ if(ZSWAG_BUILD_WHEELS AND python-cmake-wheel_ADDED)
114
+ set(CMAKE_MODULE_PATH "${python-cmake-wheel_SOURCE_DIR}" ${CMAKE_MODULE_PATH})
115
+
116
+ if(NOT TARGET wheel)
117
+ set(Python3_FIND_STRATEGY LOCATION)
118
+ include(python-wheel)
119
+ set(WHEEL_DEPLOY_DIRECTORY "${ZSWAG_DEPLOY_DIR}/wheel")
120
+ endif()
121
+ endif()
@@ -46,6 +46,16 @@ struct Config
46
46
  std::string keychain;
47
47
  };
48
48
 
49
+ struct OAuth2 {
50
+ std::string clientId;
51
+ std::string clientSecret;
52
+ std::string clientSecretKeychain;
53
+ std::string tokenUrlOverride; // optional
54
+ std::string refreshUrlOverride; // optional
55
+ std::string audience; // optional
56
+ std::vector<std::string> scopesOverride; // optional
57
+ };
58
+
49
59
  std::optional<std::string> scope;
50
60
  std::regex urlPattern;
51
61
  std::string urlPatternString;
@@ -53,6 +63,7 @@ struct Config
53
63
  std::map<std::string, std::string> cookies;
54
64
  std::optional<BasicAuthentication> auth;
55
65
  std::optional<Proxy> proxy;
66
+ std::optional<OAuth2> oauth2;
56
67
  std::optional<std::string> apiKey;
57
68
  Headers headers;
58
69
  Query query;
@@ -180,6 +180,25 @@ YAML::Node configToNode(Config const& config) {
180
180
  if (config.apiKey)
181
181
  result["api-key"] = *config.apiKey;
182
182
 
183
+ if (config.oauth2) {
184
+ YAML::Node oauth2Node;
185
+ if (!config.oauth2->clientId.empty())
186
+ oauth2Node["clientId"] = config.oauth2->clientId;
187
+ if (!config.oauth2->clientSecret.empty())
188
+ oauth2Node["clientSecret"] = config.oauth2->clientSecret;
189
+ if (!config.oauth2->clientSecretKeychain.empty())
190
+ oauth2Node["clientSecretKeychain"] = config.oauth2->clientSecretKeychain;
191
+ if (!config.oauth2->tokenUrlOverride.empty())
192
+ oauth2Node["tokenUrl"] = config.oauth2->tokenUrlOverride;
193
+ if (!config.oauth2->refreshUrlOverride.empty())
194
+ oauth2Node["refreshUrl"] = config.oauth2->refreshUrlOverride;
195
+ if (!config.oauth2->audience.empty())
196
+ oauth2Node["audience"] = config.oauth2->audience;
197
+ if (!config.oauth2->scopesOverride.empty())
198
+ oauth2Node["scopes"] = config.oauth2->scopesOverride;
199
+ result["oauth2"] = oauth2Node;
200
+ }
201
+
183
202
  return result;
184
203
  }
185
204
 
@@ -221,6 +240,27 @@ Config configFromNode(YAML::Node const& node)
221
240
  if (auto apiKey = node["api-key"])
222
241
  conf.apiKey = apiKey.as<std::string>();
223
242
 
243
+ if (auto oauth2Node = node["oauth2"]) {
244
+ Config::OAuth2 oauth2;
245
+ if (auto v = oauth2Node["clientId"])
246
+ oauth2.clientId = v.as<std::string>();
247
+ if (auto v = oauth2Node["clientSecret"])
248
+ oauth2.clientSecret = v.as<std::string>();
249
+ if (auto v = oauth2Node["clientSecretKeychain"])
250
+ oauth2.clientSecretKeychain = v.as<std::string>();
251
+ if (auto v = oauth2Node["tokenUrl"])
252
+ oauth2.tokenUrlOverride = v.as<std::string>();
253
+ if (auto v = oauth2Node["refreshUrl"])
254
+ oauth2.refreshUrlOverride = v.as<std::string>();
255
+ if (auto v = oauth2Node["audience"])
256
+ oauth2.audience = v.as<std::string>();
257
+ if (auto v = oauth2Node["scope"]) {
258
+ for (auto const& scope : v)
259
+ oauth2.scopesOverride.emplace_back(scope.as<std::string>());
260
+ }
261
+ conf.oauth2 = oauth2;
262
+ }
263
+
224
264
  return conf;
225
265
  }
226
266
  }
@@ -520,5 +560,23 @@ Config& Config::operator |= (Config const& other) {
520
560
  proxy = other.proxy;
521
561
  if (other.apiKey)
522
562
  apiKey = other.apiKey;
563
+ if (other.oauth2) {
564
+ if (!oauth2)
565
+ oauth2.emplace();
566
+ if (!other.oauth2->clientId.empty())
567
+ oauth2->clientId = other.oauth2->clientId;
568
+ if (!other.oauth2->clientSecretKeychain.empty())
569
+ oauth2->clientSecretKeychain = other.oauth2->clientSecretKeychain;
570
+ if (!other.oauth2->clientSecret.empty())
571
+ oauth2->clientSecret = other.oauth2->clientSecret;
572
+ if (!other.oauth2->tokenUrlOverride.empty())
573
+ oauth2->tokenUrlOverride = other.oauth2->tokenUrlOverride;
574
+ if (!other.oauth2->refreshUrlOverride.empty())
575
+ oauth2->refreshUrlOverride = other.oauth2->refreshUrlOverride;
576
+ if (!other.oauth2->audience.empty())
577
+ oauth2->audience = other.oauth2->audience;
578
+ if (!other.oauth2->scopesOverride.empty())
579
+ oauth2->scopesOverride = other.oauth2->scopesOverride;
580
+ }
523
581
  return *this;
524
582
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zswag
3
- Version: 1.9.1.dev6
3
+ Version: 1.9.2.dev21
4
4
  Summary: OpenAPI service transport layer for zserio.
5
5
  Home-page: https://github.com/klebert-engineering/zswag
6
6
  Author: Klebert Engineering
@@ -705,11 +705,13 @@ http-settings:
705
705
  basic-auth: # Basic auth credentials for matching requests.
706
706
  user: johndoe
707
707
  keychain: keychain-service-string
708
+ password: cleartext-password # alternative to keychain
708
709
  proxy: # Proxy settings for matching requests.
709
710
  host: localhost
710
711
  port: 8888
711
712
  user: test
712
713
  keychain: ...
714
+ password: cleartext-password # alternative to keychain
713
715
  cookies: # Additional Cookies for matching requests.
714
716
  key: value
715
717
  headers: # Additional Headers for matching requests.
@@ -717,6 +719,14 @@ http-settings:
717
719
  query: # Additional Query parameters for matching requests.
718
720
  key: value
719
721
  api-key: value # API Key as required by OpenAPI config - see description below.
722
+ oauth2:
723
+ clientId: my-client-id # For OAuth2 client-credentials auth
724
+ clientSecretKeychain: keychain-service-string # Prefer a keychain entry over cleartext
725
+ clientSecret: cleartext-secret # discouraged, use keychain
726
+ tokenUrl: https://issuer.example.com/oauth/token # optional override; defaults to tokenUrl from OpenAPI
727
+ refreshUrl: https://issuer.example.com/oauth/token # optional override; defaults to refreshUrl from OpenAPI, then tokenUrl
728
+ audience: https://api.example.com/ # Optional (some providers require it)
729
+ scope: ["orders.read", ...] # Optional scope override; defaults to OpenAPI setting per operation
720
730
  ```
721
731
 
722
732
  **Note:** For `proxy` configs, the credentials are optional.
@@ -965,10 +975,11 @@ constructor in C++/Python with the relevant detail.
965
975
 
966
976
  #### Component Support
967
977
 
968
- | Feature | C++ Client | Python Client | OAServer | zswag.gen |
969
- | ------------------ | ---------- | ------------- | -------- | --------- |
970
- | `HTTP Basic-Auth` `HTTP Bearer-Auth` `Cookie API-Key` `Header API-Key` `Query API-Key` | ✔️ | ✔️ | ✔️(**) | ✔️ |
971
- | `OpenID Connect` `OAuth2` | ❌️ | ❌️ | ✔️(**) | ❌️ |
978
+ | Feature | C++ Client | Python Client | OAServer | zswag.gen |
979
+ |----------------------------------------------------------------------------------------| ---------- | ------------- | -------- | --------- |
980
+ | `HTTP Basic-Auth` `HTTP Bearer-Auth` `Cookie API-Key` `Header API-Key` `Query API-Key` | ✔️ | ✔️ | ✔️(**) | ✔️ |
981
+ | `OAuth2[clientCredentials]` | ✔️ | ✔️ | ✔️(**) | ✔️ |
982
+ | `OpenID Connect` `OAuth2[authorizationCode]` `OAuth2[implicit]` `OAuth2[password]` | ❌️ | ❌️ | ✔️(**) | ❌️ |
972
983
 
973
984
  **(\*\*)**: The server support for all authentication schemes depends on your
974
985
  configuration of the WSGI server (Apache/Nginx/...) which wraps the zswag Flask app.
@@ -1,5 +1,6 @@
1
1
  .clang-format
2
2
  .gitignore
3
+ .gitmodules
3
4
  CMakeLists.txt
4
5
  LICENSE
5
6
  README.md
@@ -10,7 +11,6 @@ setup.py
10
11
  .github/scripts/validate_version.py
11
12
  .github/workflows/build-deploy.yml
12
13
  cmake/Dependencies.cmake
13
- cmake/OpenSSL.cmake
14
14
  doc/zswag-architecture.mdj
15
15
  doc/zswag-architecture.png
16
16
  libs/httpcl/CMakeLists.txt
@@ -60,23 +60,31 @@ libs/zswagcl/include/zswagcl/export.hpp
60
60
  libs/zswagcl/include/zswagcl/oaclient.hpp
61
61
  libs/zswagcl/include/zswagcl/private/openapi-client.hpp
62
62
  libs/zswagcl/include/zswagcl/private/openapi-config.hpp
63
+ libs/zswagcl/include/zswagcl/private/openapi-oauth.hpp
63
64
  libs/zswagcl/include/zswagcl/private/openapi-parameter-helper.hpp
64
65
  libs/zswagcl/include/zswagcl/private/openapi-parser.hpp
66
+ libs/zswagcl/include/zswagcl/private/openapi-security.hpp
65
67
  libs/zswagcl/src/base64.cpp
66
68
  libs/zswagcl/src/base64.hpp
67
69
  libs/zswagcl/src/oaclient.cpp
68
70
  libs/zswagcl/src/openapi-client.cpp
69
71
  libs/zswagcl/src/openapi-config.cpp
72
+ libs/zswagcl/src/openapi-oauth.cpp
70
73
  libs/zswagcl/src/openapi-parameter-helper.cpp
71
74
  libs/zswagcl/src/openapi-parser.cpp
75
+ libs/zswagcl/src/openapi-security.cpp
72
76
  libs/zswagcl/test/CMakeLists.txt
73
77
  libs/zswagcl/test/src/base64.cpp
74
78
  libs/zswagcl/test/src/http-service-format.cpp
75
79
  libs/zswagcl/test/src/main.cpp
76
80
  libs/zswagcl/test/src/oaclient.cpp
81
+ libs/zswagcl/test/src/oauth2-integration-test.cpp
82
+ libs/zswagcl/test/src/oauth2-test.cpp
77
83
  libs/zswagcl/test/src/openapi-parameter-helper.cpp
78
84
  libs/zswagcl/test/src/service_client_test.zs
79
85
  libs/zswagcl/test/testdata/auth.yaml
80
86
  libs/zswagcl/test/testdata/config-template.json
81
87
  libs/zswagcl/test/testdata/config-with-auth.json
82
- libs/zswagcl/test/testdata/dummy.json
88
+ libs/zswagcl/test/testdata/dummy.json
89
+ libs/zswagcl/test/testdata/oauth2-openapi.yaml
90
+ libs/zswagcl/test/testdata/oauth2-settings.yaml
@@ -1,4 +1,4 @@
1
- pyzswagcl==1.9.1.dev6
1
+ pyzswagcl==1.9.2.dev21
2
2
  connexion~=2.14.2
3
3
  requests
4
4
  zserio<3.0.0
@@ -10,13 +10,17 @@ add_library(zswagcl SHARED
10
10
  include/zswagcl/private/openapi-parameter-helper.hpp
11
11
  include/zswagcl/private/openapi-parser.hpp
12
12
  include/zswagcl/oaclient.hpp
13
+ include/zswagcl/private/openapi-security.hpp
14
+ include/zswagcl/private/openapi-oauth.hpp
13
15
 
14
16
  src/base64.cpp
15
17
  src/openapi-client.cpp
16
18
  src/openapi-config.cpp
17
19
  src/openapi-parameter-helper.cpp
18
20
  src/openapi-parser.cpp
19
- src/oaclient.cpp)
21
+ src/openapi-security.cpp
22
+ src/oaclient.cpp
23
+ src/openapi-oauth.cpp)
20
24
 
21
25
  target_link_libraries(zswagcl
22
26
  PUBLIC
@@ -5,6 +5,7 @@
5
5
  #include "openapi-parser.hpp"
6
6
  #include "openapi-config.hpp"
7
7
  #include "openapi-parameter-helper.hpp"
8
+ #include "openapi-security.hpp"
8
9
 
9
10
  #include "httpcl/uri.hpp"
10
11
  #include "httpcl/http-client.hpp"
@@ -17,6 +18,7 @@ class OpenAPIClient
17
18
  public:
18
19
  OpenAPIConfig config_;
19
20
  httpcl::Config httpConfig_;
21
+ AuthRegistry authHandlers_;
20
22
 
21
23
  OpenAPIClient(OpenAPIConfig config,
22
24
  httpcl::Config httpConfig,
@@ -21,46 +21,45 @@ struct OpenAPIConfig
21
21
  Header
22
22
  };
23
23
 
24
- struct SecurityScheme {
25
- std::string id;
26
- explicit SecurityScheme(std::string id);
27
- virtual ~SecurityScheme() = default;
28
- virtual bool checkOrApply(httpcl::Config& config, std::string& err) const = 0;
24
+ enum class SecuritySchemeType {
25
+ HttpBasic,
26
+ HttpBearer,
27
+ ApiKeyQuery,
28
+ ApiKeyHeader,
29
+ ApiKeyCookie,
30
+ OAuth2ClientCredentials
29
31
  };
30
32
 
33
+ struct SecurityScheme
34
+ {
35
+ SecuritySchemeType type;
36
+ std::string oauthTokenUrl; // For oauth2
37
+ std::string oauthRefreshUrl; // For oauth2
38
+ std::map<std::string, std::string> oauthScopes; // Scope -> Description
39
+ std::string apiKeyName; // Header/Query/Cookie name
40
+ std::string id; // Scheme name for introspection/debugging
41
+ };
31
42
  using SecuritySchemePtr = std::shared_ptr<SecurityScheme>;
32
43
 
33
44
  /**
34
- * Security Scheme References
45
+ * Security Scheme References by operations.
35
46
  *
36
47
  * Disjunctive normal form ([A [AND B]+][ OR C [AND D]+]+) of required
37
48
  * security schemes. An empty vector is used to encode that no auth
38
49
  * scheme is required.
39
50
  */
40
- using SecurityAlternatives = std::vector<std::vector<SecuritySchemePtr>>;
51
+ struct SecurityRequirement
52
+ {
53
+ SecuritySchemePtr scheme;
54
+ // For each operation, a subset of scopes may be defined which are required for it.
55
+ std::vector<std::string> scopes;
56
+ };
57
+ using SecurityAlternative = std::vector<SecurityRequirement>;
58
+ using SecurityAlternatives = std::vector<SecurityAlternative>;
41
59
 
42
60
  /**
43
61
  * Supported Authentication Schemes
44
62
  */
45
- struct BasicAuth : public SecurityScheme {
46
- explicit BasicAuth(std::string id);
47
- bool checkOrApply(httpcl::Config& config, std::string& err) const override;
48
- };
49
- struct BearerAuth : public SecurityScheme {
50
- explicit BearerAuth(std::string id);
51
- bool checkOrApply(httpcl::Config& config, std::string& err) const override;
52
- };
53
- struct APIKeyAuth : public SecurityScheme {
54
- explicit APIKeyAuth(std::string id, ParameterLocation location, std::string keyName);
55
- bool checkOrApply(httpcl::Config& config, std::string& err) const override;
56
- ParameterLocation location = ParameterLocation::Header;
57
- std::string keyName;
58
- };
59
- struct CookieAuth : public SecurityScheme {
60
- explicit CookieAuth(std::string id, std::string cookieName);
61
- bool checkOrApply(httpcl::Config& config, std::string& err) const override;
62
- std::string cookieName;
63
- };
64
63
 
65
64
  struct Parameter {
66
65
  ParameterLocation location = ParameterLocation::Query;
@@ -0,0 +1,60 @@
1
+ #pragma once
2
+
3
+ #include "openapi-security.hpp"
4
+
5
+ namespace zswagcl
6
+ {
7
+
8
+ class OAuth2ClientCredentialsHandler final : public ISecurityHandler
9
+ {
10
+ public:
11
+ bool satisfy(
12
+ const OpenAPIConfig::SecurityRequirement& req,
13
+ AuthContext const& ctx,
14
+ std::string& mismatchReason) override;
15
+
16
+ private:
17
+ // OAuth2 grant type constants
18
+ static constexpr const char* GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials";
19
+ static constexpr const char* GRANT_TYPE_REFRESH_TOKEN = "refresh_token";
20
+
21
+ struct TokenKey
22
+ {
23
+ std::string tokenUrl, clientId, audience, scopeKey;
24
+ bool operator==(const TokenKey& o) const
25
+ {
26
+ return tokenUrl == o.tokenUrl && clientId == o.clientId && audience == o.audience &&
27
+ scopeKey == o.scopeKey;
28
+ }
29
+ };
30
+
31
+ struct TokenKeyHash
32
+ {
33
+ size_t operator()(const TokenKey& k) const
34
+ {
35
+ std::hash<std::string> H;
36
+ return H(k.tokenUrl) ^ H(k.clientId) ^ H(k.audience) ^ H(k.scopeKey);
37
+ }
38
+ };
39
+
40
+ struct MintedToken
41
+ {
42
+ std::string accessToken;
43
+ std::string refreshToken; // may be empty (most CC flows don’t return one)
44
+ std::chrono::steady_clock::time_point expiresAt;
45
+ };
46
+
47
+ std::shared_mutex m_;
48
+ std::unordered_map<TokenKey, MintedToken, TokenKeyHash> cache_;
49
+
50
+ // Method for both token fetch and refresh requests
51
+ MintedToken requestToken(
52
+ AuthContext const& httpCtx,
53
+ const httpcl::Config::OAuth2& oauthConfig,
54
+ const std::string& resolvedTokenUrl,
55
+ const std::string& grantType,
56
+ const std::vector<std::string>& resolvedScopes = {},
57
+ const std::string& refreshToken = "");
58
+ };
59
+
60
+ } // namespace zswagcl