Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sdk/storage/azure-storage-blob/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Features Added
- Added `list` support to `BlobSasPermissions` for use with directory-scoped SAS tokens.
- Added opt-in client session-based authentication via the new `use_session` keyword argument. When enabled, eligible blob download requests are authenticated with a short-lived, per-container session credential obtained from the service rather than the bearer token. Requires a `TokenCredential`. Sessions are managed by a session provider, which can be shared across clients via the `session_provider` keyword, and the account name used for signing can be set explicitly with `session_account_name`.

### Bugs Fixed
- Fixed an issue where `destination_snapshot` on a blob's copy properties was always `None` when listing blobs with `response_format="arrow"`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
"azure.storage.blob.models.CorsRule": null,
"azure.storage.blob.models.CpkInfo": null,
"azure.storage.blob.models.CpkScopeInfo": null,
"azure.storage.blob.models.CreateSessionConfiguration": null,
"azure.storage.blob.models.CreateSessionResponse": null,
"azure.storage.blob.models.DelimitedTextConfiguration": null,
"azure.storage.blob.models.FilterBlobItem": null,
"azure.storage.blob.models.FilterBlobSegment": null,
Expand All @@ -46,6 +48,7 @@
"azure.storage.blob.models.QuerySerialization": null,
"azure.storage.blob.models.RetentionPolicy": null,
"azure.storage.blob.models.SequenceNumberAccessConditions": null,
"azure.storage.blob.models.SessionCredentials": null,
"azure.storage.blob.models.SignedIdentifier": null,
"azure.storage.blob.models.SourceCpkInfo": null,
"azure.storage.blob.models.SourceModifiedAccessConditions": null,
Expand All @@ -68,6 +71,7 @@
"azure.storage.blob.models.RehydratePriority": null,
"azure.storage.blob.models.BlobImmutabilityPolicyMode": null,
"azure.storage.blob.models.GeoReplicationStatusType": null,
"azure.storage.blob.models.AuthenticationType": null,
"azure.storage.blob.models.PremiumPageBlobAccessTier": null,
"azure.storage.blob.models.AccessTierOptional": null,
"azure.storage.blob.models.FileShareTokenIntent": null,
Expand Down Expand Up @@ -138,6 +142,8 @@
"azure.storage.blob.aio.operations.ContainerOperations.list_blob_hierarchy_segment_apache_arrow": null,
"azure.storage.blob.operations.ContainerOperations.get_account_info": null,
"azure.storage.blob.aio.operations.ContainerOperations.get_account_info": null,
"azure.storage.blob.operations.ContainerOperations.create_session": null,
"azure.storage.blob.aio.operations.ContainerOperations.create_session": null,
"azure.storage.blob.operations.BlobOperations.download": null,
"azure.storage.blob.aio.operations.BlobOperations.download": null,
"azure.storage.blob.operations.BlobOperations.get_properties": null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
UserDelegationKey,
Services,
)
from ._shared.session import ContainerSessionProvider, SessionProvider
from ._generated.models import RehydratePriority
from ._models import (
BlobType,
Expand Down Expand Up @@ -265,4 +266,7 @@ def download_blob_from_url(
"ObjectReplicationPolicy",
"ObjectReplicationRule",
"Services",
"SessionProvider",
"SessionOptions",
"ContainerSessionProvider",
]
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,19 @@ class BlobServiceClient(StorageAccountHostsMixin, StorageEncryptionMixin):
:keyword str audience: The audience to use when requesting tokens for Azure Active Directory
authentication. Only has an effect when credential is of type TokenCredential. The value could be
https://storage.azure.com/ (default) or https://<account>.blob.core.windows.net.
:keyword bool use_session: If True, enable session-based authentication for this container.
When enabled, eligible GET requests issued by this client will be authenticated using
a short-lived session credential obtained from the service instead
of the provided TokenCredential. Only supported with a TokenCredential;
ValueError is raised otherwise. Defaults to False.
:keyword session_provider: Creates, caches, and invalidates the session credentials used for
session-based authentication. Supply a shared instance to reuse the session cache across
multiple clients; when omitted, one is created and scoped to this client. Only has an
effect when `use_session` is True.
:paramtype session_provider: ~azure.storage.blob.SessionProvider
:keyword str session_account_name: The storage account name used to sign session-authenticated
requests. If omitted, it is derived from the account URL. Required when using a custom
endpoint, where the account name cannot be determined from the URL.

.. admonition:: Example:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ class BlobServiceClient(StorageAccountHostsMixin, StorageEncryptionMixin):
max_single_get_size: int = 32 * 1024 * 1024,
max_chunk_get_size: int = 4 * 1024 * 1024,
audience: Optional[str] = None,
use_session: bool = False,
session_provider: Optional[Any] = None,
session_account_name: Optional[str] = None,
**kwargs: Any
) -> None: ...
def __enter__(self) -> Self: ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,19 @@ class ContainerClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pyli
:keyword str audience: The audience to use when requesting tokens for Azure Active Directory
authentication. Only has an effect when credential is of type TokenCredential. The value could be
https://storage.azure.com/ (default) or https://<account>.blob.core.windows.net.
:keyword bool use_session: If True, enable session-based authentication for this container.
When enabled, eligible GET requests issued by this client will be authenticated using
a short-lived session credential obtained from the service instead
of the provided TokenCredential. Only supported with a TokenCredential;
ValueError is raised otherwise. Defaults to False.
:keyword session_provider: Creates, caches, and invalidates the session credentials used for
session-based authentication. Supply a shared instance to reuse the session cache across
multiple clients; when omitted, one is created and scoped to this client. Only has an
effect when `use_session` is True.
:paramtype session_provider: ~azure.storage.blob.SessionProvider
:keyword str session_account_name: The storage account name used to sign session-authenticated
requests. If omitted, it is derived from the account URL. Required when using a custom
endpoint, where the account name cannot be determined from the URL.

.. admonition:: Example:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ class ContainerClient(StorageAccountHostsMixin, StorageEncryptionMixin):
max_single_get_size: int = 32 * 1024 * 1024,
min_large_block_upload_threshold: int = 4 * 1024 * 1024 + 1,
use_byte_buffer: Optional[bool] = None,
use_session: bool = False,
session_provider: Optional[Any] = None,
session_account_name: Optional[str] = None,
**kwargs: Any,
) -> None: ...
def __enter__(self) -> Self: ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
build_break_lease_request,
build_change_lease_request,
build_create_request,
build_create_session_request,
build_delete_request,
build_filter_blobs_request,
build_get_access_policy_request,
Expand Down Expand Up @@ -2122,3 +2123,82 @@ async def get_account_info(

if cls:
return cls(pipeline_response, None, response_headers) # type: ignore

@distributed_trace_async
async def create_session(
self,
create_session_configuration: _models.CreateSessionConfiguration,
timeout: Optional[int] = None,
request_id_parameter: Optional[str] = None,
**kwargs: Any
) -> _models.CreateSessionResponse:
"""The Create Session operation enables users to create a session scoped to a container.

:param create_session_configuration: Required.
:type create_session_configuration: ~azure.storage.blob.models.CreateSessionConfiguration
:param timeout: The timeout parameter is expressed in seconds. For more information, see
:code:`<a
href="https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-blob-service-operations">Setting
Timeouts for Blob Service Operations.</a>`. Default value is None.
:type timeout: int
:param request_id_parameter: Provides a client-generated, opaque value with a 1 KB character
limit that is recorded in the analytics logs when storage analytics logging is enabled. Default
value is None.
:type request_id_parameter: str
:return: CreateSessionResponse or the result of cls(response)
:rtype: ~azure.storage.blob.models.CreateSessionResponse
:raises ~azure.core.exceptions.HttpResponseError:
"""
error_map: MutableMapping = {
401: ClientAuthenticationError,
404: ResourceNotFoundError,
409: ResourceExistsError,
304: ResourceNotModifiedError,
}
error_map.update(kwargs.pop("error_map", {}) or {})

_headers = case_insensitive_dict(kwargs.pop("headers", {}) or {})
_params = case_insensitive_dict(kwargs.pop("params", {}) or {})

restype: Literal["container"] = kwargs.pop("restype", _params.pop("restype", "container"))
comp: Literal["session"] = kwargs.pop("comp", _params.pop("comp", "session"))
content_type: str = kwargs.pop("content_type", _headers.pop("Content-Type", "application/xml"))
cls: ClsType[_models.CreateSessionResponse] = kwargs.pop("cls", None)

_content = self._serialize.body(create_session_configuration, "CreateSessionConfiguration", is_xml=True)

_request = build_create_session_request(
url=self._config.url,
version=self._config.version,
timeout=timeout,
request_id_parameter=request_id_parameter,
restype=restype,
comp=comp,
content_type=content_type,
content=_content,
headers=_headers,
params=_params,
)
_request.url = self._client.format_url(_request.url)

_stream = False
pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access
_request, stream=_stream, **kwargs
)

response = pipeline_response.http_response

if response.status_code not in [201]:
map_error(status_code=response.status_code, response=response, error_map=error_map)
error = self._deserialize.failsafe_deserialize(
_models.StorageError,
pipeline_response,
)
raise HttpResponseError(response=response, model=error)

deserialized = self._deserialize("CreateSessionResponse", pipeline_response.http_response)

if cls:
return cls(pipeline_response, deserialized, {}) # type: ignore

return deserialized # type: ignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
CorsRule,
CpkInfo,
CpkScopeInfo,
CreateSessionConfiguration,
CreateSessionResponse,
DelimitedTextConfiguration,
FilterBlobItem,
FilterBlobSegment,
Expand All @@ -59,6 +61,7 @@
QuerySerialization,
RetentionPolicy,
SequenceNumberAccessConditions,
SessionCredentials,
SignedIdentifier,
SourceCpkInfo,
SourceModifiedAccessConditions,
Expand All @@ -75,6 +78,7 @@
AccessTierRequired,
AccountKind,
ArchiveStatus,
AuthenticationType,
BlobCopySourceTags,
BlobExpiryOptions,
BlobImmutabilityPolicyMode,
Expand Down Expand Up @@ -129,6 +133,8 @@
"CorsRule",
"CpkInfo",
"CpkScopeInfo",
"CreateSessionConfiguration",
"CreateSessionResponse",
"DelimitedTextConfiguration",
"FilterBlobItem",
"FilterBlobSegment",
Expand All @@ -149,6 +155,7 @@
"QuerySerialization",
"RetentionPolicy",
"SequenceNumberAccessConditions",
"SessionCredentials",
"SignedIdentifier",
"SourceCpkInfo",
"SourceModifiedAccessConditions",
Expand All @@ -162,6 +169,7 @@
"AccessTierRequired",
"AccountKind",
"ArchiveStatus",
"AuthenticationType",
"BlobCopySourceTags",
"BlobExpiryOptions",
"BlobImmutabilityPolicyMode",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ class ArchiveStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta):
REHYDRATE_PENDING_TO_SMART = "rehydrate-pending-to-smart"


class AuthenticationType(str, Enum, metaclass=CaseInsensitiveEnumMeta):
"""The type of authentication required to create the session. The only type currently supported is
HMAC.
"""

HMAC = "HMAC"


class BlobCopySourceTags(str, Enum, metaclass=CaseInsensitiveEnumMeta):
"""BlobCopySourceTags."""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1456,6 +1456,84 @@ def __init__(self, *, encryption_scope: Optional[str] = None, **kwargs: Any) ->
self.encryption_scope = encryption_scope


class CreateSessionConfiguration(_serialization.Model):
"""CreateSessionConfiguration.

All required parameters must be populated in order to send to server.

:ivar authentication_type: The type of authentication required to create the session. The only
type currently supported is HMAC. Required. "HMAC"
:vartype authentication_type: str or ~azure.storage.blob.models.AuthenticationType
"""

_validation = {
"authentication_type": {"required": True},
}

_attribute_map = {
"authentication_type": {"key": "AuthenticationType", "type": "str"},
}
_xml_map = {"name": "CreateSessionRequest"}

def __init__(self, *, authentication_type: Union[str, "_models.AuthenticationType"], **kwargs: Any) -> None:
"""
:keyword authentication_type: The type of authentication required to create the session. The
only type currently supported is HMAC. Required. "HMAC"
:paramtype authentication_type: str or ~azure.storage.blob.models.AuthenticationType
"""
super().__init__(**kwargs)
self.authentication_type = authentication_type


class CreateSessionResponse(_serialization.Model):
"""CreateSessionResponse.

:ivar id: A unique identifier for the created session.
:vartype id: str
:ivar expiration: The time when the session will expire. The format follows RFC 1123.
:vartype expiration: ~datetime.datetime
:ivar authentication_type: The type of authentication required to create the session. The only
type currently supported is HMAC. "HMAC"
:vartype authentication_type: str or ~azure.storage.blob.models.AuthenticationType
:ivar credentials:
:vartype credentials: ~azure.storage.blob.models.SessionCredentials
"""

_attribute_map = {
"id": {"key": "Id", "type": "str"},
"expiration": {"key": "Expiration", "type": "rfc-1123"},
"authentication_type": {"key": "AuthenticationType", "type": "str"},
"credentials": {"key": "Credentials", "type": "SessionCredentials"},
}
_xml_map = {"name": "CreateSessionResult"}

def __init__(
self,
*,
id: Optional[str] = None, # pylint: disable=redefined-builtin
expiration: Optional[datetime.datetime] = None,
authentication_type: Optional[Union[str, "_models.AuthenticationType"]] = None,
credentials: Optional["_models.SessionCredentials"] = None,
**kwargs: Any
) -> None:
"""
:keyword id: A unique identifier for the created session.
:paramtype id: str
:keyword expiration: The time when the session will expire. The format follows RFC 1123.
:paramtype expiration: ~datetime.datetime
:keyword authentication_type: The type of authentication required to create the session. The
only type currently supported is HMAC. "HMAC"
:paramtype authentication_type: str or ~azure.storage.blob.models.AuthenticationType
:keyword credentials:
:paramtype credentials: ~azure.storage.blob.models.SessionCredentials
"""
super().__init__(**kwargs)
self.id = id
self.expiration = expiration
self.authentication_type = authentication_type
self.credentials = credentials


class DelimitedTextConfiguration(_serialization.Model):
"""Groups the settings used for interpreting the blob data if the blob is delimited text
formatted.
Expand Down Expand Up @@ -2483,6 +2561,39 @@ def __init__(
self.if_sequence_number_equal_to = if_sequence_number_equal_to


class SessionCredentials(_serialization.Model):
"""SessionCredentials.

:ivar session_token: An opaque token used to authorize subsequent requests in the session. Must
be treated as a security credential.
:vartype session_token: str
:ivar session_key: Only returned when AuthenticationType is HMAC. A symmetric encryption key
used to sign requests in the session using the Shared Key protocol.
:vartype session_key: str
"""

_attribute_map = {
"session_token": {"key": "SessionToken", "type": "str"},
"session_key": {"key": "SessionKey", "type": "str"},
}
_xml_map = {"name": "Credentials"}

def __init__(
self, *, session_token: Optional[str] = None, session_key: Optional[str] = None, **kwargs: Any
) -> None:
"""
:keyword session_token: An opaque token used to authorize subsequent requests in the session.
Must be treated as a security credential.
:paramtype session_token: str
:keyword session_key: Only returned when AuthenticationType is HMAC. A symmetric encryption key
used to sign requests in the session using the Shared Key protocol.
:paramtype session_key: str
"""
super().__init__(**kwargs)
self.session_token = session_token
self.session_key = session_key


class SignedIdentifier(_serialization.Model):
"""signed identifier.

Expand Down
Loading
Loading