capo-codedeploy 0.1.0__py3-none-any.whl

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 (472) hide show
  1. capo_codedeploy/__init__.py +70 -0
  2. capo_codedeploy/_async.py +25 -0
  3. capo_codedeploy/_auth/_identity.py +16 -0
  4. capo_codedeploy/_auth/_providers.py +886 -0
  5. capo_codedeploy/_auth/_signers.py +88 -0
  6. capo_codedeploy/_auth/_sigv4.py +434 -0
  7. capo_codedeploy/_auth/_zapros_handler.py +80 -0
  8. capo_codedeploy/_iter.py +113 -0
  9. capo_codedeploy/_operations/code_deploy_20141006/add_tags_to_on_premises_instances.py +153 -0
  10. capo_codedeploy/_operations/code_deploy_20141006/batch_get_application_revisions.py +173 -0
  11. capo_codedeploy/_operations/code_deploy_20141006/batch_get_applications.py +163 -0
  12. capo_codedeploy/_operations/code_deploy_20141006/batch_get_deployment_groups.py +178 -0
  13. capo_codedeploy/_operations/code_deploy_20141006/batch_get_deployment_instances.py +178 -0
  14. capo_codedeploy/_operations/code_deploy_20141006/batch_get_deployment_targets.py +188 -0
  15. capo_codedeploy/_operations/code_deploy_20141006/batch_get_deployments.py +158 -0
  16. capo_codedeploy/_operations/code_deploy_20141006/batch_get_on_premises_instances.py +158 -0
  17. capo_codedeploy/_operations/code_deploy_20141006/continue_deployment.py +155 -0
  18. capo_codedeploy/_operations/code_deploy_20141006/create_application.py +175 -0
  19. capo_codedeploy/_operations/code_deploy_20141006/create_deployment.py +278 -0
  20. capo_codedeploy/_operations/code_deploy_20141006/create_deployment_config.py +185 -0
  21. capo_codedeploy/_operations/code_deploy_20141006/create_deployment_group.py +320 -0
  22. capo_codedeploy/_operations/code_deploy_20141006/delete_application.py +129 -0
  23. capo_codedeploy/_operations/code_deploy_20141006/delete_deployment_config.py +136 -0
  24. capo_codedeploy/_operations/code_deploy_20141006/delete_deployment_group.py +167 -0
  25. capo_codedeploy/_operations/code_deploy_20141006/delete_git_hub_account_token.py +166 -0
  26. capo_codedeploy/_operations/code_deploy_20141006/delete_resources_by_external_id.py +137 -0
  27. capo_codedeploy/_operations/code_deploy_20141006/deregister_on_premises_instance.py +126 -0
  28. capo_codedeploy/_operations/code_deploy_20141006/get_application.py +157 -0
  29. capo_codedeploy/_operations/code_deploy_20141006/get_application_revision.py +173 -0
  30. capo_codedeploy/_operations/code_deploy_20141006/get_deployment.py +157 -0
  31. capo_codedeploy/_operations/code_deploy_20141006/get_deployment_config.py +162 -0
  32. capo_codedeploy/_operations/code_deploy_20141006/get_deployment_group.py +179 -0
  33. capo_codedeploy/_operations/code_deploy_20141006/get_deployment_instance.py +177 -0
  34. capo_codedeploy/_operations/code_deploy_20141006/get_deployment_target.py +182 -0
  35. capo_codedeploy/_operations/code_deploy_20141006/get_on_premises_instance.py +157 -0
  36. capo_codedeploy/_operations/code_deploy_20141006/list_application_revisions.py +195 -0
  37. capo_codedeploy/_operations/code_deploy_20141006/list_applications.py +149 -0
  38. capo_codedeploy/_operations/code_deploy_20141006/list_deployment_configs.py +147 -0
  39. capo_codedeploy/_operations/code_deploy_20141006/list_deployment_groups.py +162 -0
  40. capo_codedeploy/_operations/code_deploy_20141006/list_deployment_instances.py +198 -0
  41. capo_codedeploy/_operations/code_deploy_20141006/list_deployment_targets.py +188 -0
  42. capo_codedeploy/_operations/code_deploy_20141006/list_deployments.py +199 -0
  43. capo_codedeploy/_operations/code_deploy_20141006/list_git_hub_account_token_names.py +157 -0
  44. capo_codedeploy/_operations/code_deploy_20141006/list_on_premises_instances.py +159 -0
  45. capo_codedeploy/_operations/code_deploy_20141006/list_tags_for_resource.py +157 -0
  46. capo_codedeploy/_operations/code_deploy_20141006/put_lifecycle_event_hook_execution_status.py +177 -0
  47. capo_codedeploy/_operations/code_deploy_20141006/register_application_revision.py +147 -0
  48. capo_codedeploy/_operations/code_deploy_20141006/register_on_premises_instance.py +166 -0
  49. capo_codedeploy/_operations/code_deploy_20141006/remove_tags_from_on_premises_instances.py +153 -0
  50. capo_codedeploy/_operations/code_deploy_20141006/skip_wait_time_for_instance_termination.py +146 -0
  51. capo_codedeploy/_operations/code_deploy_20141006/stop_deployment.py +172 -0
  52. capo_codedeploy/_operations/code_deploy_20141006/tag_resource.py +174 -0
  53. capo_codedeploy/_operations/code_deploy_20141006/untag_resource.py +174 -0
  54. capo_codedeploy/_operations/code_deploy_20141006/update_application.py +134 -0
  55. capo_codedeploy/_operations/code_deploy_20141006/update_deployment_group.py +310 -0
  56. capo_codedeploy/_pagination.py +21 -0
  57. capo_codedeploy/_protocol/__init__.py +1 -0
  58. capo_codedeploy/_protocol/errors.py +93 -0
  59. capo_codedeploy/_protocol/eventstream.py +238 -0
  60. capo_codedeploy/_protocol/serialize.py +47 -0
  61. capo_codedeploy/_protocol/xml.py +33 -0
  62. capo_codedeploy/_rule_engine/__init__.py +0 -0
  63. capo_codedeploy/_rule_engine/_aws_partition.py +160 -0
  64. capo_codedeploy/_rule_engine/_endpoint_rule_set.py +138 -0
  65. capo_codedeploy/_rule_engine/_endpoint_runtime.py +389 -0
  66. capo_codedeploy/_services/_aws_config.py +160 -0
  67. capo_codedeploy/_services/_pipeline.py +198 -0
  68. capo_codedeploy/_services/async_code_deploy.py +3278 -0
  69. capo_codedeploy/_services/code_deploy.py +3229 -0
  70. capo_codedeploy/errors/__init__.py +335 -0
  71. capo_codedeploy/errors/_base.py +94 -0
  72. capo_codedeploy/errors/alarms_limit_exceeded_exception.py +52 -0
  73. capo_codedeploy/errors/application_already_exists_exception.py +54 -0
  74. capo_codedeploy/errors/application_does_not_exist_exception.py +54 -0
  75. capo_codedeploy/errors/application_limit_exceeded_exception.py +54 -0
  76. capo_codedeploy/errors/application_name_required_exception.py +54 -0
  77. capo_codedeploy/errors/arn_not_supported_exception.py +52 -0
  78. capo_codedeploy/errors/batch_limit_exceeded_exception.py +52 -0
  79. capo_codedeploy/errors/bucket_name_filter_required_exception.py +54 -0
  80. capo_codedeploy/errors/deployment_already_completed_exception.py +54 -0
  81. capo_codedeploy/errors/deployment_config_already_exists_exception.py +54 -0
  82. capo_codedeploy/errors/deployment_config_does_not_exist_exception.py +54 -0
  83. capo_codedeploy/errors/deployment_config_in_use_exception.py +54 -0
  84. capo_codedeploy/errors/deployment_config_limit_exceeded_exception.py +54 -0
  85. capo_codedeploy/errors/deployment_config_name_required_exception.py +54 -0
  86. capo_codedeploy/errors/deployment_does_not_exist_exception.py +54 -0
  87. capo_codedeploy/errors/deployment_group_already_exists_exception.py +54 -0
  88. capo_codedeploy/errors/deployment_group_does_not_exist_exception.py +54 -0
  89. capo_codedeploy/errors/deployment_group_limit_exceeded_exception.py +54 -0
  90. capo_codedeploy/errors/deployment_group_name_required_exception.py +54 -0
  91. capo_codedeploy/errors/deployment_id_required_exception.py +54 -0
  92. capo_codedeploy/errors/deployment_is_not_in_ready_state_exception.py +54 -0
  93. capo_codedeploy/errors/deployment_limit_exceeded_exception.py +54 -0
  94. capo_codedeploy/errors/deployment_not_started_exception.py +54 -0
  95. capo_codedeploy/errors/deployment_target_does_not_exist_exception.py +54 -0
  96. capo_codedeploy/errors/deployment_target_id_required_exception.py +54 -0
  97. capo_codedeploy/errors/deployment_target_list_size_exceeded_exception.py +56 -0
  98. capo_codedeploy/errors/description_too_long_exception.py +52 -0
  99. capo_codedeploy/errors/ecs_service_mapping_limit_exceeded_exception.py +54 -0
  100. capo_codedeploy/errors/git_hub_account_token_does_not_exist_exception.py +54 -0
  101. capo_codedeploy/errors/git_hub_account_token_name_required_exception.py +54 -0
  102. capo_codedeploy/errors/iam_arn_required_exception.py +52 -0
  103. capo_codedeploy/errors/iam_session_arn_already_registered_exception.py +54 -0
  104. capo_codedeploy/errors/iam_user_arn_already_registered_exception.py +54 -0
  105. capo_codedeploy/errors/iam_user_arn_required_exception.py +52 -0
  106. capo_codedeploy/errors/instance_does_not_exist_exception.py +54 -0
  107. capo_codedeploy/errors/instance_id_required_exception.py +52 -0
  108. capo_codedeploy/errors/instance_limit_exceeded_exception.py +54 -0
  109. capo_codedeploy/errors/instance_name_already_registered_exception.py +54 -0
  110. capo_codedeploy/errors/instance_name_required_exception.py +54 -0
  111. capo_codedeploy/errors/instance_not_registered_exception.py +54 -0
  112. capo_codedeploy/errors/invalid_alarm_config_exception.py +52 -0
  113. capo_codedeploy/errors/invalid_application_name_exception.py +54 -0
  114. capo_codedeploy/errors/invalid_arn_exception.py +52 -0
  115. capo_codedeploy/errors/invalid_auto_rollback_config_exception.py +54 -0
  116. capo_codedeploy/errors/invalid_auto_scaling_group_exception.py +54 -0
  117. capo_codedeploy/errors/invalid_blue_green_deployment_configuration_exception.py +60 -0
  118. capo_codedeploy/errors/invalid_bucket_name_filter_exception.py +54 -0
  119. capo_codedeploy/errors/invalid_compute_platform_exception.py +54 -0
  120. capo_codedeploy/errors/invalid_deployed_state_filter_exception.py +54 -0
  121. capo_codedeploy/errors/invalid_deployment_config_name_exception.py +54 -0
  122. capo_codedeploy/errors/invalid_deployment_group_name_exception.py +54 -0
  123. capo_codedeploy/errors/invalid_deployment_id_exception.py +52 -0
  124. capo_codedeploy/errors/invalid_deployment_instance_type_exception.py +54 -0
  125. capo_codedeploy/errors/invalid_deployment_status_exception.py +54 -0
  126. capo_codedeploy/errors/invalid_deployment_style_exception.py +54 -0
  127. capo_codedeploy/errors/invalid_deployment_target_id_exception.py +54 -0
  128. capo_codedeploy/errors/invalid_deployment_wait_type_exception.py +54 -0
  129. capo_codedeploy/errors/invalid_ec2_tag_combination_exception.py +54 -0
  130. capo_codedeploy/errors/invalid_ec2_tag_exception.py +52 -0
  131. capo_codedeploy/errors/invalid_ecs_service_exception.py +52 -0
  132. capo_codedeploy/errors/invalid_external_id_exception.py +52 -0
  133. capo_codedeploy/errors/invalid_file_exists_behavior_exception.py +54 -0
  134. capo_codedeploy/errors/invalid_git_hub_account_token_exception.py +54 -0
  135. capo_codedeploy/errors/invalid_git_hub_account_token_name_exception.py +54 -0
  136. capo_codedeploy/errors/invalid_iam_session_arn_exception.py +54 -0
  137. capo_codedeploy/errors/invalid_iam_user_arn_exception.py +52 -0
  138. capo_codedeploy/errors/invalid_ignore_application_stop_failures_value_exception.py +60 -0
  139. capo_codedeploy/errors/invalid_input_exception.py +52 -0
  140. capo_codedeploy/errors/invalid_instance_name_exception.py +52 -0
  141. capo_codedeploy/errors/invalid_instance_status_exception.py +54 -0
  142. capo_codedeploy/errors/invalid_instance_type_exception.py +52 -0
  143. capo_codedeploy/errors/invalid_key_prefix_filter_exception.py +54 -0
  144. capo_codedeploy/errors/invalid_lifecycle_event_hook_execution_id_exception.py +60 -0
  145. capo_codedeploy/errors/invalid_lifecycle_event_hook_execution_status_exception.py +60 -0
  146. capo_codedeploy/errors/invalid_load_balancer_info_exception.py +54 -0
  147. capo_codedeploy/errors/invalid_minimum_healthy_host_value_exception.py +54 -0
  148. capo_codedeploy/errors/invalid_next_token_exception.py +52 -0
  149. capo_codedeploy/errors/invalid_on_premises_tag_combination_exception.py +56 -0
  150. capo_codedeploy/errors/invalid_operation_exception.py +52 -0
  151. capo_codedeploy/errors/invalid_registration_status_exception.py +54 -0
  152. capo_codedeploy/errors/invalid_revision_exception.py +52 -0
  153. capo_codedeploy/errors/invalid_role_exception.py +52 -0
  154. capo_codedeploy/errors/invalid_sort_by_exception.py +52 -0
  155. capo_codedeploy/errors/invalid_sort_order_exception.py +52 -0
  156. capo_codedeploy/errors/invalid_tag_exception.py +52 -0
  157. capo_codedeploy/errors/invalid_tag_filter_exception.py +52 -0
  158. capo_codedeploy/errors/invalid_tags_to_add_exception.py +52 -0
  159. capo_codedeploy/errors/invalid_target_filter_name_exception.py +54 -0
  160. capo_codedeploy/errors/invalid_target_group_pair_exception.py +54 -0
  161. capo_codedeploy/errors/invalid_target_instances_exception.py +54 -0
  162. capo_codedeploy/errors/invalid_time_range_exception.py +52 -0
  163. capo_codedeploy/errors/invalid_traffic_routing_configuration_exception.py +58 -0
  164. capo_codedeploy/errors/invalid_trigger_config_exception.py +54 -0
  165. capo_codedeploy/errors/invalid_update_outdated_instances_only_value_exception.py +60 -0
  166. capo_codedeploy/errors/invalid_zonal_deployment_configuration_exception.py +60 -0
  167. capo_codedeploy/errors/lifecycle_event_already_completed_exception.py +54 -0
  168. capo_codedeploy/errors/lifecycle_hook_limit_exceeded_exception.py +54 -0
  169. capo_codedeploy/errors/multiple_iam_arns_provided_exception.py +54 -0
  170. capo_codedeploy/errors/operation_not_supported_exception.py +54 -0
  171. capo_codedeploy/errors/resource_arn_required_exception.py +52 -0
  172. capo_codedeploy/errors/resource_validation_exception.py +52 -0
  173. capo_codedeploy/errors/revision_does_not_exist_exception.py +54 -0
  174. capo_codedeploy/errors/revision_required_exception.py +52 -0
  175. capo_codedeploy/errors/role_required_exception.py +52 -0
  176. capo_codedeploy/errors/tag_limit_exceeded_exception.py +52 -0
  177. capo_codedeploy/errors/tag_required_exception.py +52 -0
  178. capo_codedeploy/errors/tag_set_list_limit_exceeded_exception.py +54 -0
  179. capo_codedeploy/errors/throttling_exception.py +52 -0
  180. capo_codedeploy/errors/trigger_targets_limit_exceeded_exception.py +54 -0
  181. capo_codedeploy/errors/unsupported_action_for_deployment_type_exception.py +58 -0
  182. capo_codedeploy/py.typed +0 -0
  183. capo_codedeploy/types/_prelude/blob.py +12 -0
  184. capo_codedeploy/types/_prelude/timestamp.py +17 -0
  185. capo_codedeploy/types/add_tags_to_on_premises_instances_input.py +59 -0
  186. capo_codedeploy/types/additional_deployment_status_info.py +5 -0
  187. capo_codedeploy/types/alarm.py +28 -0
  188. capo_codedeploy/types/alarm_configuration.py +51 -0
  189. capo_codedeploy/types/alarm_list.py +29 -0
  190. capo_codedeploy/types/alarm_name.py +5 -0
  191. capo_codedeploy/types/app_spec_content.py +35 -0
  192. capo_codedeploy/types/application_id.py +5 -0
  193. capo_codedeploy/types/application_info.py +90 -0
  194. capo_codedeploy/types/application_name.py +5 -0
  195. capo_codedeploy/types/application_revision_sort_by.py +18 -0
  196. capo_codedeploy/types/applications_info_list.py +33 -0
  197. capo_codedeploy/types/applications_list.py +19 -0
  198. capo_codedeploy/types/arn.py +5 -0
  199. capo_codedeploy/types/auto_rollback_configuration.py +50 -0
  200. capo_codedeploy/types/auto_rollback_event.py +18 -0
  201. capo_codedeploy/types/auto_rollback_events_list.py +35 -0
  202. capo_codedeploy/types/auto_scaling_group.py +47 -0
  203. capo_codedeploy/types/auto_scaling_group_hook.py +5 -0
  204. capo_codedeploy/types/auto_scaling_group_list.py +35 -0
  205. capo_codedeploy/types/auto_scaling_group_name.py +5 -0
  206. capo_codedeploy/types/auto_scaling_group_name_list.py +19 -0
  207. capo_codedeploy/types/batch_get_application_revisions_input.py +55 -0
  208. capo_codedeploy/types/batch_get_application_revisions_output.py +56 -0
  209. capo_codedeploy/types/batch_get_applications_input.py +45 -0
  210. capo_codedeploy/types/batch_get_applications_output.py +42 -0
  211. capo_codedeploy/types/batch_get_deployment_groups_input.py +57 -0
  212. capo_codedeploy/types/batch_get_deployment_groups_output.py +49 -0
  213. capo_codedeploy/types/batch_get_deployment_instances_input.py +53 -0
  214. capo_codedeploy/types/batch_get_deployment_instances_output.py +49 -0
  215. capo_codedeploy/types/batch_get_deployment_targets_input.py +51 -0
  216. capo_codedeploy/types/batch_get_deployment_targets_output.py +42 -0
  217. capo_codedeploy/types/batch_get_deployments_input.py +43 -0
  218. capo_codedeploy/types/batch_get_deployments_output.py +42 -0
  219. capo_codedeploy/types/batch_get_on_premises_instances_input.py +45 -0
  220. capo_codedeploy/types/batch_get_on_premises_instances_output.py +42 -0
  221. capo_codedeploy/types/blue_green_deployment_configuration.py +84 -0
  222. capo_codedeploy/types/blue_instance_termination_option.py +46 -0
  223. capo_codedeploy/types/boolean.py +5 -0
  224. capo_codedeploy/types/bundle_type.py +20 -0
  225. capo_codedeploy/types/cloud_formation_resource_type.py +5 -0
  226. capo_codedeploy/types/cloud_formation_target.py +112 -0
  227. capo_codedeploy/types/commit_id.py +5 -0
  228. capo_codedeploy/types/compute_platform.py +18 -0
  229. capo_codedeploy/types/continue_deployment_input.py +49 -0
  230. capo_codedeploy/types/create_application_input.py +67 -0
  231. capo_codedeploy/types/create_application_output.py +28 -0
  232. capo_codedeploy/types/create_deployment_config_input.py +115 -0
  233. capo_codedeploy/types/create_deployment_config_output.py +30 -0
  234. capo_codedeploy/types/create_deployment_group_input.py +353 -0
  235. capo_codedeploy/types/create_deployment_group_output.py +30 -0
  236. capo_codedeploy/types/create_deployment_input.py +178 -0
  237. capo_codedeploy/types/create_deployment_output.py +28 -0
  238. capo_codedeploy/types/delete_application_input.py +31 -0
  239. capo_codedeploy/types/delete_deployment_config_input.py +35 -0
  240. capo_codedeploy/types/delete_deployment_group_input.py +45 -0
  241. capo_codedeploy/types/delete_deployment_group_output.py +42 -0
  242. capo_codedeploy/types/delete_git_hub_account_token_input.py +30 -0
  243. capo_codedeploy/types/delete_git_hub_account_token_output.py +30 -0
  244. capo_codedeploy/types/delete_resources_by_external_id_input.py +28 -0
  245. capo_codedeploy/types/delete_resources_by_external_id_output.py +18 -0
  246. capo_codedeploy/types/deployment_config_id.py +5 -0
  247. capo_codedeploy/types/deployment_config_info.py +134 -0
  248. capo_codedeploy/types/deployment_config_name.py +5 -0
  249. capo_codedeploy/types/deployment_configs_list.py +19 -0
  250. capo_codedeploy/types/deployment_creator.py +23 -0
  251. capo_codedeploy/types/deployment_group_id.py +5 -0
  252. capo_codedeploy/types/deployment_group_info.py +420 -0
  253. capo_codedeploy/types/deployment_group_info_list.py +35 -0
  254. capo_codedeploy/types/deployment_group_name.py +5 -0
  255. capo_codedeploy/types/deployment_groups_list.py +19 -0
  256. capo_codedeploy/types/deployment_id.py +5 -0
  257. capo_codedeploy/types/deployment_info.py +499 -0
  258. capo_codedeploy/types/deployment_option.py +17 -0
  259. capo_codedeploy/types/deployment_overview.py +64 -0
  260. capo_codedeploy/types/deployment_ready_action.py +17 -0
  261. capo_codedeploy/types/deployment_ready_option.py +50 -0
  262. capo_codedeploy/types/deployment_status.py +23 -0
  263. capo_codedeploy/types/deployment_status_list.py +33 -0
  264. capo_codedeploy/types/deployment_status_message_list.py +19 -0
  265. capo_codedeploy/types/deployment_style.py +61 -0
  266. capo_codedeploy/types/deployment_target.py +115 -0
  267. capo_codedeploy/types/deployment_target_list.py +33 -0
  268. capo_codedeploy/types/deployment_target_type.py +19 -0
  269. capo_codedeploy/types/deployment_type.py +17 -0
  270. capo_codedeploy/types/deployment_wait_type.py +17 -0
  271. capo_codedeploy/types/deployments_info_list.py +31 -0
  272. capo_codedeploy/types/deployments_list.py +17 -0
  273. capo_codedeploy/types/deregister_on_premises_instance_input.py +33 -0
  274. capo_codedeploy/types/description.py +5 -0
  275. capo_codedeploy/types/diagnostics.py +63 -0
  276. capo_codedeploy/types/duration.py +5 -0
  277. capo_codedeploy/types/e_tag.py +5 -0
  278. capo_codedeploy/types/ec2_tag_filter.py +52 -0
  279. capo_codedeploy/types/ec2_tag_filter_list.py +29 -0
  280. capo_codedeploy/types/ec2_tag_filter_type.py +18 -0
  281. capo_codedeploy/types/ec2_tag_set.py +42 -0
  282. capo_codedeploy/types/ec2_tag_set_list.py +35 -0
  283. capo_codedeploy/types/ecs_cluster_name.py +5 -0
  284. capo_codedeploy/types/ecs_service.py +35 -0
  285. capo_codedeploy/types/ecs_service_list.py +29 -0
  286. capo_codedeploy/types/ecs_service_name.py +5 -0
  287. capo_codedeploy/types/ecs_target.py +114 -0
  288. capo_codedeploy/types/ecs_task_set.py +111 -0
  289. capo_codedeploy/types/ecs_task_set_count.py +5 -0
  290. capo_codedeploy/types/ecs_task_set_identifier.py +5 -0
  291. capo_codedeploy/types/ecs_task_set_list.py +29 -0
  292. capo_codedeploy/types/ecs_task_set_status.py +5 -0
  293. capo_codedeploy/types/elb_info.py +28 -0
  294. capo_codedeploy/types/elb_info_list.py +29 -0
  295. capo_codedeploy/types/elb_name.py +5 -0
  296. capo_codedeploy/types/error_code.py +49 -0
  297. capo_codedeploy/types/error_information.py +43 -0
  298. capo_codedeploy/types/error_message.py +5 -0
  299. capo_codedeploy/types/external_id.py +5 -0
  300. capo_codedeploy/types/file_exists_behavior.py +18 -0
  301. capo_codedeploy/types/filter_value.py +5 -0
  302. capo_codedeploy/types/filter_value_list.py +17 -0
  303. capo_codedeploy/types/generic_revision_info.py +96 -0
  304. capo_codedeploy/types/get_application_input.py +31 -0
  305. capo_codedeploy/types/get_application_output.py +40 -0
  306. capo_codedeploy/types/get_application_revision_input.py +51 -0
  307. capo_codedeploy/types/get_application_revision_output.py +70 -0
  308. capo_codedeploy/types/get_deployment_config_input.py +35 -0
  309. capo_codedeploy/types/get_deployment_config_output.py +42 -0
  310. capo_codedeploy/types/get_deployment_group_input.py +43 -0
  311. capo_codedeploy/types/get_deployment_group_output.py +42 -0
  312. capo_codedeploy/types/get_deployment_input.py +31 -0
  313. capo_codedeploy/types/get_deployment_instance_input.py +39 -0
  314. capo_codedeploy/types/get_deployment_instance_output.py +42 -0
  315. capo_codedeploy/types/get_deployment_output.py +40 -0
  316. capo_codedeploy/types/get_deployment_target_input.py +39 -0
  317. capo_codedeploy/types/get_deployment_target_output.py +42 -0
  318. capo_codedeploy/types/get_on_premises_instance_input.py +31 -0
  319. capo_codedeploy/types/get_on_premises_instance_output.py +40 -0
  320. capo_codedeploy/types/git_hub_account_token_name.py +5 -0
  321. capo_codedeploy/types/git_hub_account_token_name_list.py +19 -0
  322. capo_codedeploy/types/git_hub_location.py +35 -0
  323. capo_codedeploy/types/green_fleet_provisioning_action.py +17 -0
  324. capo_codedeploy/types/green_fleet_provisioning_option.py +42 -0
  325. capo_codedeploy/types/iam_session_arn.py +5 -0
  326. capo_codedeploy/types/iam_user_arn.py +5 -0
  327. capo_codedeploy/types/instance_action.py +17 -0
  328. capo_codedeploy/types/instance_arn.py +5 -0
  329. capo_codedeploy/types/instance_count.py +5 -0
  330. capo_codedeploy/types/instance_id.py +5 -0
  331. capo_codedeploy/types/instance_info.py +95 -0
  332. capo_codedeploy/types/instance_info_list.py +29 -0
  333. capo_codedeploy/types/instance_name.py +5 -0
  334. capo_codedeploy/types/instance_name_list.py +17 -0
  335. capo_codedeploy/types/instance_status.py +22 -0
  336. capo_codedeploy/types/instance_status_list.py +31 -0
  337. capo_codedeploy/types/instance_summary.py +107 -0
  338. capo_codedeploy/types/instance_summary_list.py +33 -0
  339. capo_codedeploy/types/instance_target.py +112 -0
  340. capo_codedeploy/types/instance_type.py +17 -0
  341. capo_codedeploy/types/instance_type_list.py +29 -0
  342. capo_codedeploy/types/instances_list.py +17 -0
  343. capo_codedeploy/types/key.py +5 -0
  344. capo_codedeploy/types/lambda_function_alias.py +5 -0
  345. capo_codedeploy/types/lambda_function_info.py +69 -0
  346. capo_codedeploy/types/lambda_function_name.py +5 -0
  347. capo_codedeploy/types/lambda_target.py +114 -0
  348. capo_codedeploy/types/last_deployment_info.py +74 -0
  349. capo_codedeploy/types/lifecycle_error_code.py +21 -0
  350. capo_codedeploy/types/lifecycle_event.py +95 -0
  351. capo_codedeploy/types/lifecycle_event_hook_execution_id.py +5 -0
  352. capo_codedeploy/types/lifecycle_event_list.py +31 -0
  353. capo_codedeploy/types/lifecycle_event_name.py +5 -0
  354. capo_codedeploy/types/lifecycle_event_status.py +21 -0
  355. capo_codedeploy/types/lifecycle_message.py +5 -0
  356. capo_codedeploy/types/list_application_revisions_input.py +111 -0
  357. capo_codedeploy/types/list_application_revisions_output.py +49 -0
  358. capo_codedeploy/types/list_applications_input.py +28 -0
  359. capo_codedeploy/types/list_applications_output.py +49 -0
  360. capo_codedeploy/types/list_deployment_configs_input.py +28 -0
  361. capo_codedeploy/types/list_deployment_configs_output.py +49 -0
  362. capo_codedeploy/types/list_deployment_groups_input.py +40 -0
  363. capo_codedeploy/types/list_deployment_groups_output.py +58 -0
  364. capo_codedeploy/types/list_deployment_instances_input.py +82 -0
  365. capo_codedeploy/types/list_deployment_instances_output.py +47 -0
  366. capo_codedeploy/types/list_deployment_targets_input.py +57 -0
  367. capo_codedeploy/types/list_deployment_targets_output.py +45 -0
  368. capo_codedeploy/types/list_deployments_input.py +93 -0
  369. capo_codedeploy/types/list_deployments_output.py +47 -0
  370. capo_codedeploy/types/list_git_hub_account_token_names_input.py +28 -0
  371. capo_codedeploy/types/list_git_hub_account_token_names_output.py +49 -0
  372. capo_codedeploy/types/list_on_premises_instances_input.py +68 -0
  373. capo_codedeploy/types/list_on_premises_instances_output.py +49 -0
  374. capo_codedeploy/types/list_state_filter_action.py +18 -0
  375. capo_codedeploy/types/list_tags_for_resource_input.py +38 -0
  376. capo_codedeploy/types/list_tags_for_resource_output.py +43 -0
  377. capo_codedeploy/types/listener_arn.py +5 -0
  378. capo_codedeploy/types/listener_arn_list.py +17 -0
  379. capo_codedeploy/types/load_balancer_info.py +80 -0
  380. capo_codedeploy/types/log_tail.py +5 -0
  381. capo_codedeploy/types/message.py +5 -0
  382. capo_codedeploy/types/minimum_healthy_hosts.py +50 -0
  383. capo_codedeploy/types/minimum_healthy_hosts_per_zone.py +50 -0
  384. capo_codedeploy/types/minimum_healthy_hosts_per_zone_type.py +17 -0
  385. capo_codedeploy/types/minimum_healthy_hosts_per_zone_value.py +5 -0
  386. capo_codedeploy/types/minimum_healthy_hosts_type.py +17 -0
  387. capo_codedeploy/types/minimum_healthy_hosts_value.py +5 -0
  388. capo_codedeploy/types/next_token.py +5 -0
  389. capo_codedeploy/types/nullable_boolean.py +5 -0
  390. capo_codedeploy/types/on_premises_tag_set.py +42 -0
  391. capo_codedeploy/types/on_premises_tag_set_list.py +31 -0
  392. capo_codedeploy/types/outdated_instances_strategy.py +17 -0
  393. capo_codedeploy/types/percentage.py +5 -0
  394. capo_codedeploy/types/put_lifecycle_event_hook_execution_status_input.py +60 -0
  395. capo_codedeploy/types/put_lifecycle_event_hook_execution_status_output.py +32 -0
  396. capo_codedeploy/types/raw_string.py +35 -0
  397. capo_codedeploy/types/raw_string_content.py +5 -0
  398. capo_codedeploy/types/raw_string_sha256.py +5 -0
  399. capo_codedeploy/types/register_application_revision_input.py +58 -0
  400. capo_codedeploy/types/register_on_premises_instance_input.py +47 -0
  401. capo_codedeploy/types/registration_status.py +17 -0
  402. capo_codedeploy/types/related_deployments.py +55 -0
  403. capo_codedeploy/types/remove_tags_from_on_premises_instances_input.py +61 -0
  404. capo_codedeploy/types/repository.py +5 -0
  405. capo_codedeploy/types/revision_info.py +63 -0
  406. capo_codedeploy/types/revision_info_list.py +29 -0
  407. capo_codedeploy/types/revision_location.py +114 -0
  408. capo_codedeploy/types/revision_location_list.py +33 -0
  409. capo_codedeploy/types/revision_location_type.py +19 -0
  410. capo_codedeploy/types/role.py +5 -0
  411. capo_codedeploy/types/rollback_info.py +49 -0
  412. capo_codedeploy/types/s3_bucket.py +5 -0
  413. capo_codedeploy/types/s3_key.py +5 -0
  414. capo_codedeploy/types/s3_location.py +64 -0
  415. capo_codedeploy/types/script_name.py +5 -0
  416. capo_codedeploy/types/skip_wait_time_for_instance_termination_input.py +28 -0
  417. capo_codedeploy/types/sort_order.py +17 -0
  418. capo_codedeploy/types/stop_deployment_input.py +40 -0
  419. capo_codedeploy/types/stop_deployment_output.py +43 -0
  420. capo_codedeploy/types/stop_status.py +17 -0
  421. capo_codedeploy/types/tag.py +35 -0
  422. capo_codedeploy/types/tag_filter.py +50 -0
  423. capo_codedeploy/types/tag_filter_list.py +29 -0
  424. capo_codedeploy/types/tag_filter_type.py +18 -0
  425. capo_codedeploy/types/tag_key_list.py +17 -0
  426. capo_codedeploy/types/tag_list.py +29 -0
  427. capo_codedeploy/types/tag_resource_input.py +45 -0
  428. capo_codedeploy/types/tag_resource_output.py +18 -0
  429. capo_codedeploy/types/target_arn.py +5 -0
  430. capo_codedeploy/types/target_filter_name.py +17 -0
  431. capo_codedeploy/types/target_filters.py +40 -0
  432. capo_codedeploy/types/target_group_info.py +28 -0
  433. capo_codedeploy/types/target_group_info_list.py +33 -0
  434. capo_codedeploy/types/target_group_name.py +5 -0
  435. capo_codedeploy/types/target_group_pair_info.py +79 -0
  436. capo_codedeploy/types/target_group_pair_info_list.py +35 -0
  437. capo_codedeploy/types/target_id.py +5 -0
  438. capo_codedeploy/types/target_id_list.py +17 -0
  439. capo_codedeploy/types/target_instances.py +78 -0
  440. capo_codedeploy/types/target_label.py +17 -0
  441. capo_codedeploy/types/target_status.py +22 -0
  442. capo_codedeploy/types/time.py +20 -0
  443. capo_codedeploy/types/time_based_canary.py +37 -0
  444. capo_codedeploy/types/time_based_linear.py +37 -0
  445. capo_codedeploy/types/time_range.py +50 -0
  446. capo_codedeploy/types/timestamp.py +20 -0
  447. capo_codedeploy/types/traffic_route.py +42 -0
  448. capo_codedeploy/types/traffic_routing_config.py +80 -0
  449. capo_codedeploy/types/traffic_routing_type.py +18 -0
  450. capo_codedeploy/types/traffic_weight.py +5 -0
  451. capo_codedeploy/types/trigger_config.py +58 -0
  452. capo_codedeploy/types/trigger_config_list.py +31 -0
  453. capo_codedeploy/types/trigger_event_type.py +25 -0
  454. capo_codedeploy/types/trigger_event_type_list.py +35 -0
  455. capo_codedeploy/types/trigger_name.py +5 -0
  456. capo_codedeploy/types/trigger_target_arn.py +5 -0
  457. capo_codedeploy/types/untag_resource_input.py +47 -0
  458. capo_codedeploy/types/untag_resource_output.py +18 -0
  459. capo_codedeploy/types/update_application_input.py +38 -0
  460. capo_codedeploy/types/update_deployment_group_input.py +343 -0
  461. capo_codedeploy/types/update_deployment_group_output.py +42 -0
  462. capo_codedeploy/types/value.py +5 -0
  463. capo_codedeploy/types/version.py +5 -0
  464. capo_codedeploy/types/version_id.py +5 -0
  465. capo_codedeploy/types/wait_time_in_mins.py +5 -0
  466. capo_codedeploy/types/wait_time_in_seconds.py +5 -0
  467. capo_codedeploy/types/zonal_config.py +63 -0
  468. capo_codedeploy-0.1.0.dist-info/METADATA +102 -0
  469. capo_codedeploy-0.1.0.dist-info/RECORD +472 -0
  470. capo_codedeploy-0.1.0.dist-info/WHEEL +5 -0
  471. capo_codedeploy-0.1.0.dist-info/licenses/LICENSE +21 -0
  472. capo_codedeploy-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,88 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Any, Generic, TypeVar
5
+
6
+ from zapros import Request
7
+
8
+ from capo_codedeploy._auth._identity import Credentials, Identity
9
+ from capo_codedeploy._auth._providers import IdentityProvider
10
+ from capo_codedeploy._auth._sigv4 import SigV4AuthContext, sign_sigv4
11
+
12
+ IdentityT = TypeVar("IdentityT", bound="Identity")
13
+
14
+
15
+ class Signer(ABC, Generic[IdentityT]):
16
+ """Per-request request signer. Holds an IdentityProvider plus static config."""
17
+
18
+ def __init__(self, provider: IdentityProvider[IdentityT]) -> None:
19
+ self.provider = provider
20
+
21
+ @abstractmethod
22
+ async def asign(self, req: Request) -> Request: ...
23
+ @abstractmethod
24
+ def sign(self, req: Request) -> Request: ...
25
+
26
+
27
+ class SigV4Signer(Signer[Credentials]):
28
+ """aws.auth#sigv4 — AWS Signature Version 4.
29
+
30
+ The full auth scheme (``name`` variant, ``signingName``, ``signingRegion``,
31
+ encoding/normalization flags) is provided by the caller — either from the
32
+ endpoint rule-set's ``authSchemes`` property or built by the generated
33
+ ``get_signer`` from operation defaults.
34
+ """
35
+
36
+ def __init__(
37
+ self, provider: IdentityProvider[Credentials], *, auth_scheme: dict[str, Any]
38
+ ) -> None:
39
+ super().__init__(provider)
40
+ self._auth_scheme = auth_scheme
41
+
42
+ async def asign(self, req: Request) -> Request:
43
+ creds = await self.provider.aresolve_identity()
44
+ ctx: SigV4AuthContext = {
45
+ "type": "sig_v4",
46
+ "access_key_id": creds["access_key"],
47
+ "secret_access_key": creds["secret_key"],
48
+ "session_token": creds.get("session_token"),
49
+ "signing_region": self._auth_scheme["signingRegion"],
50
+ "signing_name": self._auth_scheme["signingName"],
51
+ "disable_double_encoding": self._auth_scheme.get(
52
+ "disableDoubleEncoding", False
53
+ ),
54
+ "disable_normalize_path": self._auth_scheme.get(
55
+ "disableNormalizePath", False
56
+ ),
57
+ }
58
+ if req.body is None:
59
+ body: bytes | None = b""
60
+ elif isinstance(req.body, bytes):
61
+ body = req.body
62
+ else:
63
+ body = None
64
+ return sign_sigv4(req, ctx, body)
65
+
66
+ def sign(self, req: Request) -> Request:
67
+ creds = self.provider.resolve_identity()
68
+ ctx: SigV4AuthContext = {
69
+ "type": "sig_v4",
70
+ "access_key_id": creds["access_key"],
71
+ "secret_access_key": creds["secret_key"],
72
+ "session_token": creds.get("session_token"),
73
+ "signing_region": self._auth_scheme["signingRegion"],
74
+ "signing_name": self._auth_scheme["signingName"],
75
+ "disable_double_encoding": self._auth_scheme.get(
76
+ "disableDoubleEncoding", False
77
+ ),
78
+ "disable_normalize_path": self._auth_scheme.get(
79
+ "disableNormalizePath", False
80
+ ),
81
+ }
82
+ if req.body is None:
83
+ body: bytes | None = b""
84
+ elif isinstance(req.body, bytes):
85
+ body = req.body
86
+ else:
87
+ body = None
88
+ return sign_sigv4(req, ctx, body)
@@ -0,0 +1,434 @@
1
+ """AWS Signature Version 4 — single-chunk signing.
2
+
3
+ Reference:
4
+ https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv-create-signed-request.html
5
+
6
+ Verified byte-for-byte against ``botocore.auth.S3SigV4Auth`` / ``SigV4Auth``
7
+ across S3 GET/PUT/POST, query-string, session-token, and non-S3 (IAM)
8
+ canonicalization paths.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import datetime as _dt
14
+ import functools
15
+ import hashlib
16
+ import hmac
17
+ import re
18
+ from typing import Any, Literal, TypedDict
19
+ from urllib.parse import quote, unquote
20
+
21
+ import zapros
22
+ from pywhatwgurl import URLSearchParams
23
+ from zapros import Headers, Request
24
+ from zapros._utils import get_host_header_value
25
+
26
+
27
+ def build_sigv4_auth_scheme(
28
+ signing_name: str, region: str | None, endpoint_scheme: dict[str, Any] | None = None
29
+ ) -> dict[str, Any] | None:
30
+ """Return the sigv4 auth scheme for ``signing_name``/``region``, with the
31
+ endpoint rule set's matching ``authSchemes`` entry overlaid on top.
32
+
33
+ The rule set only *modifies* signing properties of the resolved scheme
34
+ (Smithy rules engine, ``authSchemes``): keys it carries win, keys it omits
35
+ keep the operation defaults — IAM's global endpoint names only
36
+ ``signingRegion``, for instance. Returns None when no signing region is
37
+ known from either source.
38
+ """
39
+ scheme: dict[str, Any] = {
40
+ "name": "sigv4",
41
+ "signingName": signing_name,
42
+ "signingRegion": region,
43
+ "disableDoubleEncoding": False,
44
+ "disableNormalizePath": False,
45
+ }
46
+ if endpoint_scheme:
47
+ scheme.update(endpoint_scheme)
48
+ if scheme.get("signingRegion") is None:
49
+ return None
50
+ return scheme
51
+
52
+
53
+ class SigV4AuthContext(TypedDict):
54
+ type: Literal["sig_v4"]
55
+ access_key_id: str
56
+ secret_access_key: str
57
+ session_token: str | None
58
+ signing_region: str
59
+ signing_name: str
60
+ # Rules-engine sigv4 auth-scheme flags (False = standard SigV4: normalize
61
+ # dot segments, double-encode the path). S3-family endpoint rulesets set
62
+ # ``disableDoubleEncoding`` so the path is signed exactly as sent.
63
+ disable_double_encoding: bool
64
+ disable_normalize_path: bool
65
+
66
+
67
+ _SIGV4_ALGORITHM = "AWS4-HMAC-SHA256"
68
+ _EMPTY_PAYLOAD_SHA256 = hashlib.sha256(b"").hexdigest()
69
+
70
+ # Headers excluded from the signed-headers set. Mirrors botocore's denylist:
71
+ # these are hop-by-hop / mutable in transit, so signing them would break
72
+ # proxies or duplicate values already added by the transport layer.
73
+ _UNSIGNED_HEADERS = frozenset(
74
+ {
75
+ "authorization",
76
+ "cache-control",
77
+ "connection",
78
+ "expect",
79
+ "from",
80
+ "keep-alive",
81
+ "max-forwards",
82
+ "pragma",
83
+ "referer",
84
+ "te",
85
+ "trailer",
86
+ "transfer-encoding",
87
+ "upgrade",
88
+ "user-agent",
89
+ "x-amzn-trace-id",
90
+ "content-length",
91
+ "accept",
92
+ "accept-encoding",
93
+ }
94
+ )
95
+
96
+ _MULTI_SPACE = re.compile(r" +")
97
+
98
+ # Services that require the payload hash to travel in ``x-amz-content-sha256``.
99
+ # Other services sign the hash into the canonical request without sending it.
100
+ _S3_SIGNING_NAMES = frozenset({"s3", "s3express", "s3-outposts", "s3-object-lambda"})
101
+
102
+
103
+ def _uri_encode(value: str) -> str:
104
+ """RFC 3986 percent-encoding using only the unreserved set as safe."""
105
+ return quote(value, safe="-_.~")
106
+
107
+
108
+ def _remove_dot_segments(path: str) -> str:
109
+ """RFC 3986 §5.2.4 dot-segment removal (mirrors botocore's normalize_url_path)."""
110
+ segments: list[str] = []
111
+ for seg in path.split("/"):
112
+ if not seg or seg == ".":
113
+ continue
114
+ if seg == "..":
115
+ if segments:
116
+ segments.pop()
117
+ else:
118
+ segments.append(seg)
119
+ first = "/" if path.startswith("/") else ""
120
+ last = "/" if path.endswith("/") and segments else ""
121
+ return first + "/".join(segments) + last
122
+
123
+
124
+ def _canonical_path(path: str, *, double_encode: bool, normalize: bool) -> str:
125
+ """Build CanonicalURI.
126
+
127
+ Per the SigV4 spec, every segment is URI-encoded; standard services
128
+ normalize dot segments and URI-encode each segment **twice**. The
129
+ rules-engine ``disableNormalizePath`` / ``disableDoubleEncoding`` flags
130
+ turn those steps off — S3-family services set both, so the path is
131
+ signed exactly as provided.
132
+ """
133
+ if not path:
134
+ return "/"
135
+ if not path.startswith("/"):
136
+ path = "/" + path
137
+ if normalize:
138
+ path = _remove_dot_segments(path)
139
+ if not double_encode:
140
+ return path
141
+ decoded = unquote(path)
142
+ first = quote(decoded, safe="/~")
143
+ return quote(first, safe="/~")
144
+
145
+
146
+ def _canonical_query(query: str) -> str:
147
+ """Build CanonicalQueryString from a raw query string (with or without ``?``)."""
148
+ if not query:
149
+ return ""
150
+ if query.startswith("?"):
151
+ query = query[1:]
152
+ if not query:
153
+ return ""
154
+ sp = URLSearchParams(query)
155
+ encoded = sorted((_uri_encode(k), _uri_encode(v)) for k, v in sp.entries())
156
+ return "&".join(f"{k}={v}" for k, v in encoded)
157
+
158
+
159
+ def _trim_header_value(value: str) -> str:
160
+ """Trim leading/trailing whitespace and collapse internal whitespace runs.
161
+
162
+ Spec note: the canonical form treats sequential whitespace inside an
163
+ unquoted value as a single space. We do not parse quoted-string syntax;
164
+ the conservative collapse is correct for every header AWS actually signs.
165
+ """
166
+ return _MULTI_SPACE.sub(" ", value.strip())
167
+
168
+
169
+ def _canonical_headers(headers: Headers) -> tuple[str, str]:
170
+ """Return ``(canonical_headers, signed_headers)``."""
171
+ grouped: dict[str, list[str]] = {}
172
+ for name in headers:
173
+ lname = name.lower()
174
+ if lname in _UNSIGNED_HEADERS:
175
+ continue
176
+ grouped[lname] = [_trim_header_value(v) for v in headers.getall(name)]
177
+
178
+ signed = sorted(grouped)
179
+ canonical = "".join(f"{name}:{','.join(grouped[name])}\n" for name in signed)
180
+ return canonical, ";".join(signed)
181
+
182
+
183
+ def _build_canonical_request(
184
+ *,
185
+ method: str,
186
+ path: str,
187
+ query: str,
188
+ headers: Headers,
189
+ payload_hash: str,
190
+ double_encode: bool,
191
+ normalize: bool,
192
+ ) -> tuple[str, str]:
193
+ canonical_uri = _canonical_path(
194
+ path, double_encode=double_encode, normalize=normalize
195
+ )
196
+ canonical_query = _canonical_query(query)
197
+ canonical_headers, signed_headers = _canonical_headers(headers)
198
+ canonical_request = (
199
+ f"{method}\n"
200
+ f"{canonical_uri}\n"
201
+ f"{canonical_query}\n"
202
+ f"{canonical_headers}\n"
203
+ f"{signed_headers}\n"
204
+ f"{payload_hash}"
205
+ )
206
+ return canonical_request, signed_headers
207
+
208
+
209
+ @functools.lru_cache(maxsize=8)
210
+ def _derive_signing_key(secret: str, date: str, region: str, service: str) -> bytes:
211
+ # The key depends only on the UTC date stamp (not the full timestamp),
212
+ # so it is reused for every request in the same day/region/service.
213
+ k_date = hmac.new(
214
+ b"AWS4" + secret.encode("utf-8"), date.encode("ascii"), hashlib.sha256
215
+ ).digest()
216
+ k_region = hmac.new(k_date, region.encode("utf-8"), hashlib.sha256).digest()
217
+ k_service = hmac.new(k_region, service.encode("utf-8"), hashlib.sha256).digest()
218
+ return hmac.new(k_service, b"aws4_request", hashlib.sha256).digest()
219
+
220
+
221
+ def _amz_now() -> _dt.datetime:
222
+ return _dt.datetime.now(_dt.timezone.utc)
223
+
224
+
225
+ def _canonical_query_from_pairs(pairs: list[tuple[str, str]]) -> str:
226
+ """CanonicalQueryString from raw (unencoded) key/value pairs."""
227
+ encoded = sorted((_uri_encode(k), _uri_encode(v)) for k, v in pairs)
228
+ return "&".join(f"{k}={v}" for k, v in encoded)
229
+
230
+
231
+ def sign_sigv4(
232
+ request: Request,
233
+ ctx: SigV4AuthContext,
234
+ body: bytes | None,
235
+ ) -> Request:
236
+ """Return a new ``Request`` carrying SigV4 single-chunk auth headers.
237
+
238
+ Pass ``body=None`` to sign with ``UNSIGNED-PAYLOAD`` (streaming requests).
239
+ The original ``request.body`` is forwarded unchanged in that case.
240
+ """
241
+ service = ctx["signing_name"]
242
+ region = ctx["signing_region"]
243
+
244
+ headers = request.headers.copy()
245
+
246
+ # X-Amz-Date — honor caller-supplied value (allows deterministic tests).
247
+ existing_date = headers.get("X-Amz-Date")
248
+ if existing_date:
249
+ amz_date = existing_date
250
+ date_stamp = amz_date[:8]
251
+ else:
252
+ now = _amz_now()
253
+ amz_date = now.strftime("%Y%m%dT%H%M%SZ")
254
+ date_stamp = now.strftime("%Y%m%d")
255
+ headers["X-Amz-Date"] = amz_date
256
+
257
+ # Payload hash. For S3-family services, x-amz-content-sha256 is mandatory
258
+ # and must be set BEFORE computing the canonical request (it gets signed).
259
+ payload_hash = headers.get("X-Amz-Content-SHA256")
260
+ if payload_hash is None:
261
+ if body is None:
262
+ payload_hash = "UNSIGNED-PAYLOAD"
263
+ else:
264
+ payload_hash = (
265
+ hashlib.sha256(body).hexdigest() if body else _EMPTY_PAYLOAD_SHA256
266
+ )
267
+ if service in _S3_SIGNING_NAMES:
268
+ headers["X-Amz-Content-SHA256"] = payload_hash
269
+
270
+ # Session token (STS / assumed-role credentials).
271
+ session_token = ctx.get("session_token")
272
+ if session_token:
273
+ headers["X-Amz-Security-Token"] = session_token
274
+
275
+ # Host header is added by Request.__init__ from the URL; defensive fallback.
276
+ if "host" not in headers and request.url.hostname:
277
+ headers["Host"] = get_host_header_value(request.url)
278
+
279
+ canonical_request, signed_headers = _build_canonical_request(
280
+ method=request.method.upper(),
281
+ path=request.url.pathname,
282
+ query=request.url.search,
283
+ headers=headers,
284
+ payload_hash=payload_hash,
285
+ double_encode=not ctx["disable_double_encoding"],
286
+ normalize=not ctx["disable_normalize_path"],
287
+ )
288
+
289
+ credential_scope = f"{date_stamp}/{region}/{service}/aws4_request"
290
+ string_to_sign = (
291
+ f"{_SIGV4_ALGORITHM}\n"
292
+ f"{amz_date}\n"
293
+ f"{credential_scope}\n"
294
+ f"{hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()}"
295
+ )
296
+
297
+ signing_key = _derive_signing_key(
298
+ ctx["secret_access_key"], date_stamp, region, service
299
+ )
300
+ signature = hmac.new(
301
+ signing_key, string_to_sign.encode("utf-8"), hashlib.sha256
302
+ ).hexdigest()
303
+
304
+ headers["Authorization"] = (
305
+ f"{_SIGV4_ALGORITHM} "
306
+ f"Credential={ctx['access_key_id']}/{credential_scope},"
307
+ f"SignedHeaders={signed_headers},"
308
+ f"Signature={signature}"
309
+ )
310
+
311
+ effective_body = body if body is not None else request.body
312
+ if effective_body is not None:
313
+ return Request(
314
+ request.url,
315
+ request.method,
316
+ headers,
317
+ body=effective_body,
318
+ context=request.context,
319
+ )
320
+ return Request(request.url, request.method, headers, context=request.context)
321
+
322
+
323
+ def presign_sigv4(
324
+ request: Request,
325
+ ctx: SigV4AuthContext,
326
+ *,
327
+ expires_in: int = 3600,
328
+ now: _dt.datetime | None = None,
329
+ ) -> Request:
330
+ """Return a new ``Request`` whose URL carries SigV4 query-string auth.
331
+
332
+ The signature travels in the URL (``X-Amz-*`` query params), so the result
333
+ is usable standalone (browser, curl). Payload is signed as
334
+ ``UNSIGNED-PAYLOAD``, so the body is not bound by the signature.
335
+
336
+ ``expires_in`` is the validity window in seconds; range 1..604800 (7 days),
337
+ bounded by the max lifetime of the derived signing key.
338
+ """
339
+ if not 1 <= expires_in <= 604800:
340
+ raise ValueError(f"expires_in must be in [1, 604800], got {expires_in}")
341
+
342
+ sign_time = now or _amz_now()
343
+ if sign_time.tzinfo is None:
344
+ raise ValueError("now must be timezone-aware (UTC)")
345
+
346
+ service = ctx["signing_name"]
347
+ region = ctx["signing_region"]
348
+
349
+ amz_date = sign_time.strftime("%Y%m%dT%H%M%SZ")
350
+ date_stamp = sign_time.strftime("%Y%m%d")
351
+ credential_scope = f"{date_stamp}/{region}/{service}/aws4_request"
352
+
353
+ # Canonical headers: host is mandatory; anything else already on the request
354
+ # is signed too (and must then be sent alongside the URL). Strip the headers
355
+ # that belong in the query string to avoid header/query value conflicts
356
+ # (which AWS rejects as InvalidRequest).
357
+ headers = request.headers.copy()
358
+ for h in (
359
+ "Authorization",
360
+ "X-Amz-Date",
361
+ "X-Amz-Content-SHA256",
362
+ "X-Amz-Security-Token",
363
+ ):
364
+ if h in headers:
365
+ del headers[h]
366
+ if "host" not in headers and request.url.hostname:
367
+ headers["Host"] = get_host_header_value(request.url)
368
+
369
+ canonical_headers, signed_headers = _canonical_headers(headers)
370
+
371
+ # Signed query params (raw values). X-Amz-Signature is appended afterwards.
372
+ amz_params: list[tuple[str, str]] = [
373
+ ("X-Amz-Algorithm", _SIGV4_ALGORITHM),
374
+ ("X-Amz-Credential", f"{ctx['access_key_id']}/{credential_scope}"),
375
+ ("X-Amz-Date", amz_date),
376
+ ("X-Amz-Expires", str(expires_in)),
377
+ ("X-Amz-SignedHeaders", signed_headers),
378
+ ]
379
+ session_token = ctx.get("session_token")
380
+ if session_token:
381
+ # S3 and most services require the token inside the canonical query.
382
+ # (A few — e.g. iotdevicegateway — want it appended post-signature
383
+ # instead; handle those as a special case if you ever target them.)
384
+ amz_params.append(("X-Amz-Security-Token", session_token))
385
+
386
+ existing = list(URLSearchParams(request.url.search).entries())
387
+ canonical_query = _canonical_query_from_pairs(existing + amz_params)
388
+
389
+ canonical_uri = _canonical_path(
390
+ request.url.pathname,
391
+ double_encode=not ctx["disable_double_encoding"],
392
+ normalize=not ctx["disable_normalize_path"],
393
+ )
394
+ canonical_request = (
395
+ f"{request.method.upper()}\n"
396
+ f"{canonical_uri}\n"
397
+ f"{canonical_query}\n"
398
+ f"{canonical_headers}\n"
399
+ f"{signed_headers}\n"
400
+ f"UNSIGNED-PAYLOAD"
401
+ )
402
+
403
+ string_to_sign = (
404
+ f"{_SIGV4_ALGORITHM}\n"
405
+ f"{amz_date}\n"
406
+ f"{credential_scope}\n"
407
+ f"{hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()}"
408
+ )
409
+ signing_key = _derive_signing_key(
410
+ ctx["secret_access_key"], date_stamp, region, service
411
+ )
412
+ signature = hmac.new(
413
+ signing_key, string_to_sign.encode("utf-8"), hashlib.sha256
414
+ ).hexdigest()
415
+
416
+ # X-Amz-Signature is hex (no encoding needed) and is NOT part of the
417
+ # canonical query. The sorted canonical query doubles as the URL query.
418
+ final_query = f"{canonical_query}&X-Amz-Signature={signature}"
419
+
420
+ url = request.url
421
+ fragment = url.hash or ""
422
+ presigned_href = zapros.URL(
423
+ f"{url.protocol}//{url.host}{url.pathname}?{final_query}{fragment}"
424
+ )
425
+
426
+ if request.body is not None:
427
+ return Request(
428
+ presigned_href,
429
+ request.method,
430
+ headers,
431
+ body=request.body,
432
+ context=request.context,
433
+ )
434
+ return Request(presigned_href, request.method, headers, context=request.context)
@@ -0,0 +1,80 @@
1
+ from typing import Optional, cast
2
+
3
+ from zapros import (
4
+ AsyncBaseHandler,
5
+ AsyncBaseMiddleware,
6
+ AsyncSyncMismatchError,
7
+ BaseHandler,
8
+ BaseMiddleware,
9
+ Request,
10
+ )
11
+ from zapros._models import Response
12
+
13
+ from ._signers import Signer
14
+
15
+
16
+ def ensure_async_handler(
17
+ handler: AsyncBaseHandler | BaseHandler,
18
+ ) -> AsyncBaseHandler:
19
+ if isinstance(handler, AsyncBaseHandler):
20
+ return handler
21
+ raise AsyncSyncMismatchError(
22
+ "Handler was expected to be an AsyncBaseHandler, but it is not."
23
+ )
24
+
25
+
26
+ def ensure_sync_handler(
27
+ handler: AsyncBaseHandler | BaseHandler,
28
+ ) -> BaseHandler:
29
+ if isinstance(handler, BaseHandler):
30
+ return handler
31
+ raise AsyncSyncMismatchError(
32
+ "Handler was expected to be a BaseHandler, but it is not."
33
+ )
34
+
35
+
36
+ def strip_accept_encoding(request: Request) -> Request:
37
+ """Drop the ``Accept-Encoding`` zapros adds to every request.
38
+
39
+ Like the AWS SDKs, the client never asks for transport compression: a
40
+ response body must be the service's actual bytes so ``Content-Length``,
41
+ checksums and ``Range`` stay coherent and a ``Content-Encoding`` on it is
42
+ always stored metadata (an S3 object's), never something to undo. Removing
43
+ the header before signing also keeps it out of the canonical request, where
44
+ a proxy rewriting it in flight would break the signature.
45
+ """
46
+ if "accept-encoding" in request.headers:
47
+ del request.headers["Accept-Encoding"]
48
+ return request
49
+
50
+
51
+ class AuthMiddleware(BaseMiddleware, AsyncBaseMiddleware):
52
+ """Sign the outgoing request using the Signer placed in ``context["signer"]``.
53
+
54
+ The generated ``get_signer`` in each operation module resolves the
55
+ effective auth scheme to a concrete :class:`Signer` and threads it
56
+ through ``request.context`` before dispatch. This middleware does the
57
+ actual signing; ``None`` means the operation opted out via
58
+ ``@optionalAuth`` and the request passes through unsigned. Either way
59
+ ``Accept-Encoding`` is stripped first (see :func:`strip_accept_encoding`).
60
+ """
61
+
62
+ def __init__(self, next_handler: BaseHandler | AsyncBaseHandler) -> None:
63
+ self.next = cast(BaseHandler, next_handler)
64
+ self.async_next = cast(AsyncBaseHandler, next_handler)
65
+
66
+ def handle(self, request: Request) -> Response:
67
+ next_handler = ensure_sync_handler(self.next)
68
+ request = strip_accept_encoding(request)
69
+ signer = cast(Optional[Signer], request.context.get("signer"))
70
+ if signer is not None:
71
+ request = signer.sign(request)
72
+ return next_handler.handle(request)
73
+
74
+ async def ahandle(self, request: Request) -> Response:
75
+ next_handler = ensure_async_handler(self.async_next)
76
+ request = strip_accept_encoding(request)
77
+ signer = cast(Optional[Signer], request.context.get("signer"))
78
+ if signer is not None:
79
+ request = await signer.asign(request)
80
+ return await next_handler.ahandle(request)