diff --git a/README.md b/README.md index c2856557..2a5baa06 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ Operations: - [x] [Delete items](https://developer.1password.com/docs/sdks/manage-items#delete-an-item) - [x] [Archive items](https://developer.1password.com/docs/sdks/manage-items/#archive-an-item) - [x] [List items](https://developer.1password.com/docs/sdks/list-vaults-items/) -- [x] [Share items](https://developer.1password.com/docs/sdks/share-items) (items with files cannot be shared) +- [x] [Share items](https://developer.1password.com/docs/sdks/share-items) - [x] [Generate PIN, random and memorable passwords](https://developer.1password.com/docs/sdks/manage-items#generate-a-password) Field types: @@ -98,7 +98,7 @@ Field types: - [x] Concealed fields - [x] Text fields - [x] Notes -- [x] SSH private keys, public keys, fingerprint and key type +- [x] SSH private keys, public keys, fingerprint and key type - [x] One-time passwords - [x] URLs - [x] Websites (used to suggest and autofill logins) @@ -107,8 +107,8 @@ Field types: - [x] Credit card numbers - [x] Emails - [x] References to other items -- [ ] Address -- [ ] Date +- [x] Address +- [x] Date - [x] MM/YY - [x] Files attachments and Document items - [x] Menu diff --git a/example/example.py b/example/example.py index 2cfc0e41..b0e1e4a5 100644 --- a/example/example.py +++ b/example/example.py @@ -102,7 +102,9 @@ async def main(): ) print(code) # [developer-docs.sdk.python.resolve-totp-code]-end - + await resolve_all_secrets( + client, created_item.vault_id, created_item.id, "username", "password" + ) # [developer-docs.sdk.python.get-totp-item-crud]-start # Fetch a totp code from the item for f in created_item.fields: @@ -229,6 +231,7 @@ async def share_item(client: Client, vault_id: str, item_id: str): async def create_ssh_key_item(client: Client): + # [developer-docs.sdk.python.create-sshkey-item]-start # Generate a 2048-bit RSA private key private_key = rsa.generate_private_key( public_exponent=65537, @@ -242,7 +245,6 @@ async def create_ssh_key_item(client: Client): encryption_algorithm=serialization.NoEncryption(), ) - # [developer-docs.sdk.python.create-sshkey-item]-start # Create an Item containing SSH Key and add it to your vault. to_create = ItemCreateParams( title="SSH Key Item Created With Python SDK", @@ -382,5 +384,85 @@ async def create_attach_and_delete_file_field_item(client: Client): await client.items.delete(deleted_file_item.vault_id, deleted_file_item.id) +async def resolve_all_secrets( + client: Client, vault_id: str, item_id: str, field_id: str, field_id2: str +): + # [developer-docs.sdk.python.resolve-bulk-secret]-start + # Retrieves multiple secret from 1Password. + secrets = await client.secrets.resolve_all( + [ + f"op://{vault_id}//{item_id}/{field_id}", + f"op://{vault_id}/{item_id}/{field_id2}", + ] + ) + for secret in secrets.individual_responses.values(): + if secret.error is not None: + print(str(secret.error)) + else: + print(secret.content.secret) + # [developer-docs.sdk.python.resolve-bulk-secret]-end + + +def generate_special_item_fields(): + fields = ( + [ + # [developer-docs.sdk.python.address-field-type]-start + ItemField( + id="address", + title="Address", + sectionId="", + field_type=ItemFieldType.ADDRESS, + value="", + details=ItemFieldDetailsAddress( + content=AddressFieldDetails( + street="1234 Main St", + city="San Francisco", + state="CA", + zip="94111", + country="USA", + ), + ), + ), + # [developer-docs.sdk.python.address-field-type]-end + # [developer-docs.sdk.python.date-field-type]-start + ItemField( + id="date", + title="Date", + section_id="", + field_type=ItemFieldType.DATE, + value="1998-03-15", + ), + # [developer-docs.sdk.python.date-field-type]-end + # [developer-docs.sdk.python.month-year-field-type]-start + ItemField( + id="month_year", + title="Month Year", + section_id="", + field_type=ItemFieldType.MONTHYEAR, + value="03/1998", + ), + # [developer-docs.sdk.python.month-year-field-type]-end + # [developer-docs.sdk.python.reference-field-type]-start + ItemField( + id="Reference", + title="Reference", + sectionId="", + field_type=ItemFieldType.REFERENCE, + value="f43hnkatjllm5fsfsmgaqdhv7a", + ), + # [developer-docs.sdk.python.reference-field-type]-end + # [developer-docs.sdk.python.totp-field-type]-start + ItemField( + id="onetimepassword", + title="one-time-password", + section_id="", + field_type=ItemFieldType.TOTP, + value="otpauth://totp/my-example-otp?secret=jncrjgbdjnrncbjsr&issuer=1Password", + ), + # [developer-docs.sdk.python.totp-field-type]-end + ], + ) + + if __name__ == "__main__": asyncio.run(main()) diff --git a/src/onepassword/build_number.py b/src/onepassword/build_number.py index 687a1c06..595a8ca9 100644 --- a/src/onepassword/build_number.py +++ b/src/onepassword/build_number.py @@ -1 +1 @@ -SDK_BUILD_NUMBER = "0020001" +SDK_BUILD_NUMBER = "0020101" diff --git a/src/onepassword/core.py b/src/onepassword/core.py index 5f6af199..b1592e15 100644 --- a/src/onepassword/core.py +++ b/src/onepassword/core.py @@ -3,7 +3,7 @@ from onepassword.errors import raise_typed_exception -# In empirical tests, we determined that maximum message size that can cross the FFI boundary +# In empirical tests, we determined that maximum message size that can cross the FFI boundary # is ~128MB. Past this limit, FFI will throw an error and the program will crash. # We set the limit to 50MB to be safe and consistent with the other SDKs (where this limit is 64MB), to be reconsidered upon further testing MESSAGE_LIMIT = 50 * 1024 * 1024 @@ -33,7 +33,8 @@ async def _invoke(invoke_config): serialized_config = json.dumps(invoke_config) if len(serialized_config.encode()) > MESSAGE_LIMIT: raise ValueError( - f"message size exceeds the limit of {MESSAGE_LIMIT} bytes, please contact 1Password at support@1password.com or https://developer.1password.com/joinslack if you need help.") + f"message size exceeds the limit of {MESSAGE_LIMIT} bytes, please contact 1Password at support@1password.com or https://developer.1password.com/joinslack if you need help." + ) try: return await core.invoke(serialized_config) except Exception as e: @@ -45,7 +46,8 @@ def _invoke_sync(invoke_config): serialized_config = json.dumps(invoke_config) if len(serialized_config.encode()) > MESSAGE_LIMIT: raise ValueError( - f"message size exceeds the limit of {MESSAGE_LIMIT} bytes, please contact 1Password at support@1password.com or https://developer.1password.com/joinslack if you need help.") + f"message size exceeds the limit of {MESSAGE_LIMIT} bytes, please contact 1Password at support@1password.com or https://developer.1password.com/joinslack if you need help." + ) try: return core.invoke_sync(serialized_config) except Exception as e: diff --git a/src/onepassword/lib/aarch64/libop_uniffi_core.dylib b/src/onepassword/lib/aarch64/libop_uniffi_core.dylib index efc3ecaf..8d4211be 100755 Binary files a/src/onepassword/lib/aarch64/libop_uniffi_core.dylib and b/src/onepassword/lib/aarch64/libop_uniffi_core.dylib differ diff --git a/src/onepassword/lib/aarch64/libop_uniffi_core.so b/src/onepassword/lib/aarch64/libop_uniffi_core.so index 40849eab..a386ee58 100755 Binary files a/src/onepassword/lib/aarch64/libop_uniffi_core.so and b/src/onepassword/lib/aarch64/libop_uniffi_core.so differ diff --git a/src/onepassword/lib/x86_64/libop_uniffi_core.dylib b/src/onepassword/lib/x86_64/libop_uniffi_core.dylib index 752e0361..8ccf2801 100755 Binary files a/src/onepassword/lib/x86_64/libop_uniffi_core.dylib and b/src/onepassword/lib/x86_64/libop_uniffi_core.dylib differ diff --git a/src/onepassword/lib/x86_64/libop_uniffi_core.so b/src/onepassword/lib/x86_64/libop_uniffi_core.so index 011343cb..015db2f9 100755 Binary files a/src/onepassword/lib/x86_64/libop_uniffi_core.so and b/src/onepassword/lib/x86_64/libop_uniffi_core.so differ diff --git a/src/onepassword/lib/x86_64/op_uniffi_core.dll b/src/onepassword/lib/x86_64/op_uniffi_core.dll index 077d5e56..21b3ef66 100644 Binary files a/src/onepassword/lib/x86_64/op_uniffi_core.dll and b/src/onepassword/lib/x86_64/op_uniffi_core.dll differ diff --git a/src/onepassword/secrets.py b/src/onepassword/secrets.py index 7a2a004c..9d16e64e 100644 --- a/src/onepassword/secrets.py +++ b/src/onepassword/secrets.py @@ -4,7 +4,7 @@ from .iterator import SDKIterator from typing import Optional, List from pydantic import TypeAdapter -from .types import GeneratePasswordResponse, PasswordRecipe +from .types import GeneratePasswordResponse, PasswordRecipe, ResolveAllResponse class Secrets: @@ -35,6 +35,25 @@ async def resolve(self, secret_reference: str) -> str: response = TypeAdapter(str).validate_json(response) return response + async def resolve_all(self, secret_references: List[str]) -> ResolveAllResponse: + """ + Resolve takes in a list of secret references and returns the secrets they point to or errors if any. + """ + response = await _invoke( + { + "invocation": { + "clientId": self.client_id, + "parameters": { + "name": "SecretsResolveAll", + "parameters": {"secret_references": secret_references}, + }, + } + } + ) + + response = TypeAdapter(ResolveAllResponse).validate_json(response) + return response + @staticmethod def validate_secret_reference(secret_reference: str) -> None: """ diff --git a/src/onepassword/types.py b/src/onepassword/types.py index 99b79fd1..c7379c66 100644 --- a/src/onepassword/types.py +++ b/src/onepassword/types.py @@ -4,9 +4,13 @@ from __future__ import annotations +from datetime import datetime from enum import Enum from pydantic import BaseModel, BeforeValidator, ConfigDict, Field, PlainSerializer -from typing import Annotated, List, Literal, Optional, Union +from typing import Annotated, Dict, Generic, List, Literal, Optional, TypeVar, Union + +E = TypeVar("E") +T = TypeVar("T") def serialize_binary_data(value: bytes) -> list[int]: @@ -23,10 +27,56 @@ def deserialize_binary_data(value): raise TypeError("Content must be a list of integers (0-255) or bytes.") +def serialize_datetime_data(utc_time: datetime) -> str: + return utc_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ") + + +def parse_rfc3339(date_str: str) -> datetime: + date_formats = ["%Y-%m-%dT%H:%M:%SZ", "%Y-%m-%dT%H:%M:%S.%fZ"] + + for fmt in date_formats: + try: + return datetime.strptime(date_str, fmt) + except ValueError: + continue + + raise ValueError(f"Invalid RFC 3339 date format: {date_str}") + + +ErrorMessage = str + + +class AddressFieldDetails(BaseModel): + """ + Additional attributes for OTP fields. + """ + + street: str + """ + The street address + """ + city: str + """ + The city + """ + country: str + """ + The country + """ + zip: str + """ + The ZIP code + """ + state: str + """ + The state + """ + + class DocumentCreateParams(BaseModel): name: str """ - the name of the file + The name of the file """ content: Annotated[ bytes, @@ -34,7 +84,7 @@ class DocumentCreateParams(BaseModel): PlainSerializer(serialize_binary_data), ] """ - the content of the file + The content of the file """ @@ -58,7 +108,7 @@ class FileCreateParams(BaseModel): name: str """ - the name of the file + The name of the file """ content: Annotated[ bytes, @@ -66,15 +116,15 @@ class FileCreateParams(BaseModel): PlainSerializer(serialize_binary_data), ] """ - the content of the file + The content of the file """ section_id: str = Field(alias="sectionId") """ - the section id where the file should be stored + The section id where the file should be stored """ field_id: str = Field(alias="fieldId") """ - the field id where the file should be stored + The field id where the file should be stored """ @@ -130,12 +180,15 @@ class ItemFieldType(str, Enum): SSHKEY = "SshKey" MENU = "Menu" MONTHYEAR = "MonthYear" + ADDRESS = "Address" + DATE = "Date" UNSUPPORTED = "Unsupported" class ItemFieldDetailsTypes(str, Enum): OTP = "Otp" SSH_KEY = "SshKey" + ADDRESS = "Address" class ItemFieldDetailsOtp(BaseModel): @@ -156,8 +209,19 @@ class ItemFieldDetailsSshKey(BaseModel): content: Optional[SshKeyAttributes] +class ItemFieldDetailsAddress(BaseModel): + """ + Address components + """ + + type: Literal[ItemFieldDetailsTypes.ADDRESS] = ItemFieldDetailsTypes.ADDRESS + content: Optional[AddressFieldDetails] + + # Field type-specific attributes. -ItemFieldDetails = Union[ItemFieldDetailsOtp, ItemFieldDetailsSshKey] +ItemFieldDetails = Union[ + ItemFieldDetailsOtp, ItemFieldDetailsSshKey, ItemFieldDetailsAddress +] class ItemField(BaseModel): @@ -321,6 +385,22 @@ class Item(BaseModel): """ The document file for the Document item category """ + created_at: Annotated[ + datetime, + BeforeValidator(parse_rfc3339), + PlainSerializer(serialize_datetime_data), + ] = Field(alias="createdAt") + """ + The time the item was created at + """ + updated_at: Annotated[ + datetime, + BeforeValidator(parse_rfc3339), + PlainSerializer(serialize_datetime_data), + ] = Field(alias="updatedAt") + """ + The time the item was updated at + """ class ItemCreateParams(BaseModel): @@ -364,13 +444,13 @@ class ItemCreateParams(BaseModel): """ document: Optional[DocumentCreateParams] = Field(default=None) """ - The document file for the Document item category + The document file for the Document item type. Empty when the item isn't of Document type. """ class ItemOverview(BaseModel): """ - Represents a decrypted 1Password item. + Represents a decrypted 1Password item overview. """ model_config = ConfigDict(populate_by_name=True) @@ -395,6 +475,26 @@ class ItemOverview(BaseModel): """ The websites used for autofilling for items of the Login and Password categories. """ + tags: List[str] + """ + The item tags + """ + created_at: Annotated[ + datetime, + BeforeValidator(parse_rfc3339), + PlainSerializer(serialize_datetime_data), + ] = Field(alias="createdAt") + """ + The time the item was created at + """ + updated_at: Annotated[ + datetime, + BeforeValidator(parse_rfc3339), + PlainSerializer(serialize_datetime_data), + ] = Field(alias="updatedAt") + """ + The time the item was updated at + """ class ItemShareDuration(str, Enum): @@ -454,6 +554,49 @@ class AllowedRecipientType(str, Enum): """ +class ItemShareFiles(BaseModel): + """ + The file sharing policy + """ + + model_config = ConfigDict(populate_by_name=True) + + allowed: bool + """ + Whether files can be included in item shares + """ + max_size: int = Field(alias="maxSize") + """ + The maximum encrypted size (in bytes) an included file can be + """ + allowed_types: Optional[List[AllowedType]] = Field( + alias="allowedTypes", default=None + ) + """ + The allowed types of item sharing - either "Authenticated" (share to specific users) or "Public" (share to anyone with a link) + """ + allowed_recipient_types: Optional[List[AllowedRecipientType]] = Field( + alias="allowedRecipientTypes", default=None + ) + """ + The allowed recipient types of item sharing - either "Email" or "Domain" + """ + max_expiry: Optional[ItemShareDuration] = Field(alias="maxExpiry", default=None) + """ + The maximum duration that an item can be shared for + """ + default_expiry: Optional[ItemShareDuration] = Field( + alias="defaultExpiry", default=None + ) + """ + The default duration that an item is shared for + """ + max_views: Optional[int] = Field(alias="maxViews", default=None) + """ + The maximum number of times an item can be viewed. A null value means unlimited views + """ + + class ItemShareAccountPolicy(BaseModel): """ The account policy for sharing items, set by your account owner/admin @@ -484,6 +627,10 @@ class ItemShareAccountPolicy(BaseModel): """ The allowed recipient types of item sharing - either "Email" or "Domain" """ + files: ItemShareFiles + """ + The file sharing policy + """ class ValidRecipientEmailInner(BaseModel): @@ -568,6 +715,226 @@ class OtpFieldDetails(BaseModel): """ +class Response(BaseModel, Generic[T, E]): + content: Optional[T] = Field(default=None) + error: Optional[E] = Field(default=None) + + +class ResolvedReference(BaseModel): + model_config = ConfigDict(populate_by_name=True) + + secret: str + item_id: str = Field(alias="itemId") + vault_id: str = Field(alias="vaultId") + + +class ResolveReferenceErrorTypes(str, Enum): + PARSING = "parsing" + FIELD_NOT_FOUND = "fieldNotFound" + VAULT_NOT_FOUND = "vaultNotFound" + TOO_MANY_VAULTS = "tooManyVaults" + ITEM_NOT_FOUND = "itemNotFound" + TOO_MANY_ITEMS = "tooManyItems" + TOO_MANY_MATCHING_FIELDS = "tooManyMatchingFields" + NO_MATCHING_SECTIONS = "noMatchingSections" + INCOMPATIBLE_TOTP_QUERY_PARAMETER_FIELD = "incompatibleTOTPQueryParameterField" + UNABLE_TO_GENERATE_TOTP_CODE = "unableToGenerateTotpCode" + S_SH_KEY_METADATA_NOT_FOUND = "sSHKeyMetadataNotFound" + UNSUPPORTED_FILE_FORMAT = "unsupportedFileFormat" + INCOMPATIBLE_SSH_KEY_QUERY_PARAMETER_FIELD = "incompatibleSshKeyQueryParameterField" + UNABLE_TO_PARSE_PRIVATE_KEY = "unableToParsePrivateKey" + UNABLE_TO_FORMAT_PRIVATE_KEY_TO_OPEN_SSH = "unableToFormatPrivateKeyToOpenSsh" + OTHER = "other" + + +class ResolveReferenceErrorParsing(BaseModel): + """ + Error parsing the secret reference + """ + + type: Literal[ResolveReferenceErrorTypes.PARSING] = ( + ResolveReferenceErrorTypes.PARSING + ) + message: ErrorMessage + + +class ResolveReferenceErrorFieldNotFound(BaseModel): + """ + The specified reference cannot be found within the item + """ + + type: Literal[ResolveReferenceErrorTypes.FIELD_NOT_FOUND] = ( + ResolveReferenceErrorTypes.FIELD_NOT_FOUND + ) + + +class ResolveReferenceErrorVaultNotFound(BaseModel): + """ + No vault matched the secret reference query + """ + + type: Literal[ResolveReferenceErrorTypes.VAULT_NOT_FOUND] = ( + ResolveReferenceErrorTypes.VAULT_NOT_FOUND + ) + + +class ResolveReferenceErrorTooManyVaults(BaseModel): + """ + More than one vault matched the secret reference query + """ + + type: Literal[ResolveReferenceErrorTypes.TOO_MANY_VAULTS] = ( + ResolveReferenceErrorTypes.TOO_MANY_VAULTS + ) + + +class ResolveReferenceErrorItemNotFound(BaseModel): + """ + No item matched the secret reference query + """ + + type: Literal[ResolveReferenceErrorTypes.ITEM_NOT_FOUND] = ( + ResolveReferenceErrorTypes.ITEM_NOT_FOUND + ) + + +class ResolveReferenceErrorTooManyItems(BaseModel): + """ + More than one item matched the secret reference query + """ + + type: Literal[ResolveReferenceErrorTypes.TOO_MANY_ITEMS] = ( + ResolveReferenceErrorTypes.TOO_MANY_ITEMS + ) + + +class ResolveReferenceErrorTooManyMatchingFields(BaseModel): + """ + More than one field matched the provided secret reference + """ + + type: Literal[ResolveReferenceErrorTypes.TOO_MANY_MATCHING_FIELDS] = ( + ResolveReferenceErrorTypes.TOO_MANY_MATCHING_FIELDS + ) + + +class ResolveReferenceErrorNoMatchingSections(BaseModel): + """ + No section found within the item for the provided identifier + """ + + type: Literal[ResolveReferenceErrorTypes.NO_MATCHING_SECTIONS] = ( + ResolveReferenceErrorTypes.NO_MATCHING_SECTIONS + ) + + +class ResolveReferenceErrorIncompatibleTOTPQueryParameterField(BaseModel): + """ + Incompatiable TOTP query parameters + """ + + type: Literal[ + ResolveReferenceErrorTypes.INCOMPATIBLE_TOTP_QUERY_PARAMETER_FIELD + ] = ResolveReferenceErrorTypes.INCOMPATIBLE_TOTP_QUERY_PARAMETER_FIELD + + +class ResolveReferenceErrorUnableToGenerateTotpCode(BaseModel): + """ + The totp was not able to be generated + """ + + type: Literal[ResolveReferenceErrorTypes.UNABLE_TO_GENERATE_TOTP_CODE] = ( + ResolveReferenceErrorTypes.UNABLE_TO_GENERATE_TOTP_CODE + ) + message: ErrorMessage + + +class ResolveReferenceErrorSSHKeyMetadataNotFound(BaseModel): + """ + Couldn't find attributes specific to an SSH Key field + """ + + type: Literal[ResolveReferenceErrorTypes.S_SH_KEY_METADATA_NOT_FOUND] = ( + ResolveReferenceErrorTypes.S_SH_KEY_METADATA_NOT_FOUND + ) + + +class ResolveReferenceErrorUnsupportedFileFormat(BaseModel): + """ + Currently only support text files + """ + + type: Literal[ResolveReferenceErrorTypes.UNSUPPORTED_FILE_FORMAT] = ( + ResolveReferenceErrorTypes.UNSUPPORTED_FILE_FORMAT + ) + + +class ResolveReferenceErrorIncompatibleSshKeyQueryParameterField(BaseModel): + """ + Trying to convert a non-private key to a private key format + """ + + type: Literal[ + ResolveReferenceErrorTypes.INCOMPATIBLE_SSH_KEY_QUERY_PARAMETER_FIELD + ] = ResolveReferenceErrorTypes.INCOMPATIBLE_SSH_KEY_QUERY_PARAMETER_FIELD + + +class ResolveReferenceErrorUnableToParsePrivateKey(BaseModel): + """ + Unable to properly parse a private key string to convert to an internal Private Key type + """ + + type: Literal[ResolveReferenceErrorTypes.UNABLE_TO_PARSE_PRIVATE_KEY] = ( + ResolveReferenceErrorTypes.UNABLE_TO_PARSE_PRIVATE_KEY + ) + + +class ResolveReferenceErrorUnableToFormatPrivateKeyToOpenSsh(BaseModel): + """ + Unable to format a private key to OpenSSH format + """ + + type: Literal[ + ResolveReferenceErrorTypes.UNABLE_TO_FORMAT_PRIVATE_KEY_TO_OPEN_SSH + ] = ResolveReferenceErrorTypes.UNABLE_TO_FORMAT_PRIVATE_KEY_TO_OPEN_SSH + + +class ResolveReferenceErrorOther(BaseModel): + """ + Other type + """ + + type: Literal[ResolveReferenceErrorTypes.OTHER] = ResolveReferenceErrorTypes.OTHER + + +ResolveReferenceError = Union[ + ResolveReferenceErrorParsing, + ResolveReferenceErrorFieldNotFound, + ResolveReferenceErrorVaultNotFound, + ResolveReferenceErrorTooManyVaults, + ResolveReferenceErrorItemNotFound, + ResolveReferenceErrorTooManyItems, + ResolveReferenceErrorTooManyMatchingFields, + ResolveReferenceErrorNoMatchingSections, + ResolveReferenceErrorIncompatibleTOTPQueryParameterField, + ResolveReferenceErrorUnableToGenerateTotpCode, + ResolveReferenceErrorSSHKeyMetadataNotFound, + ResolveReferenceErrorUnsupportedFileFormat, + ResolveReferenceErrorIncompatibleSshKeyQueryParameterField, + ResolveReferenceErrorUnableToParsePrivateKey, + ResolveReferenceErrorUnableToFormatPrivateKeyToOpenSsh, + ResolveReferenceErrorOther, +] + + +class ResolveAllResponse(BaseModel): + model_config = ConfigDict(populate_by_name=True) + + individual_responses: Dict[ + str, Response[ResolvedReference, ResolveReferenceError] + ] = Field(alias="individualResponses") + + class SshKeyAttributes(BaseModel): model_config = ConfigDict(populate_by_name=True) diff --git a/src/release/RELEASE-NOTES b/src/release/RELEASE-NOTES index f9e50bbf..72513733 100644 --- a/src/release/RELEASE-NOTES +++ b/src/release/RELEASE-NOTES @@ -1,13 +1,20 @@ -# 1Password Python SDK v0.2.0 +# 1Password Python SDK v0.2.1 ## NEW -- **File Support:** You can now create Document items, attach files to items, delete files from items, and read file contents using the SDK. +- **`CreatedAt` and `UpdatedAt` item metadata:** Items and item overviews now expose attributes with their creation and last edit times. +- **Resolving secrets in bulk**: With the `client.secrets.resolveAll` function, the SDK is now able to resolve multiple secrets at once, improving the performance of the operation. ## IMPROVED -- **Read files using secret references**: You can now resolve secret references that point to files attached to 1Password items. -- **Read SSH keys in Open SSH format**: You can now use a secret reference to fetch a private key in OpenSSH format. For example: `op://vault//private key?ssh-format=openssh` -- **Support for more item field types**: You can now create, retrieve, and edit items containing SSH keys, Month-Year and Menu-type fields using the SDK. -- **Read more field types using secret references**: You can now resolve secret references that point to information stored in Date, Month/Year, Address, and Reference field types. -- **Improved error messages**: The error messages returned by the SDK were improved to be more clear and actionable. +- **Support for new field types:** Items with `Address` and `Date` fields can now be created, retrieved, and edited using the 1Password SDK. +- **Item sharing for attachments and documents**: Items with files attached now can also be shared using the `client.items.shares` functions. +- **Adding custom fields in sections automatically**: The SDK now automatically adds custom fields without a section to an empty section within the item, creating it if necessary. +- **`Tags` in item overviews**: The return type of `items.listAll` now also contains the item tags. +- **Broader item editing capabilities**: You are now able to use the `items.put` function on more items, including those with fields that are not directly editable through the SDK (such as legacy fields, passkeys etc.) + +## FIXED + +- **Improvements to resolving secret references:** + - Archived items are no longer used for secret references. + - When multiple sections match a section query in resolving secret references, the SDK look through the fields in all sections, instead of erroring. diff --git a/version.py b/version.py index f5ebd34f..05a2fb11 100644 --- a/version.py +++ b/version.py @@ -1 +1 @@ -SDK_VERSION = "0.2.0" +SDK_VERSION = "0.2.1"