Browse code

* s3cmd: Pre-parse ACL parameters in OptionS3ACL()

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

Michal Ludvig authored on 2010/05/20 20:42:47
Showing 2 changed files
... ...
@@ -3,6 +3,7 @@
3 3
 	* s3cmd, S3/ACL.py, S3/Config.py: Support for --acl-grant
4 4
 	  and --acl-revoke (contributed by Timothee Linden)
5 5
 	* s3cmd: Couple of fixes on top of the above commit.
6
+	* s3cmd: Pre-parse ACL parameters in OptionS3ACL()
6 7
 
7 8
 2010-05-20  Michal Ludvig  <mludvig@logix.net.nz>
8 9
 
... ...
@@ -1082,39 +1082,43 @@ def cmd_sync(args):
1082 1082
 
1083 1083
 def cmd_setacl(args):
1084 1084
 	def _update_acl(uri, seq_label = ""):
1085
+		something_changed = False
1085 1086
 		acl = s3.get_acl(uri)
1086 1087
 		debug(u"acl: %s - %r" % (uri, acl.grantees))
1087 1088
 		if cfg.acl_public == True:
1088 1089
 			if acl.isAnonRead():
1089 1090
 				info(u"%s: already Public, skipping %s" % (uri, seq_label))
1090
-				if not cfg.acl_grants and not cfg.acl_revokes:
1091
-					return
1092 1091
 			else:
1093 1092
 				acl.grantAnonRead()
1093
+				something_changed = True
1094 1094
 		elif cfg.acl_public == False: # we explicitely check for False, because it could be None
1095 1095
 			if not acl.isAnonRead():
1096 1096
 				info(u"%s: already Private, skipping %s" % (uri, seq_label))
1097
-				if not cfg.acl_grants and not cfg.acl_revokes:
1098
-					return
1099 1097
 			else:
1100 1098
 				acl.revokeAnonRead()
1099
+				something_changed = True
1101 1100
 
1102 1101
 		# update acl with arguments
1103 1102
 		# grant first and revoke later, because revoke has priority
1104 1103
 		if cfg.acl_grants:
1104
+			something_changed = True
1105 1105
 			for grant in cfg.acl_grants:
1106 1106
 				acl.grant(**grant);
1107 1107
 
1108 1108
 		if cfg.acl_revokes:
1109
+			something_changed = True
1109 1110
 			for revoke in cfg.acl_revokes:
1110 1111
 				acl.revoke(**revoke);
1111 1112
 
1113
+		if not something_changed:
1114
+			return
1115
+
1112 1116
 		retsponse = s3.set_acl(uri, acl)
1113 1117
 		if retsponse['status'] == 200:
1114
-			if cfg.acl_public == True or cfg.acl_public == False:
1118
+			if cfg.acl_public in (True, False):
1115 1119
 				output(u"%s: ACL set to %s  %s" % (uri, set_to_acl, seq_label))
1116 1120
 			else:
1117
-				output(u"ACL updated")
1121
+				output(u"%s: ACL updated" % uri)
1118 1122
 
1119 1123
 	s3 = S3(cfg)
1120 1124
 
... ...
@@ -1507,9 +1511,26 @@ class OptionMimeType(Option):
1507 1507
 			return value
1508 1508
 		raise OptionValueError("option %s: invalid MIME-Type format: %r" % (opt, value))
1509 1509
 
1510
-	TYPES = Option.TYPES + ("mimetype",)
1510
+class OptionS3ACL(Option):
1511
+	def check_s3acl(option, opt, value):
1512
+		permissions = ('read', 'write', 'read_acp', 'write_acp', 'full_control', 'all')
1513
+		try:
1514
+			permission, grantee = re.compile("^(\w+):(.+)$", re.IGNORECASE).match(value).groups()
1515
+			if not permission or not grantee:
1516
+				raise
1517
+			if permission in permissions:
1518
+				return { 'name' : grantee, 'permission' : permission.upper() }
1519
+			else:
1520
+				raise OptionValueError("option %s: invalid S3 ACL permission: %s (valid values: %s)" % 
1521
+					(opt, permission, ", ".join(permissions)))
1522
+		except:
1523
+			raise OptionValueError("option %s: invalid S3 ACL format: %r" % (opt, value))
1524
+
1525
+class OptionAll(OptionMimeType, OptionS3ACL):
1511 1526
 	TYPE_CHECKER = copy(Option.TYPE_CHECKER)
1512
-	TYPE_CHECKER["mimetype"] = check_mimetype
1527
+	TYPE_CHECKER["mimetype"] = OptionMimeType.check_mimetype
1528
+	TYPE_CHECKER["s3acl"] = OptionS3ACL.check_s3acl
1529
+	TYPES = Option.TYPES + ("mimetype", "s3acl")
1513 1530
 
1514 1531
 class MyHelpFormatter(IndentedHelpFormatter):
1515 1532
 	def format_epilog(self, epilog):
... ...
@@ -1530,7 +1551,7 @@ def main():
1530 1530
 			commands[cmd["cmd"]] = cmd
1531 1531
 
1532 1532
 	default_verbosity = Config().verbosity
1533
-	optparser = OptionParser(option_class=OptionMimeType, formatter=MyHelpFormatter())
1533
+	optparser = OptionParser(option_class=OptionAll, formatter=MyHelpFormatter())
1534 1534
 	#optparser.disable_interspersed_args()
1535 1535
 
1536 1536
 	config_file = None
... ...
@@ -1559,8 +1580,8 @@ def main():
1559 1559
 	optparser.add_option("-r", "--recursive", dest="recursive", action="store_true", help="Recursive upload, download or removal.")
1560 1560
 	optparser.add_option("-P", "--acl-public", dest="acl_public", action="store_true", help="Store objects with ACL allowing read for anyone.")
1561 1561
 	optparser.add_option(      "--acl-private", dest="acl_public", action="store_false", help="Store objects with default ACL allowing access for you only.")
1562
-	optparser.add_option(      "--acl-grant", dest="acl_grants", action="append", metavar="PERMISSION:EMAIL or USER_CANONICAL_ID", help="Grant stated permission to a given amazon user. Permission is one of: read, write, read_acp, write_acp, full_control, all")
1563
-	optparser.add_option(      "--acl-revoke", dest="acl_revokes", action="append", metavar="PERMISSION:USER_CANONICAL_ID", help="Revoke stated permission for a given amazon user. Permission is one of: read, write, read_acp, wr     ite_acp, full_control, all")
1562
+	optparser.add_option(      "--acl-grant", dest="acl_grants", type="s3acl", action="append", metavar="PERMISSION:EMAIL or USER_CANONICAL_ID", help="Grant stated permission to a given amazon user. Permission is one of: read, write, read_acp, write_acp, full_control, all")
1563
+	optparser.add_option(      "--acl-revoke", dest="acl_revokes", type="s3acl", action="append", metavar="PERMISSION:USER_CANONICAL_ID", help="Revoke stated permission for a given amazon user. Permission is one of: read, write, read_acp, wr     ite_acp, full_control, all")
1564 1564
 
1565 1565
 	optparser.add_option(      "--delete-removed", dest="delete_removed", action="store_true", help="Delete remote objects with no corresponding local file [sync]")
1566 1566
 	optparser.add_option(      "--no-delete-removed", dest="delete_removed", action="store_false", help="Don't delete remote objects.")
... ...
@@ -1671,28 +1692,14 @@ def main():
1671 1671
 			debug(u"Updating Config.Config extra_headers[%s] -> %s" % (key.strip(), val.strip()))
1672 1672
 			cfg.extra_headers[key.strip()] = val.strip()
1673 1673
 
1674
-
1675
-	permission_re = "(?P<PERMISSION>read(_acp)?|write(_acp)?|full_control|all)"
1676
-
1674
+	## --acl-grant/--acl-revoke arguments are pre-parsed by OptionS3ACL()
1677 1675
 	if options.acl_grants:
1678
-		r_acl_grant = re.compile("^%s:(?P<NAME>.+)$" % permission_re, re.IGNORECASE)
1679 1676
 		for grant in options.acl_grants:
1680
-			is_data = r_acl_grant.match(grant)
1681
-			if is_data:
1682
-				data = is_data.groupdict()
1683
-				cfg.acl_grants.append({'name': data['NAME'].lower(), 'permission': data["PERMISSION"].upper()})
1684
-			else:
1685
-				warning(u"skipped invalid --acl-grant option: [%s]" % grant)
1677
+			cfg.acl_grants.append(grant)
1686 1678
 
1687 1679
 	if options.acl_revokes:
1688
-		r_acl_revoke = re.compile("^%s:(?P<NAME>.+)$" % permission_re, re.IGNORECASE)
1689
-		for revoke in options.acl_revokes:
1690
-			is_data = r_acl_revoke.match(revoke)
1691
-			if is_data:
1692
-				data = is_data.groupdict()
1693
-				cfg.acl_revokes.append({'name': data['NAME'].lower(), 'permission': data["PERMISSION"].upper()})
1694
-			else:
1695
-				warning(u"skipped invalid --acl-revoke option: [%s]" % revoke)
1680
+		for grant in options.acl_revokes:
1681
+			cfg.acl_revokes.append(grant)
1696 1682
 
1697 1683
 	## Update Config with other parameters
1698 1684
 	for option in cfg.option_list():