Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File rest parity #7001

Merged
merged 14 commits into from
Sep 9, 2019
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------

import sys
xiafu-msft marked this conversation as resolved.
Show resolved Hide resolved

if sys.version_info < (3,):
def _str(value):
if isinstance(value, unicode): # pylint: disable=undefined-variable
return value.encode('utf-8')

return str(value)
else:
_str = str


def _to_utc_datetime(value):
return value.strftime('%Y-%m-%dT%H:%M:%SZ')
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,13 @@
# license information.
# --------------------------------------------------------------------------

import sys
from datetime import date

from .parser import _str, _to_utc_datetime
from .constants import X_MS_VERSION
from . import sign_string, url_quote


if sys.version_info < (3,):
def _str(value):
if isinstance(value, unicode): # pylint: disable=undefined-variable
return value.encode('utf-8')

return str(value)
else:
_str = str


def _to_utc_datetime(value):
return value.strftime('%Y-%m-%dT%H:%M:%SZ')


class QueryStringConstants(object):
SIGNED_SIGNATURE = 'sig'
SIGNED_PERMISSION = 'sp'
Expand Down
6 changes: 4 additions & 2 deletions sdk/storage/azure-storage-file/azure/storage/file/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
AccessPolicy,
FilePermissions,
SharePermissions,
ContentSettings)
ContentSettings,
NTFSAttributes)


__version__ = VERSION
Expand Down Expand Up @@ -60,5 +61,6 @@
'FileProperties',
'ContentSettings',
'Handle',
'HandlesPaged'
'HandlesPaged',
'NTFSAttributes'
]
18 changes: 18 additions & 0 deletions sdk/storage/azure-storage-file/azure/storage/file/_deserialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,21 @@ def deserialize_file_stream(response, obj, headers):
file_properties = deserialize_file_properties(response, obj, headers)
obj.properties = file_properties
return response.location_mode, obj


def deserialize_permission(response, obj, headers): # pylint: disable=unused-argument
'''
Extracts out file permission
'''

return obj.permission


def deserialize_permission_key(response, obj, headers): # pylint: disable=unused-argument
'''
Extracts out file permission key
'''

if response is None or headers is None:
return None
return headers.get('x-ms-file-permission-key', None)
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,12 @@ async def create_snapshot(self, timeout=None, metadata=None, *, cls=None, **kwar
return cls(response, None, response_headers)
create_snapshot.metadata = {'url': '/{shareName}'}

async def create_permission(self, timeout=None, *, cls=None, **kwargs):
async def create_permission(self, share_permission, timeout=None, *, cls=None, **kwargs):
"""Create a permission (a security descriptor).

:param share_permission: A permission (a security descriptor) at the
share level.
:type share_permission: ~azure.storage.file.models.SharePermission
:param timeout: The timeout parameter is expressed in seconds. For
more information, see <a
href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting
Expand Down Expand Up @@ -327,10 +330,14 @@ async def create_permission(self, timeout=None, *, cls=None, **kwargs):

# Construct headers
header_parameters = {}
header_parameters['Content-Type'] = 'application/json; charset=utf-8'
header_parameters['x-ms-version'] = self._serialize.header("self._config.version", self._config.version, 'str')

# Construct body
body_content = self._serialize.body(share_permission, 'SharePermission', is_xml=False)

# Construct and send request
request = self._client.put(url, query_parameters, header_parameters)
request = self._client.put(url, query_parameters, header_parameters, body_content)
pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs)
response = pipeline_response.http_response

Expand Down Expand Up @@ -363,8 +370,8 @@ async def get_permission(self, file_permission_key=None, timeout=None, *, cls=No
:type timeout: int
:param callable cls: A custom type or function that will be passed the
direct response
:return: None or the result of cls(response)
:rtype: None
:return: SharePermission or the result of cls(response)
:rtype: ~azure.storage.file.models.SharePermission
:raises:
:class:`StorageErrorException<azure.storage.file.models.StorageErrorException>`
"""
Expand All @@ -387,6 +394,7 @@ async def get_permission(self, file_permission_key=None, timeout=None, *, cls=No

# Construct headers
header_parameters = {}
header_parameters['Accept'] = 'application/json'
if file_permission_key is not None:
header_parameters['x-ms-file-permission-key'] = self._serialize.header("file_permission_key", file_permission_key, 'str')
header_parameters['x-ms-version'] = self._serialize.header("self._config.version", self._config.version, 'str')
Expand All @@ -400,14 +408,21 @@ async def get_permission(self, file_permission_key=None, timeout=None, *, cls=No
map_error(status_code=response.status_code, response=response, error_map=error_map)
raise models.StorageErrorException(response, self._deserialize)

if cls:
response_headers = {
header_dict = {}
deserialized = None
if response.status_code == 200:
deserialized = self._deserialize('SharePermission', response)
header_dict = {
'x-ms-request-id': self._deserialize('str', response.headers.get('x-ms-request-id')),
'x-ms-version': self._deserialize('str', response.headers.get('x-ms-version')),
'Date': self._deserialize('rfc-1123', response.headers.get('Date')),
'x-ms-error-code': self._deserialize('str', response.headers.get('x-ms-error-code')),
}
return cls(response, None, response_headers)

if cls:
return cls(response, deserialized, header_dict)

return deserialized
get_permission.metadata = {'url': '/{shareName}'}

async def set_quota(self, timeout=None, quota=None, *, cls=None, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from ._models_py3 import Range
from ._models_py3 import RetentionPolicy
from ._models_py3 import ShareItem
from ._models_py3 import SharePermission
from ._models_py3 import ShareProperties
from ._models_py3 import ShareStats
from ._models_py3 import SignedIdentifier
Expand All @@ -47,6 +48,7 @@
from ._models import Range
from ._models import RetentionPolicy
from ._models import ShareItem
from ._models import SharePermission
from ._models import ShareProperties
from ._models import ShareStats
from ._models import SignedIdentifier
Expand Down Expand Up @@ -77,6 +79,7 @@
'Range',
'RetentionPolicy',
'ShareItem',
'SharePermission',
'ShareProperties',
'ShareStats',
'SignedIdentifier',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,32 @@ def __init__(self, **kwargs):
self.metadata = kwargs.get('metadata', None)


class SharePermission(Model):
"""A permission (a security descriptor) at the share level.

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

:param permission: Required. The permission in the Security Descriptor
Definition Language (SDDL).
:type permission: str
"""

_validation = {
'permission': {'required': True},
}

_attribute_map = {
'permission': {'key': 'permission', 'type': 'str', 'xml': {'name': 'permission'}},
}

_xml_map = {
}

def __init__(self, **kwargs):
super(SharePermission, self).__init__(**kwargs)
self.permission = kwargs.get('permission', None)


class ShareProperties(Model):
"""Properties of a share.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,32 @@ def __init__(self, *, name: str, properties, snapshot: str=None, metadata=None,
self.metadata = metadata


class SharePermission(Model):
"""A permission (a security descriptor) at the share level.

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

:param permission: Required. The permission in the Security Descriptor
Definition Language (SDDL).
:type permission: str
"""

_validation = {
'permission': {'required': True},
}

_attribute_map = {
'permission': {'key': 'permission', 'type': 'str', 'xml': {'name': 'permission'}},
}

_xml_map = {
}

def __init__(self, *, permission: str, **kwargs) -> None:
super(SharePermission, self).__init__(**kwargs)
self.permission = permission


class ShareProperties(Model):
"""Properties of a share.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,12 @@ def create_snapshot(self, timeout=None, metadata=None, cls=None, **kwargs):
return cls(response, None, response_headers)
create_snapshot.metadata = {'url': '/{shareName}'}

def create_permission(self, timeout=None, cls=None, **kwargs):
def create_permission(self, share_permission, timeout=None, cls=None, **kwargs):
"""Create a permission (a security descriptor).

:param share_permission: A permission (a security descriptor) at the
share level.
:type share_permission: ~azure.storage.file.models.SharePermission
:param timeout: The timeout parameter is expressed in seconds. For
more information, see <a
href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting
Expand Down Expand Up @@ -327,10 +330,14 @@ def create_permission(self, timeout=None, cls=None, **kwargs):

# Construct headers
header_parameters = {}
header_parameters['Content-Type'] = 'application/json; charset=utf-8'
header_parameters['x-ms-version'] = self._serialize.header("self._config.version", self._config.version, 'str')

# Construct body
body_content = self._serialize.body(share_permission, 'SharePermission', is_xml=False)

# Construct and send request
request = self._client.put(url, query_parameters, header_parameters)
request = self._client.put(url, query_parameters, header_parameters, body_content)
pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs)
response = pipeline_response.http_response

Expand Down Expand Up @@ -363,8 +370,8 @@ def get_permission(self, file_permission_key=None, timeout=None, cls=None, **kwa
:type timeout: int
:param callable cls: A custom type or function that will be passed the
direct response
:return: None or the result of cls(response)
:rtype: None
:return: SharePermission or the result of cls(response)
:rtype: ~azure.storage.file.models.SharePermission
:raises:
:class:`StorageErrorException<azure.storage.file.models.StorageErrorException>`
"""
Expand All @@ -387,6 +394,7 @@ def get_permission(self, file_permission_key=None, timeout=None, cls=None, **kwa

# Construct headers
header_parameters = {}
header_parameters['Accept'] = 'application/json'
if file_permission_key is not None:
header_parameters['x-ms-file-permission-key'] = self._serialize.header("file_permission_key", file_permission_key, 'str')
header_parameters['x-ms-version'] = self._serialize.header("self._config.version", self._config.version, 'str')
Expand All @@ -400,14 +408,21 @@ def get_permission(self, file_permission_key=None, timeout=None, cls=None, **kwa
map_error(status_code=response.status_code, response=response, error_map=error_map)
raise models.StorageErrorException(response, self._deserialize)

if cls:
response_headers = {
header_dict = {}
deserialized = None
if response.status_code == 200:
deserialized = self._deserialize('SharePermission', response)
header_dict = {
'x-ms-request-id': self._deserialize('str', response.headers.get('x-ms-request-id')),
'x-ms-version': self._deserialize('str', response.headers.get('x-ms-version')),
'Date': self._deserialize('rfc-1123', response.headers.get('Date')),
'x-ms-error-code': self._deserialize('str', response.headers.get('x-ms-error-code')),
}
return cls(response, None, response_headers)

if cls:
return cls(response, deserialized, header_dict)

return deserialized
get_permission.metadata = {'url': '/{shareName}'}

def set_quota(self, timeout=None, quota=None, cls=None, **kwargs):
Expand Down
48 changes: 48 additions & 0 deletions sdk/storage/azure-storage-file/azure/storage/file/_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------

from datetime import datetime, timedelta
xiafu-msft marked this conversation as resolved.
Show resolved Hide resolved

_ERROR_TOO_MANY_FILE_PERMISSIONS = 'file_permission and file_permission_key should not be set at the same time'
_FILE_PERMISSION_TOO_LONG = 'Size of file_permission is too large. file_permission should be <=8KB, else' \
'please use file_permission_key'


def _get_file_permission(file_permission, file_permission_key, default_permission):
# if file_permission and file_permission_key are both empty, then use the default_permission
# value as file permission, file_permission size should be <= 8KB, else file permission_key should be used
empty_file_permission = not file_permission
empty_file_permission_key = not file_permission_key
file_permission_size_too_big = False if file_permission is None \
else len(str(file_permission).encode('utf-8')) > 8 * 1024

if file_permission_size_too_big:
raise ValueError(_FILE_PERMISSION_TOO_LONG)

if empty_file_permission:
if empty_file_permission_key:
return default_permission

return None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just curious: if file_permission is empty and file_permission_key is not, shouldn't this return the key?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No - if you check the function that call this one - file_permission_key is used independently. I checked as I had the same question ;)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for clarifying. 😄


if empty_file_permission_key:
return file_permission

raise ValueError(_ERROR_TOO_MANY_FILE_PERMISSIONS)


def _parse_datetime_from_str(string_datetime):
if not string_datetime:
return None
dt, _, us = string_datetime.partition(".")
dt = datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S")
us = int(us[:-2]) # microseconds
datetime_obj = dt + timedelta(microseconds=us)
return datetime_obj


def _datetime_to_str(datetime_obj):
return datetime_obj if isinstance(datetime_obj, str) else datetime_obj.isoformat() + '0Z'
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sys


X_MS_VERSION = '2018-03-28'
X_MS_VERSION = '2019-02-02'
annatisch marked this conversation as resolved.
Show resolved Hide resolved

# Socket timeout in seconds
DEFAULT_SOCKET_TIMEOUT = 20
Expand Down
Loading