txaws-dev team mailing list archive
-
txaws-dev team
-
Mailing list archive
-
Message #00017
[Merge] lp:~djfroofy/txaws/641620-acls into lp:txaws
Drew Smathers has proposed merging lp:~djfroofy/txaws/641620-acls into lp:txaws.
Requested reviews:
txAWS Developers (txaws-dev)
Related bugs:
#641620 s3 client does not provide ACLs api
https://bugs.launchpad.net/bugs/641620
--
https://code.launchpad.net/~djfroofy/txaws/641620-acls/+merge/35959
Your team txAWS Developers is requested to review the proposed merge of lp:~djfroofy/txaws/641620-acls into lp:txaws.
=== added file 'txaws/s3/acls.py'
--- txaws/s3/acls.py 1970-01-01 00:00:00 +0000
+++ txaws/s3/acls.py 2010-09-19 18:31:04 +0000
@@ -0,0 +1,117 @@
+from txaws.util import XML
+
+PERMISSIONS = (
+ 'FULL_CONTROL',
+ 'WRITE',
+ 'WRITE_ACP',
+ 'READ',
+ 'READ_ACP' )
+
+
+class XMLMixin(object):
+
+ def to_xml(self):
+ return ''.join(self._to_xml())
+
+
+class AccessControlPolicy(XMLMixin):
+
+ def __init__(self, owner=None, access_control_list=()):
+ self.owner = owner
+ self.access_control_list = access_control_list
+
+ def _to_xml(self, buffer=None):
+ if buffer is None:
+ buffer = []
+ buffer.append('<AccessControlPolicy>\n')
+ if self.owner:
+ self.owner._to_xml(buffer=buffer, indent=1)
+ buffer.append(' <AccessControlList>\n')
+ for grant in self.access_control_list:
+ grant._to_xml(buffer=buffer, indent=2)
+ buffer.append(' </AccessControlList>\n'
+ '</AccessControlPolicy>')
+ return buffer
+
+ @classmethod
+ def from_xml(cls, xml_bytes):
+ root = XML(xml_bytes)
+ owner_node = root.find('Owner')
+ owner = Owner(owner_node.findtext('ID'),
+ owner_node.findtext('DisplayName'))
+ acl_node = root.find('AccessControlList')
+ acl = []
+ for grant_node in acl_node.findall('Grant'):
+ grantee_node = grant_node.find('Grantee')
+ grantee = Grantee(grantee_node.findtext('ID'),
+ grantee_node.findtext('DisplayName'))
+ permission = grant_node.findtext('Permission')
+ acl.append(Grant(grantee, permission))
+ return cls(owner, acl)
+
+
+class Grant(XMLMixin):
+
+ def __init__(self, grantee, permission=None):
+ self.grantee = grantee
+ self.permission = permission
+
+ def _set_permission(self, perm):
+ if perm not in PERMISSIONS:
+ raise ValueError('Invalid permission "%s". Must be one of %s' %
+ (perm, ','.join(PERMISSIONS)))
+ self._permission = perm
+
+ def _get_permission(self):
+ return self._permission
+
+ permission = property(_get_permission, _set_permission)
+
+ def _to_xml(self, buffer=None, indent=0):
+ if buffer is None:
+ buffer = []
+ ws = ' ' * (indent * 2)
+ buffer.append(ws + '<Grant>\n')
+ if self.grantee:
+ self.grantee._to_xml(buffer, indent+1)
+ if self.permission:
+ buffer.append('%s <Permission>%s</Permission>\n' % (
+ ws, self.permission))
+ buffer.append(ws + '</Grant>\n')
+ return buffer
+
+
+class Owner(XMLMixin):
+
+ def __init__(self, id, display_name):
+ self.id = id
+ self.display_name = display_name
+
+ def _to_xml(self, buffer=None, indent=0):
+ if buffer is None:
+ buffer = []
+ ws = ' ' * (indent * 2)
+ buffer.append('%s<Owner>\n'
+ '%s <ID>%s</ID>\n'
+ '%s <DisplayName>%s</DisplayName>\n'
+ '%s</Owner>\n' % (
+ ws, ws, self.id, ws,
+ self.display_name, ws))
+ return buffer
+
+class Grantee(Owner):
+
+ def _to_xml(self, buffer=None, indent=0):
+ if buffer is None:
+ buffer = []
+ ws = ' ' * (indent * 2)
+ buffer.append('%s<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
+ ' xsi:type="CanonicalUser">\n'
+ '%s <ID>%s</ID>\n'
+ '%s <DisplayName>%s</DisplayName>\n'
+ '%s</Grantee>\n' % (
+ ws, ws, self.id, ws,
+ self.display_name, ws))
+ return buffer
+
+
=== modified file 'txaws/s3/client.py'
--- txaws/s3/client.py 2009-11-23 00:55:44 +0000
+++ txaws/s3/client.py 2010-09-19 18:31:04 +0000
@@ -19,6 +19,7 @@
from txaws.client.base import BaseClient, BaseQuery, error_wrapper
from txaws.s3 import model
+from txaws.s3 import acls
from txaws.s3.exception import S3Error
from txaws.service import AWSServiceEndpoint, S3_ENDPOINT
from txaws.util import XML, calculate_md5
@@ -177,6 +178,31 @@
name, prefix, marker, max_keys, is_truncated, contents,
common_prefixes)
+
+ def get_bucket_acl(self, bucket):
+ """
+ Get the access control policy for a bucket.
+ """
+ query = self.query_factory(
+ action='GET', creds=self.creds, endpoint=self.endpoint,
+ bucket=bucket, object_name='?acl')
+ return query.submit().addCallback(self._parse_acl)
+
+ def put_bucket_acl(self, bucket, access_control_policy):
+ """
+ Set access control policy on a bucket.
+ """
+ data = access_control_policy.to_xml()
+ query = self.query_factory(
+ action='PUT', creds=self.creds, endpoint=self.endpoint,
+ bucket=bucket, object_name='?acl', data=data)
+ return query.submit().addCallback(self._parse_acl)
+
+
+ def _parse_acl(self, xml_bytes):
+ return acls.AccessControlPolicy.from_xml(xml_bytes)
+
+
def put_object(self, bucket, object_name, data, content_type=None,
metadata={}):
"""
@@ -220,6 +246,14 @@
bucket=bucket, object_name=object_name)
return query.submit()
+ def get_object_acl(self, bucket, object_name):
+ """
+ Get the access control policy for an object.
+ """
+ query = self.query_factory(
+ action='GET', creds=self.creds, endpoint=self.endpoint,
+ bucket=bucket, object_name='%s?acl' % object_name)
+ return query.submit().addCallback(self._parse_acl)
class Query(BaseQuery):
"""A query for submission to the S3 service."""
=== added file 'txaws/s3/tests/test_acls.py'
--- txaws/s3/tests/test_acls.py 1970-01-01 00:00:00 +0000
+++ txaws/s3/tests/test_acls.py 2010-09-19 18:31:04 +0000
@@ -0,0 +1,86 @@
+from twisted.trial.unittest import TestCase
+
+from txaws.testing import payload
+from txaws.s3 import acls
+
+
+class ACLTests(TestCase):
+
+ def test_owner_to_xml(self):
+ owner = acls.Owner(id='8a6925ce4adf588a4f21c32aa379004fef',
+ display_name='BucketOwnersEmail@xxxxxxxxxx')
+ xml_bytes = owner.to_xml()
+ self.assertEquals(xml_bytes, """\
+<Owner>
+ <ID>8a6925ce4adf588a4f21c32aa379004fef</ID>
+ <DisplayName>BucketOwnersEmail@xxxxxxxxxx</DisplayName>
+</Owner>
+""")
+
+
+ def test_grantee_to_xml(self):
+ grantee = acls.Grantee(id='8a6925ce4adf588a4f21c32aa379004fef',
+ display_name='BucketOwnersEmail@xxxxxxxxxx')
+ xml_bytes = grantee.to_xml()
+ self.assertEquals(xml_bytes, """\
+<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
+ <ID>8a6925ce4adf588a4f21c32aa379004fef</ID>
+ <DisplayName>BucketOwnersEmail@xxxxxxxxxx</DisplayName>
+</Grantee>
+""")
+
+
+ def test_grant_to_xml(self):
+ grantee = acls.Grantee(id='8a6925ce4adf588a4f21c32aa379004fef',
+ display_name='BucketOwnersEmail@xxxxxxxxxx')
+ grant = acls.Grant(grantee, 'FULL_CONTROL')
+ xml_bytes = grant.to_xml()
+ self.assertEquals(xml_bytes, """\
+<Grant>
+ <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
+ <ID>8a6925ce4adf588a4f21c32aa379004fef</ID>
+ <DisplayName>BucketOwnersEmail@xxxxxxxxxx</DisplayName>
+ </Grantee>
+ <Permission>FULL_CONTROL</Permission>
+</Grant>
+""")
+
+ def test_access_control_policy_to_xml(self):
+
+ grantee = acls.Grantee(id='8a6925ce4adf588a4f21c32aa379004fef',
+ display_name='foo@xxxxxxxxxxx')
+ grant1 = acls.Grant(grantee, 'FULL_CONTROL')
+ grantee = acls.Grantee(id='8a6925ce4adf588a4f21c32aa37900feed',
+ display_name='bar@xxxxxxxxxxx')
+ grant2 = acls.Grant(grantee, 'READ')
+ owner = acls.Owner(id='8a6925ce4adf588a4f21c32aa37900beef',
+ display_name='baz@xxxxxxxxxxx')
+ acp = acls.AccessControlPolicy(owner=owner, access_control_list=[grant1, grant2])
+ xml_bytes = acp.to_xml()
+ self.assertEquals(xml_bytes, payload.sample_access_control_policy_result)
+
+
+ def test_permission_enum(self):
+ grantee = acls.Grantee(id='8a6925ce4adf588a4f21c32aa379004fef',
+ display_name='BucketOwnersEmail@xxxxxxxxxx')
+ acls.Grant(grantee, 'FULL_CONTROL')
+ acls.Grant(grantee, 'WRITE')
+ acls.Grant(grantee, 'WRITE_ACP')
+ acls.Grant(grantee, 'READ')
+ acls.Grant(grantee, 'READ_ACP')
+ self.assertRaises(ValueError, acls.Grant, grantee, 'GO_HOG_WILD')
+
+ def test_from_xml(self):
+ policy = acls.AccessControlPolicy.from_xml(payload.sample_access_control_policy_result)
+ self.assertEquals(policy.owner.id, '8a6925ce4adf588a4f21c32aa37900beef')
+ self.assertEquals(policy.owner.display_name, 'baz@xxxxxxxxxxx')
+ self.assertEquals(len(policy.access_control_list), 2)
+ grant1 = policy.access_control_list[0]
+ self.assertEquals(grant1.grantee.id, '8a6925ce4adf588a4f21c32aa379004fef')
+ self.assertEquals(grant1.grantee.display_name, 'foo@xxxxxxxxxxx')
+ self.assertEquals(grant1.permission, 'FULL_CONTROL')
+ grant2 = policy.access_control_list[1]
+ self.assertEquals(grant2.grantee.id, '8a6925ce4adf588a4f21c32aa37900feed')
+ self.assertEquals(grant2.grantee.display_name, 'bar@xxxxxxxxxxx')
+ self.assertEquals(grant2.permission, 'READ')
+
=== modified file 'txaws/s3/tests/test_client.py'
--- txaws/s3/tests/test_client.py 2009-11-23 00:55:44 +0000
+++ txaws/s3/tests/test_client.py 2010-09-19 18:31:04 +0000
@@ -2,6 +2,7 @@
from txaws.credentials import AWSCredentials
from txaws.s3 import client
+from txaws.s3 import acls
from txaws.service import AWSServiceEndpoint
from txaws.testing import payload
from txaws.testing.base import TXAWSTestCase
@@ -195,6 +196,62 @@
creds = AWSCredentials("foo", "bar")
s3 = client.S3Client(creds, query_factory=StubQuery)
return s3.delete_bucket("mybucket")
+
+ def test_put_bucket_acl(self):
+
+ class StubQuery(client.Query):
+
+ def __init__(query, action, creds, endpoint, bucket=None, object_name=None,
+ data=''):
+ super(StubQuery, query).__init__(
+ action=action, creds=creds, bucket=bucket, object_name=object_name,
+ data=data)
+ self.assertEquals(action, "PUT")
+ self.assertEqual(creds.access_key, "foo")
+ self.assertEqual(creds.secret_key, "bar")
+ self.assertEqual(query.bucket, "mybucket")
+ self.assertEqual(query.object_name, '?acl')
+ self.assertEqual(query.data, payload.sample_access_control_policy_result)
+ self.assertEqual(query.metadata, {})
+
+ def submit(query, url_context=None):
+ return succeed(payload.sample_access_control_policy_result)
+
+ def check_result(result):
+ self.assert_(isinstance(result, acls.AccessControlPolicy))
+
+ creds = AWSCredentials("foo", "bar")
+ s3 = client.S3Client(creds, query_factory=StubQuery)
+ policy = acls.AccessControlPolicy.from_xml(payload.sample_access_control_policy_result)
+ return s3.put_bucket_acl("mybucket", policy).addCallback(check_result)
+
+ def test_get_bucket_acl(self):
+
+ class StubQuery(client.Query):
+
+ def __init__(query, action, creds, endpoint, bucket=None, object_name=None,
+ data=''):
+ super(StubQuery, query).__init__(
+ action=action, creds=creds, bucket=bucket, object_name=object_name,
+ data=data)
+ self.assertEquals(action, "GET")
+ self.assertEqual(creds.access_key, "foo")
+ self.assertEqual(creds.secret_key, "bar")
+ self.assertEqual(query.bucket, "mybucket")
+ self.assertEqual(query.object_name, '?acl')
+ self.assertEqual(query.data, '')
+ self.assertEqual(query.metadata, {})
+
+ def submit(query, url_context=None):
+ return succeed(payload.sample_access_control_policy_result)
+
+ def check_result(result):
+ self.assert_(isinstance(result, acls.AccessControlPolicy))
+
+ creds = AWSCredentials("foo", "bar")
+ s3 = client.S3Client(creds, query_factory=StubQuery)
+ policy = acls.AccessControlPolicy.from_xml(payload.sample_access_control_policy_result)
+ return s3.get_bucket_acl("mybucket").addCallback(check_result)
def test_put_object(self):
@@ -297,6 +354,33 @@
s3 = client.S3Client(creds, query_factory=StubQuery)
return s3.delete_object("mybucket", "objectname")
+ def test_get_object_acl(self):
+
+ class StubQuery(client.Query):
+
+ def __init__(query, action, creds, endpoint, bucket=None, object_name=None,
+ data=''):
+ super(StubQuery, query).__init__(
+ action=action, creds=creds, bucket=bucket, object_name=object_name,
+ data=data)
+ self.assertEquals(action, "GET")
+ self.assertEqual(creds.access_key, "foo")
+ self.assertEqual(creds.secret_key, "bar")
+ self.assertEqual(query.bucket, "mybucket")
+ self.assertEqual(query.object_name, 'myobject?acl')
+ self.assertEqual(query.data, '')
+ self.assertEqual(query.metadata, {})
+
+ def submit(query, url_context=None):
+ return succeed(payload.sample_access_control_policy_result)
+
+ def check_result(result):
+ self.assert_(isinstance(result, acls.AccessControlPolicy))
+
+ creds = AWSCredentials("foo", "bar")
+ s3 = client.S3Client(creds, query_factory=StubQuery)
+ policy = acls.AccessControlPolicy.from_xml(payload.sample_access_control_policy_result)
+ return s3.get_object_acl("mybucket", "myobject").addCallback(check_result)
class QueryTestCase(TXAWSTestCase):
=== modified file 'txaws/testing/payload.py'
--- txaws/testing/payload.py 2010-07-20 10:15:48 +0000
+++ txaws/testing/payload.py 2010-09-19 18:31:04 +0000
@@ -903,3 +903,28 @@
<AWSAccessKeyId>SOMEKEYID</AWSAccessKeyId>
</Error>
"""
+
+sample_access_control_policy_result = """\
+<AccessControlPolicy>
+ <Owner>
+ <ID>8a6925ce4adf588a4f21c32aa37900beef</ID>
+ <DisplayName>baz@xxxxxxxxxxx</DisplayName>
+ </Owner>
+ <AccessControlList>
+ <Grant>
+ <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
+ <ID>8a6925ce4adf588a4f21c32aa379004fef</ID>
+ <DisplayName>foo@xxxxxxxxxxx</DisplayName>
+ </Grantee>
+ <Permission>FULL_CONTROL</Permission>
+ </Grant>
+ <Grant>
+ <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
+ <ID>8a6925ce4adf588a4f21c32aa37900feed</ID>
+ <DisplayName>bar@xxxxxxxxxxx</DisplayName>
+ </Grantee>
+ <Permission>READ</Permission>
+ </Grant>
+ </AccessControlList>
+</AccessControlPolicy>"""
+