diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index 05d4bf0e..57e886b1 100755 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -5,8 +5,8 @@ management: docVersion: 1.1.30 speakeasyVersion: 1.552.0 generationVersion: 2.610.0 - releaseVersion: 0.37.4 - configChecksum: e25743f343bf001c8363d5a665e685d5 + releaseVersion: 0.38.1 + configChecksum: accf1dab899bf95bca0a1c9d8f7ce37a repoURL: https://github.com/Unstructured-IO/unstructured-python-client.git repoSubDirectory: . installationURL: https://github.com/Unstructured-IO/unstructured-python-client.git @@ -17,6 +17,7 @@ features: additionalDependencies: 1.0.0 constsAndDefaults: 1.0.5 core: 5.19.0 + customCodeRegions: 0.1.1 defaultEnabledRetries: 0.2.0 enumUnions: 0.1.0 envVarSecurityUsage: 0.3.2 diff --git a/.speakeasy/workflow.lock b/.speakeasy/workflow.lock index 0f215822..33c4c366 100644 --- a/.speakeasy/workflow.lock +++ b/.speakeasy/workflow.lock @@ -2,20 +2,20 @@ speakeasyVersion: 1.552.0 sources: my-source: sourceNamespace: my-source - sourceRevisionDigest: sha256:eb89977003fa8188a88bc2996d243ca9385d4d6964330e201b0f76a0a4bb2dcd + sourceRevisionDigest: sha256:2f2db6ff7bc20d7cf5d761369bbca095edb6c1fd11a11cbf90b1356285b31cea sourceBlobDigest: sha256:ebd768e737b62ea63eb80866cbb5520c544310d41e0960dad7f1b7f056e8e9c7 tags: - latest - - speakeasy-sdk-regen-1751381120 + - speakeasy-sdk-regen-1751553080 - 1.1.30 targets: unstructured-python: source: my-source sourceNamespace: my-source - sourceRevisionDigest: sha256:eb89977003fa8188a88bc2996d243ca9385d4d6964330e201b0f76a0a4bb2dcd + sourceRevisionDigest: sha256:2f2db6ff7bc20d7cf5d761369bbca095edb6c1fd11a11cbf90b1356285b31cea sourceBlobDigest: sha256:ebd768e737b62ea63eb80866cbb5520c544310d41e0960dad7f1b7f056e8e9c7 codeSamplesNamespace: my-source-code-samples - codeSamplesRevisionDigest: sha256:881506df7bd3dc39018398887eb4b2914e0f22f7be5b7c6418810213af6d1546 + codeSamplesRevisionDigest: sha256:d7f7ccae807fbcc93d5037607208e53a3605ebc2c0919f8ad2d9f5d63fdd006d workflow: workflowVersion: 1.0.0 speakeasyVersion: latest diff --git a/RELEASES.md b/RELEASES.md index 7ad11ac7..bb14869b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1038,4 +1038,14 @@ Based on: ### Generated - [python v0.37.4] . ### Releases -- [PyPI v0.37.4] https://pypi.org/project/unstructured-client/0.37.4 - . \ No newline at end of file +- [PyPI v0.37.4] https://pypi.org/project/unstructured-client/0.37.4 - . + +## 2025-07-03 14:31:04 +### Changes +Based on: +- OpenAPI Doc +- Speakeasy CLI 1.552.0 (2.610.0) https://github.com/speakeasy-api/speakeasy +### Generated +- [python v0.38.1] . +### Releases +- [PyPI v0.38.1] https://pypi.org/project/unstructured-client/0.38.1 - . \ No newline at end of file diff --git a/gen.yaml b/gen.yaml index dc366447..d0e3a401 100644 --- a/gen.yaml +++ b/gen.yaml @@ -14,7 +14,7 @@ generation: oAuth2ClientCredentialsEnabled: false oAuth2PasswordEnabled: false python: - version: 0.37.4 + version: 0.38.1 additionalDependencies: dev: deepdiff: '>=6.0' diff --git a/pyproject.toml b/pyproject.toml index e37567cd..4e51f923 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "unstructured-client" -version = "0.37.4" +version = "0.38.1" description = "Python Client SDK for Unstructured API" authors = [{ name = "Unstructured" },] readme = "README-PYPI.md" diff --git a/src/unstructured_client/_version.py b/src/unstructured_client/_version.py index 0ed32221..1b774d0c 100644 --- a/src/unstructured_client/_version.py +++ b/src/unstructured_client/_version.py @@ -3,10 +3,10 @@ import importlib.metadata __title__: str = "unstructured-client" -__version__: str = "0.37.4" +__version__: str = "0.38.1" __openapi_doc_version__: str = "1.1.30" __gen_version__: str = "2.610.0" -__user_agent__: str = "speakeasy-sdk/python 0.37.4 2.610.0 1.1.30 unstructured-client" +__user_agent__: str = "speakeasy-sdk/python 0.38.1 2.610.0 1.1.30 unstructured-client" try: if __package__ is not None: diff --git a/src/unstructured_client/users.py b/src/unstructured_client/users.py index ef3c2dd0..33eaf4f2 100644 --- a/src/unstructured_client/users.py +++ b/src/unstructured_client/users.py @@ -17,7 +17,162 @@ import base64 # endregion imports + class Users(BaseSDK): + # region sdk-class-body + def _encrypt_rsa_aes( + self, + public_key: rsa.RSAPublicKey, + plaintext: str, + ) -> dict: + # Generate a random AES key + aes_key = os.urandom(32) # 256-bit AES key + + # Generate a random IV + iv = os.urandom(16) + + # Encrypt using AES-CFB + cipher = Cipher( + algorithms.AES(aes_key), + modes.CFB(iv), + ) + encryptor = cipher.encryptor() + ciphertext = encryptor.update(plaintext.encode("utf-8")) + encryptor.finalize() + + # Encrypt the AES key using the RSA public key + encrypted_key = public_key.encrypt( + aes_key, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None, + ), + ) + + return { + "encrypted_aes_key": base64.b64encode(encrypted_key).decode("utf-8"), + "aes_iv": base64.b64encode(iv).decode("utf-8"), + "encrypted_value": base64.b64encode(ciphertext).decode("utf-8"), + "type": "rsa_aes", + } + + def _encrypt_rsa( + self, + public_key: rsa.RSAPublicKey, + plaintext: str, + ) -> dict: + # Load public RSA key + ciphertext = public_key.encrypt( + plaintext.encode(), + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None, + ), + ) + return { + "encrypted_value": base64.b64encode(ciphertext).decode("utf-8"), + "type": "rsa", + "encrypted_aes_key": "", + "aes_iv": "", + } + + def decrypt_secret( + self, + private_key_pem: str, + encrypted_value: str, + secret_type: str, + encrypted_aes_key: str, + aes_iv: str, + ) -> str: + private_key = serialization.load_pem_private_key( + private_key_pem.encode("utf-8"), password=None, backend=default_backend() + ) + + if not isinstance(private_key, rsa.RSAPrivateKey): + raise TypeError("Private key must be a RSA private key for decryption.") + + if secret_type == "rsa": + ciphertext = base64.b64decode(encrypted_value) + plaintext = private_key.decrypt( + ciphertext, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None, + ), + ) + return plaintext.decode("utf-8") + + # aes_rsa + encrypted_aes_key_decoded = base64.b64decode(encrypted_aes_key) + iv = base64.b64decode(aes_iv) + ciphertext = base64.b64decode(encrypted_value) + + aes_key = private_key.decrypt( + encrypted_aes_key_decoded, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None, + ), + ) + cipher = Cipher( + algorithms.AES(aes_key), + modes.CFB(iv), + ) + decryptor = cipher.decryptor() + plaintext = decryptor.update(ciphertext) + decryptor.finalize() + return plaintext.decode("utf-8") + + def encrypt_secret( + self, + encryption_cert_or_key_pem: str, + plaintext: str, + encryption_type: Optional[str] = None, + ) -> dict: + """ + Encrypts a plaintext string for securely sending to the Unstructured API. + + Args: + encryption_cert_or_key_pem (str): A PEM-encoded RSA public key or certificate. + plaintext (str): The string to encrypt. + type (str, optional): Encryption type, either "rsa" or "rsa_aes". + + Returns: + dict: A dictionary with encrypted AES key, iv, and ciphertext (all base64-encoded). + """ + # If a cert is provided, extract the public key + if "BEGIN CERTIFICATE" in encryption_cert_or_key_pem: + cert = x509.load_pem_x509_certificate( + encryption_cert_or_key_pem.encode("utf-8"), + ) + + public_key = cert.public_key() # type: ignore[assignment] + else: + public_key = serialization.load_pem_public_key( + encryption_cert_or_key_pem.encode("utf-8"), backend=default_backend() + ) # type: ignore[assignment] + + if not isinstance(public_key, rsa.RSAPublicKey): + raise TypeError("Public key must be a RSA public key for encryption.") + + # If the plaintext is short, use RSA directly + # Otherwise, use a RSA_AES envelope hybrid + # Use the length of the public key to determine the encryption type + key_size_bytes = public_key.key_size // 8 + max_rsa_length = key_size_bytes - 66 # OAEP SHA256 overhead + + if not encryption_type: + encryption_type = "rsa" if len(plaintext) <= max_rsa_length else "rsa_aes" + + if encryption_type == "rsa": + return self._encrypt_rsa(public_key, plaintext) + + return self._encrypt_rsa_aes(public_key, plaintext) + + # endregion sdk-class-body + def retrieve( self, *, @@ -467,159 +622,3 @@ async def store_secret_async( http_res_text, http_res, ) - - # region sdk-class-body - def _encrypt_rsa_aes( - self, - public_key: rsa.RSAPublicKey, - plaintext: str, - ) -> dict: - # Generate a random AES key - aes_key = os.urandom(32) # 256-bit AES key - - # Generate a random IV - iv = os.urandom(16) - - # Encrypt using AES-CFB - cipher = Cipher( - algorithms.AES(aes_key), - modes.CFB(iv), - ) - encryptor = cipher.encryptor() - ciphertext = encryptor.update(plaintext.encode('utf-8')) + encryptor.finalize() - - # Encrypt the AES key using the RSA public key - encrypted_key = public_key.encrypt( - aes_key, - padding.OAEP( - mgf=padding.MGF1(algorithm=hashes.SHA256()), - algorithm=hashes.SHA256(), - label=None - ) - ) - - return { - 'encrypted_aes_key': base64.b64encode(encrypted_key).decode('utf-8'), - 'aes_iv': base64.b64encode(iv).decode('utf-8'), - 'encrypted_value': base64.b64encode(ciphertext).decode('utf-8'), - 'type': 'rsa_aes', - } - - def _encrypt_rsa( - self, - public_key: rsa.RSAPublicKey, - plaintext: str, - ) -> dict: - # Load public RSA key - ciphertext = public_key.encrypt( - plaintext.encode(), - padding.OAEP( - mgf=padding.MGF1(algorithm=hashes.SHA256()), - algorithm=hashes.SHA256(), - label=None - ), - ) - return { - 'encrypted_value': base64.b64encode(ciphertext).decode('utf-8'), - 'type': 'rsa', - 'encrypted_aes_key': "", - 'aes_iv': "", - } - - def decrypt_secret( - self, - private_key_pem: str, - encrypted_value: str, - secret_type: str, - encrypted_aes_key: str, - aes_iv: str, - ) -> str: - private_key = serialization.load_pem_private_key( - private_key_pem.encode('utf-8'), - password=None, - backend=default_backend() - ) - - if not isinstance(private_key, rsa.RSAPrivateKey): - raise TypeError("Private key must be a RSA private key for decryption.") - - if secret_type == 'rsa': - ciphertext = base64.b64decode(encrypted_value) - plaintext = private_key.decrypt( - ciphertext, - padding.OAEP( - mgf=padding.MGF1(algorithm=hashes.SHA256()), - algorithm=hashes.SHA256(), - label=None - ) - ) - return plaintext.decode('utf-8') - - # aes_rsa - encrypted_aes_key_decoded = base64.b64decode(encrypted_aes_key) - iv = base64.b64decode(aes_iv) - ciphertext = base64.b64decode(encrypted_value) - - aes_key = private_key.decrypt( - encrypted_aes_key_decoded, - padding.OAEP( - mgf=padding.MGF1(algorithm=hashes.SHA256()), - algorithm=hashes.SHA256(), - label=None - ) - ) - cipher = Cipher( - algorithms.AES(aes_key), - modes.CFB(iv), - ) - decryptor = cipher.decryptor() - plaintext = decryptor.update(ciphertext) + decryptor.finalize() - return plaintext.decode('utf-8') - - def encrypt_secret( - self, - encryption_cert_or_key_pem: str, - plaintext: str, - encryption_type: Optional[str] = None, - ) -> dict: - """ - Encrypts a plaintext string for securely sending to the Unstructured API. - - Args: - encryption_cert_or_key_pem (str): A PEM-encoded RSA public key or certificate. - plaintext (str): The string to encrypt. - type (str, optional): Encryption type, either "rsa" or "rsa_aes". - - Returns: - dict: A dictionary with encrypted AES key, iv, and ciphertext (all base64-encoded). - """ - # If a cert is provided, extract the public key - if "BEGIN CERTIFICATE" in encryption_cert_or_key_pem: - cert = x509.load_pem_x509_certificate( - encryption_cert_or_key_pem.encode('utf-8'), - ) - - public_key = cert.public_key() # type: ignore[assignment] - else: - public_key = serialization.load_pem_public_key( - encryption_cert_or_key_pem.encode('utf-8'), - backend=default_backend() - ) # type: ignore[assignment] - - if not isinstance(public_key, rsa.RSAPublicKey): - raise TypeError("Public key must be a RSA public key for encryption.") - - # If the plaintext is short, use RSA directly - # Otherwise, use a RSA_AES envelope hybrid - # Use the length of the public key to determine the encryption type - key_size_bytes = public_key.key_size // 8 - max_rsa_length = key_size_bytes - 66 # OAEP SHA256 overhead - - if not encryption_type: - encryption_type = "rsa" if len(plaintext) <= max_rsa_length else "rsa_aes" - - if encryption_type == "rsa": - return self._encrypt_rsa(public_key, plaintext) - - return self._encrypt_rsa_aes(public_key, plaintext) - # endregion sdk-class-body