Browse code

* s3cmd: New command 'setacl'. * S3/S3.py: Implemented set_acl(). * S3/ACL.py: Fill in <Owner/> tag in ACL XML. * NEWS: Info about 'setacl'.

git-svn-id: https://s3tools.svn.sourceforge.net/svnroot/s3tools/s3cmd/trunk@332 830e0280-6d2a-0410-9c65-932aecc39d9d

Michal Ludvig authored on 2009/01/07 19:22:56
Showing 5 changed files
... ...
@@ -1,5 +1,12 @@
1 1
 2009-01-07  Michal Ludvig  <michal@logix.cz>
2 2
 
3
+	* s3cmd: New command 'setacl'.
4
+	* S3/S3.py: Implemented set_acl().
5
+	* S3/ACL.py: Fill in <Owner/> tag in ACL XML.
6
+	* NEWS: Info about 'setacl'.
7
+
8
+2009-01-07  Michal Ludvig  <michal@logix.cz>
9
+
3 10
 	* s3cmd: Factored remote_keys generation from cmd_object_get()
4 11
 	  to fetch_remote_keys().
5 12
 	* s3cmd: Display Public URL in 'info' for AnonRead objects.
... ...
@@ -1,3 +1,7 @@
1
+s3cmd 0.9.9-pre5
2
+================
3
+* New command 'setacl' for setting ACL on existing objects.
4
+
1 5
 s3cmd 0.9.9-pre4 - 2008-12-30
2 6
 ================
3 7
 * Support for non-recursive [ls]
... ...
@@ -51,18 +51,25 @@ class GranteeAnonRead(Grantee):
51 51
 	permission = "READ"
52 52
 
53 53
 class ACL(object):
54
-	EMPTY_ACL = "<AccessControlPolicy><AccessControlList></AccessControlList></AccessControlPolicy>"
54
+	EMPTY_ACL = "<AccessControlPolicy><Owner><ID></ID></Owner><AccessControlList></AccessControlList></AccessControlPolicy>"
55 55
 
56 56
 	grantees = []
57
+	owner_id = ""
58
+	owner_nick = ""
57 59
 
58 60
 	def __init__(self, xml = None):
59 61
 		if not xml:
60 62
 			xml = ACL.EMPTY_ACL
61
-		self.tree = getTreeFromXml(xml)
62
-		self.parseGrants()
63
-	
64
-	def parseGrants(self):
65
-		for grant in self.tree.findall(".//Grant"):
63
+		tree = getTreeFromXml(xml)
64
+		self.parseOwner(tree)
65
+		self.parseGrants(tree)
66
+
67
+	def parseOwner(self, tree):
68
+		self.owner_id = tree.findtext(".//Owner//ID")
69
+		self.owner_nick = tree.findtext(".//Owner//DisplayName")
70
+
71
+	def parseGrants(self, tree):
72
+		for grant in tree.findall(".//Grant"):
66 73
 			grantee = Grantee()
67 74
 			g = grant.find(".//Grantee")
68 75
 			grantee.xsi_type = g.attrib['{http://www.w3.org/2001/XMLSchema-instance}type']
... ...
@@ -87,6 +94,9 @@ class ACL(object):
87 87
 			acl[user] = grantee.permission
88 88
 		return acl
89 89
 
90
+	def getOwner(self):
91
+		return { 'id' : self.owner_id, 'nick' : self.owner_nick }
92
+
90 93
 	def isAnonRead(self):
91 94
 		for grantee in self.grantees:
92 95
 			if grantee.isAnonRead():
... ...
@@ -103,6 +113,8 @@ class ACL(object):
103 103
 	def __str__(self):
104 104
 		tree = getTreeFromXml(ACL.EMPTY_ACL)
105 105
 		tree.attrib['xmlns'] = "http://s3.amazonaws.com/doc/2006-03-01/"
106
+		owner = tree.find(".//Owner//ID")
107
+		owner.text = self.owner_id
106 108
 		acl = tree.find(".//AccessControlList")
107 109
 		for grantee in self.grantees:
108 110
 			acl.append(grantee.getElement())
... ...
@@ -256,6 +256,17 @@ class S3(object):
256 256
 		acl = ACL(response['data'])
257 257
 		return acl
258 258
 
259
+	def set_acl(self, uri, acl):
260
+		if uri.has_object():
261
+			request = self.create_request("OBJECT_PUT", uri = uri, extra = "?acl")
262
+		else:
263
+			request = self.create_request("BUCKET_CREATE", bucket = uri.bucket(), extra = "?acl")
264
+
265
+		body = str(acl)
266
+		debug(u"set_acl(%s): acl-xml: %s" % (uri, body))
267
+		response = self.send_request(request, body)
268
+		return response
269
+
259 270
 	## Low level methods
260 271
 	def urlencode_string(self, string):
261 272
 		if type(string) == unicode:
... ...
@@ -884,7 +884,34 @@ def cmd_sync(args):
884 884
 		return cmd_sync_local2remote(src, dst)
885 885
 	if S3Uri(src).type == "s3" and S3Uri(dst).type == "file":
886 886
 		return cmd_sync_remote2local(src, dst)
887
-	
887
+
888
+def cmd_setacl(args):
889
+	s3 = S3(cfg)
890
+
891
+	set_to_acl = cfg.acl_public and "Public" or "Private"
892
+
893
+	remote_keys = fetch_remote_keys(args)
894
+	total_keys = len(remote_keys)
895
+	seq = 0
896
+	for key in remote_keys:
897
+		seq += 1
898
+		seq_label = "[%d of %d]" % (seq, total_keys)
899
+		uri = key['remote_uri']
900
+		acl = s3.get_acl(uri)
901
+		if cfg.acl_public:
902
+			if acl.isAnonRead():
903
+				info(u"%s: already Public, skippingi %s" % (uri, seq_label))
904
+				continue
905
+			acl.grantAnonRead()
906
+		else:
907
+			if not acl.isAnonRead():
908
+				info(u"%s: already Private, skipping %s" % (uri, seq_label))
909
+				continue
910
+			acl.revokeAnonRead()
911
+		retsponse = s3.set_acl(uri, acl)
912
+		if retsponse['status'] == 200:
913
+			output(u"%s: ACL set to %s  %s" % (uri, set_to_acl, seq_label))
914
+
888 915
 def resolve_list(lst, args):
889 916
 	retval = []
890 917
 	for item in lst:
... ...
@@ -1085,7 +1112,7 @@ commands_list = [
1085 1085
 	{"cmd":"info", "label":"Get various information about Buckets or Objects", "param":"s3://BUCKET[/OBJECT]", "func":cmd_info, "argc":1},
1086 1086
 	{"cmd":"cp", "label":"Copy object", "param":"s3://BUCKET1/OBJECT1 s3://BUCKET2[/OBJECT2]", "func":cmd_cp, "argc":2},
1087 1087
 	{"cmd":"mv", "label":"Move object", "param":"s3://BUCKET1/OBJECT1 s3://BUCKET2[/OBJECT2]", "func":cmd_mv, "argc":2},
1088
-	#{"cmd":"setacl", "label":"Modify Access control list for Bucket or Object", "param":"s3://BUCKET[/OBJECT]", "func":cmd_setacl, "argc":1},
1088
+	{"cmd":"setacl", "label":"Modify Access control list for Bucket or Object", "param":"s3://BUCKET[/OBJECT]", "func":cmd_setacl, "argc":1},
1089 1089
 	]
1090 1090
 
1091 1091
 def format_commands(progname):