subnoto-api-client 0.0.99__py3-none-manylinux2014_x86_64.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.
- oak_py_sdk/oak_client.so +0 -0
- subnoto_api_client/__init__.py +23 -0
- subnoto_api_client/client.py +155 -0
- subnoto_api_client/generated/__init__.py +8 -0
- subnoto_api_client/generated/api/__init__.py +1 -0
- subnoto_api_client/generated/api/contact/__init__.py +1 -0
- subnoto_api_client/generated/api/contact/post_public_contact_create.py +241 -0
- subnoto_api_client/generated/api/contact/post_public_contact_delete.py +241 -0
- subnoto_api_client/generated/api/contact/post_public_contact_get.py +241 -0
- subnoto_api_client/generated/api/contact/post_public_contact_list.py +241 -0
- subnoto_api_client/generated/api/contact/post_public_contact_update.py +241 -0
- subnoto_api_client/generated/api/envelope/__init__.py +1 -0
- subnoto_api_client/generated/api/envelope/post_public_envelope_add_blocks.py +243 -0
- subnoto_api_client/generated/api/envelope/post_public_envelope_add_recipients.py +253 -0
- subnoto_api_client/generated/api/envelope/post_public_envelope_complete_document_upload.py +257 -0
- subnoto_api_client/generated/api/envelope/post_public_envelope_create.py +253 -0
- subnoto_api_client/generated/api/envelope/post_public_envelope_create_from_template.py +257 -0
- subnoto_api_client/generated/api/envelope/post_public_envelope_delete.py +241 -0
- subnoto_api_client/generated/api/envelope/post_public_envelope_delete_blocks.py +253 -0
- subnoto_api_client/generated/api/envelope/post_public_envelope_delete_recipients.py +253 -0
- subnoto_api_client/generated/api/envelope/post_public_envelope_get.py +241 -0
- subnoto_api_client/generated/api/envelope/post_public_envelope_get_document.py +247 -0
- subnoto_api_client/generated/api/envelope/post_public_envelope_get_proof.py +237 -0
- subnoto_api_client/generated/api/envelope/post_public_envelope_list.py +227 -0
- subnoto_api_client/generated/api/envelope/post_public_envelope_send.py +241 -0
- subnoto_api_client/generated/api/envelope/post_public_envelope_sign.py +241 -0
- subnoto_api_client/generated/api/template/__init__.py +1 -0
- subnoto_api_client/generated/api/template/post_public_template_list.py +227 -0
- subnoto_api_client/generated/api/utils/__init__.py +1 -0
- subnoto_api_client/generated/api/utils/post_public_utils_whoami.py +227 -0
- subnoto_api_client/generated/api/workspace/__init__.py +1 -0
- subnoto_api_client/generated/api/workspace/post_public_workspace_list.py +227 -0
- subnoto_api_client/generated/client.py +282 -0
- subnoto_api_client/generated/errors.py +16 -0
- subnoto_api_client/generated/models/__init__.py +1271 -0
- subnoto_api_client/generated/models/post_public_contact_create_body.py +89 -0
- subnoto_api_client/generated/models/post_public_contact_create_body_contacts_item.py +88 -0
- subnoto_api_client/generated/models/post_public_contact_create_response_200.py +62 -0
- subnoto_api_client/generated/models/post_public_contact_create_response_200_contacts_item.py +80 -0
- subnoto_api_client/generated/models/post_public_contact_create_response_400.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_create_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_create_response_400_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_contact_create_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_create_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_create_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_contact_create_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_create_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_create_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_contact_create_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_create_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_create_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_contact_delete_body.py +69 -0
- subnoto_api_client/generated/models/post_public_contact_delete_response_200.py +24 -0
- subnoto_api_client/generated/models/post_public_contact_delete_response_400.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_delete_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_delete_response_400_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_contact_delete_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_delete_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_delete_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_contact_delete_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_delete_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_delete_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_contact_delete_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_delete_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_delete_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_contact_get_body.py +69 -0
- subnoto_api_client/generated/models/post_public_contact_get_response_200.py +52 -0
- subnoto_api_client/generated/models/post_public_contact_get_response_200_contact.py +80 -0
- subnoto_api_client/generated/models/post_public_contact_get_response_400.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_get_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_get_response_400_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_contact_get_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_get_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_get_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_contact_get_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_get_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_get_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_contact_get_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_get_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_get_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_contact_list_body.py +61 -0
- subnoto_api_client/generated/models/post_public_contact_list_response_200.py +62 -0
- subnoto_api_client/generated/models/post_public_contact_list_response_200_contacts_item.py +80 -0
- subnoto_api_client/generated/models/post_public_contact_list_response_400.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_list_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_list_response_400_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_contact_list_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_list_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_list_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_contact_list_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_list_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_list_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_contact_list_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_list_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_list_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_contact_update_body.py +79 -0
- subnoto_api_client/generated/models/post_public_contact_update_body_contact.py +91 -0
- subnoto_api_client/generated/models/post_public_contact_update_response_200.py +52 -0
- subnoto_api_client/generated/models/post_public_contact_update_response_200_contact.py +80 -0
- subnoto_api_client/generated/models/post_public_contact_update_response_400.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_update_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_update_response_400_error_code.py +11 -0
- subnoto_api_client/generated/models/post_public_contact_update_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_update_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_update_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_contact_update_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_update_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_update_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_contact_update_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_contact_update_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_contact_update_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_body.py +174 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_body_blocks_item_type_0.py +153 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_body_blocks_item_type_0_templated_text.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_body_blocks_item_type_0_type.py +8 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_body_blocks_item_type_1.py +133 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_body_blocks_item_type_1_type.py +8 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_body_blocks_item_type_2.py +117 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_body_blocks_item_type_2_type.py +8 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_response_200.py +42 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_response_400.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_response_400_error_code.py +13 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_add_blocks_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_body.py +171 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_body_recipients_item_type_0.py +91 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_body_recipients_item_type_0_type.py +8 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_body_recipients_item_type_1.py +75 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_body_recipients_item_type_1_type.py +8 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_body_recipients_item_type_2.py +75 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_body_recipients_item_type_2_type.py +8 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_200.py +64 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_200_recipients_item.py +93 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_200_recipients_item_role.py +8 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_200_recipients_item_status.py +12 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_400.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_400_error_code.py +14 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_401.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_403.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_500.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_add_recipients_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_complete_document_upload_body.py +77 -0
- subnoto_api_client/generated/models/post_public_envelope_complete_document_upload_response_200.py +24 -0
- subnoto_api_client/generated/models/post_public_envelope_complete_document_upload_response_400.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_complete_document_upload_response_400_error.py +72 -0
- subnoto_api_client/generated/models/post_public_envelope_complete_document_upload_response_400_error_code.py +14 -0
- subnoto_api_client/generated/models/post_public_envelope_complete_document_upload_response_401.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_complete_document_upload_response_401_error.py +72 -0
- subnoto_api_client/generated/models/post_public_envelope_complete_document_upload_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_complete_document_upload_response_403.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_complete_document_upload_response_403_error.py +72 -0
- subnoto_api_client/generated/models/post_public_envelope_complete_document_upload_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_complete_document_upload_response_500.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_complete_document_upload_response_500_error.py +72 -0
- subnoto_api_client/generated/models/post_public_envelope_complete_document_upload_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_create_body.py +81 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_body.py +170 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_body_recipients_item_type_0.py +99 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_body_recipients_item_type_0_type.py +8 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_body_recipients_item_type_1.py +83 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_body_recipients_item_type_1_type.py +8 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_body_recipients_item_type_2.py +83 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_body_recipients_item_type_2_type.py +8 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_response_200.py +50 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_response_400.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_response_400_error_code.py +13 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_response_401.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_response_403.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_response_500.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_create_from_template_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_200.py +95 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_200_presigned_s3_params.py +63 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_200_presigned_s3_params_fields.py +99 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_400.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_400_error_code.py +12 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_create_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_blocks_body.py +85 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_blocks_response_200.py +42 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_blocks_response_400.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_blocks_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_blocks_response_400_error_code.py +12 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_blocks_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_blocks_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_blocks_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_blocks_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_blocks_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_blocks_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_blocks_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_blocks_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_blocks_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_body.py +69 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_body.py +99 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_body_recipients_item.py +61 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_response_200.py +24 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_response_400.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_response_400_error_code.py +12 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_response_401.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_response_403.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_response_500.py +86 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_recipients_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_response_200.py +24 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_response_400.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_response_400_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_delete_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_get_body.py +69 -0
- subnoto_api_client/generated/models/post_public_envelope_get_document_body.py +77 -0
- subnoto_api_client/generated/models/post_public_envelope_get_document_response_400.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_get_document_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_get_document_response_400_error_code.py +13 -0
- subnoto_api_client/generated/models/post_public_envelope_get_document_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_get_document_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_get_document_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_get_document_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_get_document_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_get_document_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_get_document_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_get_document_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_get_document_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_get_proof_body.py +69 -0
- subnoto_api_client/generated/models/post_public_envelope_get_proof_response_400.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_get_proof_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_get_proof_response_400_error_code.py +13 -0
- subnoto_api_client/generated/models/post_public_envelope_get_proof_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_get_proof_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_get_proof_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_get_proof_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_get_proof_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_get_proof_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_get_proof_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_get_proof_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_get_proof_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200.py +170 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item.py +106 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item_blocks.py +162 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item_blocks_additional_property_item_type_0.py +236 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item_blocks_additional_property_item_type_0_color.py +15 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item_blocks_additional_property_item_type_0_label_icon.py +14 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item_blocks_additional_property_item_type_0_templated_text.py +12 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item_blocks_additional_property_item_type_0_type.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item_blocks_additional_property_item_type_1.py +207 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item_blocks_additional_property_item_type_1_color.py +15 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item_blocks_additional_property_item_type_1_label_icon.py +14 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item_blocks_additional_property_item_type_1_type.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item_blocks_additional_property_item_type_2.py +191 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item_blocks_additional_property_item_type_2_color.py +15 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item_blocks_additional_property_item_type_2_label_icon.py +14 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_documents_item_blocks_additional_property_item_type_2_type.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_metrics.py +66 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_owner.py +93 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_sender.py +51 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_200_status.py +14 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_400.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_400_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_get_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_list_body.py +82 -0
- subnoto_api_client/generated/models/post_public_envelope_list_response_200.py +62 -0
- subnoto_api_client/generated/models/post_public_envelope_list_response_200_envelopes_item.py +144 -0
- subnoto_api_client/generated/models/post_public_envelope_list_response_200_envelopes_item_metrics.py +67 -0
- subnoto_api_client/generated/models/post_public_envelope_list_response_200_envelopes_item_owner.py +79 -0
- subnoto_api_client/generated/models/post_public_envelope_list_response_200_envelopes_item_status.py +14 -0
- subnoto_api_client/generated/models/post_public_envelope_list_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_list_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_list_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_list_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_list_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_list_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_list_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_list_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_list_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_send_body.py +90 -0
- subnoto_api_client/generated/models/post_public_envelope_send_response_200.py +24 -0
- subnoto_api_client/generated/models/post_public_envelope_send_response_400.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_send_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_send_response_400_error_code.py +15 -0
- subnoto_api_client/generated/models/post_public_envelope_send_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_send_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_send_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_send_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_send_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_send_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_send_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_send_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_send_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_sign_body.py +88 -0
- subnoto_api_client/generated/models/post_public_envelope_sign_response_200.py +58 -0
- subnoto_api_client/generated/models/post_public_envelope_sign_response_400.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_sign_response_400_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_sign_response_400_error_code.py +16 -0
- subnoto_api_client/generated/models/post_public_envelope_sign_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_sign_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_sign_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_envelope_sign_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_sign_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_sign_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_envelope_sign_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_envelope_sign_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_envelope_sign_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_template_list_body.py +52 -0
- subnoto_api_client/generated/models/post_public_template_list_response_200.py +62 -0
- subnoto_api_client/generated/models/post_public_template_list_response_200_templates_item.py +174 -0
- subnoto_api_client/generated/models/post_public_template_list_response_200_templates_item_documents_item.py +50 -0
- subnoto_api_client/generated/models/post_public_template_list_response_200_templates_item_owner.py +79 -0
- subnoto_api_client/generated/models/post_public_template_list_response_200_templates_item_recipients_item.py +42 -0
- subnoto_api_client/generated/models/post_public_template_list_response_200_templates_item_status.py +11 -0
- subnoto_api_client/generated/models/post_public_template_list_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_template_list_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_template_list_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_template_list_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_template_list_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_template_list_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_template_list_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_template_list_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_template_list_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_utils_whoami_body.py +46 -0
- subnoto_api_client/generated/models/post_public_utils_whoami_response_200.py +74 -0
- subnoto_api_client/generated/models/post_public_utils_whoami_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_utils_whoami_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_utils_whoami_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_utils_whoami_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_utils_whoami_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_utils_whoami_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_utils_whoami_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_utils_whoami_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_utils_whoami_response_500_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_workspace_list_body.py +46 -0
- subnoto_api_client/generated/models/post_public_workspace_list_response_200.py +64 -0
- subnoto_api_client/generated/models/post_public_workspace_list_response_200_workspaces_item.py +74 -0
- subnoto_api_client/generated/models/post_public_workspace_list_response_401.py +84 -0
- subnoto_api_client/generated/models/post_public_workspace_list_response_401_error.py +70 -0
- subnoto_api_client/generated/models/post_public_workspace_list_response_401_error_code.py +10 -0
- subnoto_api_client/generated/models/post_public_workspace_list_response_403.py +84 -0
- subnoto_api_client/generated/models/post_public_workspace_list_response_403_error.py +70 -0
- subnoto_api_client/generated/models/post_public_workspace_list_response_403_error_code.py +9 -0
- subnoto_api_client/generated/models/post_public_workspace_list_response_500.py +84 -0
- subnoto_api_client/generated/models/post_public_workspace_list_response_500_error.py +70 -0
- subnoto_api_client/generated/models/post_public_workspace_list_response_500_error_code.py +9 -0
- subnoto_api_client/generated/types.py +54 -0
- subnoto_api_client/middleware/__init__.py +15 -0
- subnoto_api_client/middleware/signature_utils.py +128 -0
- subnoto_api_client/middleware/tunnel.py +119 -0
- subnoto_api_client/session.py +239 -0
- subnoto_api_client/transport.py +156 -0
- subnoto_api_client/types.py +43 -0
- subnoto_api_client-0.0.99.dist-info/METADATA +81 -0
- subnoto_api_client-0.0.99.dist-info/RECORD +399 -0
- subnoto_api_client-0.0.99.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from typing import Any, TypeVar
|
|
5
|
+
|
|
6
|
+
from attrs import define as _attrs_define
|
|
7
|
+
|
|
8
|
+
from ..models.post_public_workspace_list_response_500_error_code import (
|
|
9
|
+
PostPublicWorkspaceListResponse500ErrorCode,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
T = TypeVar("T", bound="PostPublicWorkspaceListResponse500Error")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@_attrs_define
|
|
16
|
+
class PostPublicWorkspaceListResponse500Error:
|
|
17
|
+
"""
|
|
18
|
+
Attributes:
|
|
19
|
+
code (PostPublicWorkspaceListResponse500ErrorCode): The error code
|
|
20
|
+
message (str): The error message
|
|
21
|
+
suggestion (str): A suggestion to resolve the error
|
|
22
|
+
documentation_url (str): A URL to the documentation
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
code: PostPublicWorkspaceListResponse500ErrorCode
|
|
26
|
+
message: str
|
|
27
|
+
suggestion: str
|
|
28
|
+
documentation_url: str
|
|
29
|
+
|
|
30
|
+
def to_dict(self) -> dict[str, Any]:
|
|
31
|
+
code = self.code.value
|
|
32
|
+
|
|
33
|
+
message = self.message
|
|
34
|
+
|
|
35
|
+
suggestion = self.suggestion
|
|
36
|
+
|
|
37
|
+
documentation_url = self.documentation_url
|
|
38
|
+
|
|
39
|
+
field_dict: dict[str, Any] = {}
|
|
40
|
+
|
|
41
|
+
field_dict.update(
|
|
42
|
+
{
|
|
43
|
+
"code": code,
|
|
44
|
+
"message": message,
|
|
45
|
+
"suggestion": suggestion,
|
|
46
|
+
"documentationUrl": documentation_url,
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return field_dict
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
|
|
54
|
+
d = dict(src_dict)
|
|
55
|
+
code = PostPublicWorkspaceListResponse500ErrorCode(d.pop("code"))
|
|
56
|
+
|
|
57
|
+
message = d.pop("message")
|
|
58
|
+
|
|
59
|
+
suggestion = d.pop("suggestion")
|
|
60
|
+
|
|
61
|
+
documentation_url = d.pop("documentationUrl")
|
|
62
|
+
|
|
63
|
+
post_public_workspace_list_response_500_error = cls(
|
|
64
|
+
code=code,
|
|
65
|
+
message=message,
|
|
66
|
+
suggestion=suggestion,
|
|
67
|
+
documentation_url=documentation_url,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
return post_public_workspace_list_response_500_error
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Contains some shared types for properties"""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping, MutableMapping
|
|
4
|
+
from http import HTTPStatus
|
|
5
|
+
from typing import IO, BinaryIO, Generic, Literal, TypeVar
|
|
6
|
+
|
|
7
|
+
from attrs import define
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Unset:
|
|
11
|
+
def __bool__(self) -> Literal[False]:
|
|
12
|
+
return False
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
UNSET: Unset = Unset()
|
|
16
|
+
|
|
17
|
+
# The types that `httpx.Client(files=)` can accept, copied from that library.
|
|
18
|
+
FileContent = IO[bytes] | bytes | str
|
|
19
|
+
FileTypes = (
|
|
20
|
+
# (filename, file (or bytes), content_type)
|
|
21
|
+
tuple[str | None, FileContent, str | None]
|
|
22
|
+
# (filename, file (or bytes), content_type, headers)
|
|
23
|
+
| tuple[str | None, FileContent, str | None, Mapping[str, str]]
|
|
24
|
+
)
|
|
25
|
+
RequestFiles = list[tuple[str, FileTypes]]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@define
|
|
29
|
+
class File:
|
|
30
|
+
"""Contains information for file uploads"""
|
|
31
|
+
|
|
32
|
+
payload: BinaryIO
|
|
33
|
+
file_name: str | None = None
|
|
34
|
+
mime_type: str | None = None
|
|
35
|
+
|
|
36
|
+
def to_tuple(self) -> FileTypes:
|
|
37
|
+
"""Return a tuple representation that httpx will accept for multipart/form-data"""
|
|
38
|
+
return self.file_name, self.payload, self.mime_type
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
T = TypeVar("T")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@define
|
|
45
|
+
class Response(Generic[T]):
|
|
46
|
+
"""A response from an endpoint"""
|
|
47
|
+
|
|
48
|
+
status_code: HTTPStatus
|
|
49
|
+
content: bytes
|
|
50
|
+
headers: MutableMapping[str, str]
|
|
51
|
+
parsed: T | None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
__all__ = ["UNSET", "File", "FileTypes", "RequestFiles", "Response", "Unset"]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright 2025 Subnoto
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
#
|
|
15
|
+
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Copyright 2025 Subnoto
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
#
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
HTTP signature utilities for signing requests (RFC 9421)
|
|
18
|
+
|
|
19
|
+
Uses http-message-signatures library (RFC 9421 implementation)
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import base64
|
|
23
|
+
import hashlib
|
|
24
|
+
import time
|
|
25
|
+
from typing import Dict
|
|
26
|
+
|
|
27
|
+
from http_message_signatures import HTTPMessageSigner, HTTPSignatureKeyResolver, algorithms
|
|
28
|
+
from http_message_signatures import http_sfv
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SubnotoHTTPSignatureKeyResolver(HTTPSignatureKeyResolver):
|
|
32
|
+
"""Key resolver for Subnoto API signatures"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, access_key: str, secret_key: str):
|
|
35
|
+
self.access_key = access_key
|
|
36
|
+
self.secret_key_bytes = bytes.fromhex(secret_key)
|
|
37
|
+
|
|
38
|
+
def resolve_private_key(self, key_id: str):
|
|
39
|
+
if key_id == self.access_key:
|
|
40
|
+
return self.secret_key_bytes
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def sign_request_headers(
|
|
45
|
+
url: str,
|
|
46
|
+
method: str,
|
|
47
|
+
body_content: bytes,
|
|
48
|
+
access_key: str,
|
|
49
|
+
secret_key: str,
|
|
50
|
+
existing_headers: Dict[str, str]
|
|
51
|
+
) -> Dict[str, str]:
|
|
52
|
+
"""
|
|
53
|
+
Generate HTTP signature headers for a request using RFC 9421
|
|
54
|
+
|
|
55
|
+
Returns a dictionary of headers to add to the request, including:
|
|
56
|
+
- X-Timestamp
|
|
57
|
+
- Digest (legacy)
|
|
58
|
+
- Content-Digest (RFC 9530)
|
|
59
|
+
- Content-Type
|
|
60
|
+
- Content-Length
|
|
61
|
+
- Signature-Input
|
|
62
|
+
- Signature
|
|
63
|
+
"""
|
|
64
|
+
# Generate digests
|
|
65
|
+
sha256_hash = hashlib.sha256(body_content).digest()
|
|
66
|
+
digest_base64 = base64.b64encode(sha256_hash).decode("utf-8")
|
|
67
|
+
|
|
68
|
+
# Legacy Digest header (for compatibility)
|
|
69
|
+
digest = f"SHA-256={digest_base64}"
|
|
70
|
+
|
|
71
|
+
# RFC 9530 Content-Digest header (using http_sfv for proper formatting)
|
|
72
|
+
content_digest_dict = http_sfv.Dictionary({"sha-256": sha256_hash})
|
|
73
|
+
content_digest = str(content_digest_dict)
|
|
74
|
+
|
|
75
|
+
# Timestamp
|
|
76
|
+
timestamp = str(int(time.time() * 1000))
|
|
77
|
+
|
|
78
|
+
# Get content type from existing headers (case-insensitive)
|
|
79
|
+
content_type = "application/json"
|
|
80
|
+
for k, v in existing_headers.items():
|
|
81
|
+
if k.lower() == "content-type":
|
|
82
|
+
content_type = v
|
|
83
|
+
break
|
|
84
|
+
|
|
85
|
+
# Build headers dict
|
|
86
|
+
headers = {
|
|
87
|
+
"X-Timestamp": timestamp,
|
|
88
|
+
"Digest": digest,
|
|
89
|
+
"Content-Digest": content_digest,
|
|
90
|
+
"Content-Type": content_type,
|
|
91
|
+
"Content-Length": str(len(body_content)),
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Simple object for the signing library (modifies headers in place)
|
|
95
|
+
class SignableRequest:
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
request = SignableRequest()
|
|
99
|
+
request.method = method
|
|
100
|
+
request.url = url
|
|
101
|
+
request.headers = headers
|
|
102
|
+
request.body = body_content
|
|
103
|
+
|
|
104
|
+
# Sign the request
|
|
105
|
+
key_resolver = SubnotoHTTPSignatureKeyResolver(access_key, secret_key)
|
|
106
|
+
signer = HTTPMessageSigner(
|
|
107
|
+
signature_algorithm=algorithms.HMAC_SHA256,
|
|
108
|
+
key_resolver=key_resolver
|
|
109
|
+
)
|
|
110
|
+
signer.sign(
|
|
111
|
+
request,
|
|
112
|
+
key_id=access_key,
|
|
113
|
+
covered_component_ids=(
|
|
114
|
+
"x-timestamp",
|
|
115
|
+
"@authority",
|
|
116
|
+
"content-type",
|
|
117
|
+
"content-digest",
|
|
118
|
+
"content-length"
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Replace "pyhms" label with "team"
|
|
123
|
+
if "Signature" in headers:
|
|
124
|
+
headers["Signature"] = headers["Signature"].replace("pyhms=", "team=")
|
|
125
|
+
if "Signature-Input" in headers:
|
|
126
|
+
headers["Signature-Input"] = headers["Signature-Input"].replace("pyhms=", "team=")
|
|
127
|
+
|
|
128
|
+
return headers
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Copyright 2025 Subnoto
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
#
|
|
15
|
+
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
|
|
18
|
+
import httpx
|
|
19
|
+
|
|
20
|
+
from ..types import SubnotoError
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from ..session import SessionManager
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def create_tunnel_middleware(session_manager: "SessionManager"):
|
|
27
|
+
"""Create tunnel encryption/decryption middleware"""
|
|
28
|
+
|
|
29
|
+
async def encrypt_request(request: "httpx.Request") -> None:
|
|
30
|
+
"""Encrypt request body before sending"""
|
|
31
|
+
url = str(request.url)
|
|
32
|
+
|
|
33
|
+
# Skip if not targeting the enclave API
|
|
34
|
+
if "/tunnel/" not in url and "/public/" not in url:
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
# Skip tunnel handshake endpoint itself
|
|
38
|
+
if "/tunnel/session" in url:
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
# Ensure session is established
|
|
42
|
+
await session_manager.ensure_session()
|
|
43
|
+
|
|
44
|
+
# Get request body
|
|
45
|
+
body_content = request.content
|
|
46
|
+
if not body_content or len(body_content) == 0:
|
|
47
|
+
raise SubnotoError("EMPTY_REQUEST_BODY")
|
|
48
|
+
|
|
49
|
+
# Encrypt the request body
|
|
50
|
+
encrypted_message = session_manager.encrypt_request(body_content)
|
|
51
|
+
|
|
52
|
+
if not encrypted_message or len(encrypted_message) == 0:
|
|
53
|
+
raise SubnotoError("Empty encrypted message")
|
|
54
|
+
|
|
55
|
+
# Get session ID
|
|
56
|
+
session_id = session_manager.get_session_id()
|
|
57
|
+
if not session_id:
|
|
58
|
+
raise SubnotoError("Session ID not available")
|
|
59
|
+
|
|
60
|
+
# Update headers
|
|
61
|
+
request.headers["X-Session-Id"] = session_id
|
|
62
|
+
request.headers["Content-Type"] = "application/octet-stream"
|
|
63
|
+
|
|
64
|
+
# Remove old Content-Length so httpx recalculates it
|
|
65
|
+
if "Content-Length" in request.headers:
|
|
66
|
+
del request.headers["Content-Length"]
|
|
67
|
+
|
|
68
|
+
# Add cookies if available
|
|
69
|
+
cookies = session_manager._get_cookies_for_request(url)
|
|
70
|
+
if cookies:
|
|
71
|
+
request.headers["Cookie"] = cookies
|
|
72
|
+
|
|
73
|
+
# Update request content using stream - httpx handles Content-Length
|
|
74
|
+
request.stream = httpx.ByteStream(encrypted_message)
|
|
75
|
+
|
|
76
|
+
async def decrypt_response(response: "httpx.Response") -> None:
|
|
77
|
+
"""Decrypt response body after receiving"""
|
|
78
|
+
url = str(response.request.url)
|
|
79
|
+
|
|
80
|
+
# Skip if not targeting the enclave API
|
|
81
|
+
if "/tunnel/" not in url and "/public/" not in url:
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
# Skip tunnel handshake endpoint itself
|
|
85
|
+
if "/tunnel/session" in url:
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
# Check if response is encrypted
|
|
89
|
+
is_encrypted = (
|
|
90
|
+
response.headers.get("x-subnoto-encrypted-response") == "true"
|
|
91
|
+
and response.headers.get("content-type") == "application/octet-stream"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if not is_encrypted:
|
|
95
|
+
# Return as-is if not encrypted
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
# Get encrypted data
|
|
99
|
+
encrypted_data = response.content
|
|
100
|
+
|
|
101
|
+
if len(encrypted_data) == 0:
|
|
102
|
+
raise SubnotoError("Empty encrypted response")
|
|
103
|
+
|
|
104
|
+
# Decrypt the response
|
|
105
|
+
decrypted_data = session_manager.decrypt_response(encrypted_data)
|
|
106
|
+
|
|
107
|
+
# Replace response content with decrypted data
|
|
108
|
+
# Note: httpx.Response is immutable, so we need to modify the stream
|
|
109
|
+
# This is a workaround - in practice, we may need to return a new response
|
|
110
|
+
response._content = decrypted_data
|
|
111
|
+
response.headers["Content-Type"] = "application/json"
|
|
112
|
+
if "x-subnoto-encrypted-response" in response.headers:
|
|
113
|
+
del response.headers["x-subnoto-encrypted-response"]
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
"request": encrypt_request,
|
|
117
|
+
"response": decrypt_response
|
|
118
|
+
}
|
|
119
|
+
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Copyright 2025 Subnoto
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
#
|
|
15
|
+
|
|
16
|
+
import asyncio
|
|
17
|
+
from http.cookiejar import Cookie, CookieJar
|
|
18
|
+
from typing import Optional
|
|
19
|
+
from urllib.parse import urlparse
|
|
20
|
+
|
|
21
|
+
import httpx
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
from oak_py_sdk.oak_client import PyClientSession
|
|
25
|
+
except ImportError as e:
|
|
26
|
+
raise ImportError(
|
|
27
|
+
f"oak_client module not found: {e}\n"
|
|
28
|
+
"Make sure oak_py_sdk is built:\n"
|
|
29
|
+
" eval \"$(direnv export bash)\" && bazel build //oak_py_sdk:oak_client"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
from .types import SubnotoConfig, SubnotoError
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class SessionManager:
|
|
36
|
+
"""Manages Oak session encryption using PyO3 bindings from oak_py_sdk"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, config: SubnotoConfig, http_client: Optional[httpx.AsyncClient] = None):
|
|
39
|
+
self.config = config
|
|
40
|
+
self.session_id: Optional[str] = None
|
|
41
|
+
self.handshake_in_progress = False
|
|
42
|
+
self.cookie_jar = CookieJar()
|
|
43
|
+
self.http_client = http_client # Use provided client for handshake (with signatures)
|
|
44
|
+
|
|
45
|
+
# Create Oak client session using PyO3
|
|
46
|
+
self._session = PyClientSession(
|
|
47
|
+
unattested=config.unattested,
|
|
48
|
+
attester_key=config.attester_key
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def _is_session_open(self) -> bool:
|
|
52
|
+
"""Check if the session is open"""
|
|
53
|
+
return self._session.is_open()
|
|
54
|
+
|
|
55
|
+
async def ensure_session(self) -> None:
|
|
56
|
+
"""Ensure session is established, performing handshake if needed"""
|
|
57
|
+
if self._is_session_open():
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
if self.handshake_in_progress:
|
|
61
|
+
# Wait for existing handshake
|
|
62
|
+
while self.handshake_in_progress:
|
|
63
|
+
await asyncio.sleep(0.05)
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
await self._handshake()
|
|
67
|
+
|
|
68
|
+
async def _handshake(self) -> None:
|
|
69
|
+
"""Perform the Oak session handshake"""
|
|
70
|
+
self.handshake_in_progress = True
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
max_steps = 4
|
|
74
|
+
step = 0
|
|
75
|
+
|
|
76
|
+
while not self._is_session_open() and step < max_steps:
|
|
77
|
+
step += 1
|
|
78
|
+
|
|
79
|
+
# Get outgoing handshake message
|
|
80
|
+
outgoing = self._session.get_outgoing_message()
|
|
81
|
+
if not outgoing:
|
|
82
|
+
break
|
|
83
|
+
|
|
84
|
+
if self._is_session_open():
|
|
85
|
+
break
|
|
86
|
+
|
|
87
|
+
# Send to server
|
|
88
|
+
url = f"{self.config.api_base_url}/tunnel/session"
|
|
89
|
+
headers = {"Content-Type": "application/octet-stream"}
|
|
90
|
+
|
|
91
|
+
if self.session_id:
|
|
92
|
+
headers["X-Session-Id"] = self.session_id
|
|
93
|
+
|
|
94
|
+
# Add cookies
|
|
95
|
+
cookies_str = self._get_cookies_for_request(url)
|
|
96
|
+
if cookies_str:
|
|
97
|
+
headers["Cookie"] = cookies_str
|
|
98
|
+
|
|
99
|
+
# Use provided client if available (has signatures), otherwise create new one
|
|
100
|
+
if self.http_client:
|
|
101
|
+
response = await self.http_client.post(
|
|
102
|
+
url,
|
|
103
|
+
content=outgoing,
|
|
104
|
+
headers=headers
|
|
105
|
+
)
|
|
106
|
+
else:
|
|
107
|
+
async with httpx.AsyncClient() as client:
|
|
108
|
+
response = await client.post(
|
|
109
|
+
url,
|
|
110
|
+
content=outgoing,
|
|
111
|
+
headers=headers
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Extract session ID from first response
|
|
115
|
+
if not self.session_id:
|
|
116
|
+
session_id_header = response.headers.get("X-Session-Id")
|
|
117
|
+
if not session_id_header:
|
|
118
|
+
raise SubnotoError("No session ID received from server")
|
|
119
|
+
self.session_id = session_id_header
|
|
120
|
+
|
|
121
|
+
if not response.is_success:
|
|
122
|
+
error_text = response.text
|
|
123
|
+
raise SubnotoError(
|
|
124
|
+
f"Handshake failed with status {response.status_code}: {error_text}",
|
|
125
|
+
response.status_code
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Store cookies
|
|
129
|
+
self._store_cookies(response, url)
|
|
130
|
+
|
|
131
|
+
# Process response
|
|
132
|
+
response_data = response.content
|
|
133
|
+
if len(response_data) > 0:
|
|
134
|
+
self._session.put_incoming_message(bytes(response_data))
|
|
135
|
+
else:
|
|
136
|
+
raise SubnotoError("Empty response from server during handshake")
|
|
137
|
+
|
|
138
|
+
if not self._is_session_open() or not self.session_id:
|
|
139
|
+
raise SubnotoError("Failed to establish session after handshake")
|
|
140
|
+
|
|
141
|
+
finally:
|
|
142
|
+
self.handshake_in_progress = False
|
|
143
|
+
|
|
144
|
+
def encrypt_request(self, plaintext: bytes) -> bytes:
|
|
145
|
+
"""Encrypt a request body"""
|
|
146
|
+
if not self._is_session_open():
|
|
147
|
+
raise SubnotoError("Session not open")
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
# PyClientSession.write expects string, returns bytes
|
|
151
|
+
plaintext_str = plaintext.decode('utf-8')
|
|
152
|
+
self._session.write(plaintext_str)
|
|
153
|
+
|
|
154
|
+
# Get the encrypted message
|
|
155
|
+
encrypted = self._session.get_outgoing_message()
|
|
156
|
+
if not encrypted:
|
|
157
|
+
raise SubnotoError("No encrypted message available")
|
|
158
|
+
|
|
159
|
+
return encrypted
|
|
160
|
+
except Exception as e:
|
|
161
|
+
raise SubnotoError(f"Encryption failed: {e}")
|
|
162
|
+
|
|
163
|
+
def decrypt_response(self, encrypted: bytes) -> bytes:
|
|
164
|
+
"""Decrypt a response body"""
|
|
165
|
+
if not self._is_session_open():
|
|
166
|
+
raise SubnotoError("Session not open")
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
# Put the encrypted response into the session
|
|
170
|
+
self._session.put_incoming_message(encrypted)
|
|
171
|
+
|
|
172
|
+
# Read the decrypted plaintext
|
|
173
|
+
decrypted_str = self._session.read()
|
|
174
|
+
if decrypted_str is None:
|
|
175
|
+
raise SubnotoError("No decrypted message available")
|
|
176
|
+
|
|
177
|
+
return decrypted_str.encode('utf-8')
|
|
178
|
+
except Exception as e:
|
|
179
|
+
raise SubnotoError(f"Decryption failed: {e}")
|
|
180
|
+
|
|
181
|
+
def get_session_id(self) -> Optional[str]:
|
|
182
|
+
"""Get the current session ID"""
|
|
183
|
+
return self.session_id
|
|
184
|
+
|
|
185
|
+
def _get_cookies_for_request(self, url: str) -> str:
|
|
186
|
+
"""Get cookies for a request URL"""
|
|
187
|
+
from urllib.request import Request as UrllibRequest
|
|
188
|
+
request = UrllibRequest(url)
|
|
189
|
+
self.cookie_jar.add_cookie_header(request)
|
|
190
|
+
return request.get_header("Cookie", "")
|
|
191
|
+
|
|
192
|
+
def _store_cookies(self, response: httpx.Response, url: str) -> None:
|
|
193
|
+
"""Store cookies from a response"""
|
|
194
|
+
# Extract Set-Cookie headers and add to jar
|
|
195
|
+
for cookie_str in response.headers.get_list("set-cookie"):
|
|
196
|
+
# Parse cookie string into Cookie object
|
|
197
|
+
try:
|
|
198
|
+
# Simple parsing - in production might want to use http.cookies
|
|
199
|
+
parts = cookie_str.split(';')[0].split('=', 1)
|
|
200
|
+
if len(parts) == 2:
|
|
201
|
+
name, value = parts
|
|
202
|
+
parsed_url = urlparse(url)
|
|
203
|
+
cookie = Cookie(
|
|
204
|
+
version=0,
|
|
205
|
+
name=name.strip(),
|
|
206
|
+
value=value.strip(),
|
|
207
|
+
port=None,
|
|
208
|
+
port_specified=False,
|
|
209
|
+
domain=parsed_url.netloc,
|
|
210
|
+
domain_specified=True,
|
|
211
|
+
domain_initial_dot=False,
|
|
212
|
+
path='/',
|
|
213
|
+
path_specified=True,
|
|
214
|
+
secure=False,
|
|
215
|
+
expires=None,
|
|
216
|
+
discard=True,
|
|
217
|
+
comment=None,
|
|
218
|
+
comment_url=None,
|
|
219
|
+
rest={},
|
|
220
|
+
rfc2109=False
|
|
221
|
+
)
|
|
222
|
+
self.cookie_jar.set_cookie(cookie)
|
|
223
|
+
except Exception:
|
|
224
|
+
pass # Skip malformed cookies
|
|
225
|
+
|
|
226
|
+
def destroy(self) -> None:
|
|
227
|
+
"""Destroy the session"""
|
|
228
|
+
self._session = None
|
|
229
|
+
self.session_id = None
|
|
230
|
+
|
|
231
|
+
def get_attestation_results(self) -> Optional[str]:
|
|
232
|
+
"""Get attestation results as JSON string"""
|
|
233
|
+
# PyClientSession doesn't expose this yet
|
|
234
|
+
return None
|
|
235
|
+
|
|
236
|
+
def get_attestation_status(self) -> Optional[str]:
|
|
237
|
+
"""Get attestation status as JSON string"""
|
|
238
|
+
# PyClientSession doesn't expose this yet
|
|
239
|
+
return None
|