S3/ACL.py
7f50f846
 ## Amazon S3 - Access Control List representation
 ## Author: Michal Ludvig <michal@logix.cz>
 ##         http://www.logix.cz/michal
 ## License: GPL Version 2
 
cb0bbaef
 from Utils import getTreeFromXml
7f50f846
 
 try:
 	import xml.etree.ElementTree as ET
 except ImportError:
 	import elementtree.ElementTree as ET
 
34399925
 class Grantee(object):
 	ALL_USERS_URI = "http://acs.amazonaws.com/groups/global/AllUsers"
cb0bbaef
 	LOG_DELIVERY_URI = "http://acs.amazonaws.com/groups/s3/LogDelivery"
34399925
 
e3244a8c
 	def __init__(self):
 		self.xsi_type = None
 		self.tag = None
 		self.name = None
 		self.display_name = None
 		self.permission = None
34399925
 
d07b947c
 	def __repr__(self):
 		return 'Grantee("%(tag)s", "%(name)s", "%(permission)s")' % { 
 			"tag" : self.tag, 
 			"name" : self.name, 
 			"permission" : self.permission 
 		}
34399925
 
 	def isAllUsers(self):
 		return self.tag == "URI" and self.name == Grantee.ALL_USERS_URI
 	
 	def isAnonRead(self):
259dab5d
 		return self.isAllUsers() and (self.permission == "READ" or self.permission == "FULL_CONTROL")
d07b947c
 	
 	def getElement(self):
 		el = ET.Element("Grant")
 		grantee = ET.SubElement(el, "Grantee", { 
 			'xmlns:xsi' : 'http://www.w3.org/2001/XMLSchema-instance',
 			'xsi:type' : self.xsi_type
 		})
 		name = ET.SubElement(grantee, self.tag)
 		name.text = self.name
 		permission = ET.SubElement(el, "Permission")
 		permission.text = self.permission
 		return el
34399925
 
 class GranteeAnonRead(Grantee):
e3244a8c
 	def __init__(self):
 		Grantee.__init__(self)
 		self.xsi_type = "Group"
 		self.tag = "URI"
 		self.name = Grantee.ALL_USERS_URI
 		self.permission = "READ"
34399925
 
cb0bbaef
 class GranteeLogDelivery(Grantee):
 	def __init__(self, permission):
 		"""
 		permission must be either READ_ACP or WRITE
 		"""
 		Grantee.__init__(self)
 		self.xsi_type = "Group"
 		self.tag = "URI"
 		self.name = Grantee.LOG_DELIVERY_URI
 		self.permission = permission
 
7f50f846
 class ACL(object):
585c735a
 	EMPTY_ACL = "<AccessControlPolicy><Owner><ID></ID></Owner><AccessControlList></AccessControlList></AccessControlPolicy>"
34399925
 
7f50f846
 	def __init__(self, xml = None):
 		if not xml:
 			xml = ACL.EMPTY_ACL
e3244a8c
 
 		self.grantees = []
 		self.owner_id = ""
 		self.owner_nick = ""
 
585c735a
 		tree = getTreeFromXml(xml)
 		self.parseOwner(tree)
 		self.parseGrants(tree)
 
 	def parseOwner(self, tree):
 		self.owner_id = tree.findtext(".//Owner//ID")
 		self.owner_nick = tree.findtext(".//Owner//DisplayName")
 
 	def parseGrants(self, tree):
 		for grant in tree.findall(".//Grant"):
34399925
 			grantee = Grantee()
 			g = grant.find(".//Grantee")
 			grantee.xsi_type = g.attrib['{http://www.w3.org/2001/XMLSchema-instance}type']
 			grantee.permission = grant.find('Permission').text
 			for el in g:
 				if el.tag == "DisplayName":
 					grantee.display_name = el.text
 				else:
 					grantee.tag = el.tag
 					grantee.name = el.text
d07b947c
 			self.grantees.append(grantee)
34399925
 
 	def getGrantList(self):
68afbd78
 		acl = []
d07b947c
 		for grantee in self.grantees:
34399925
 			if grantee.display_name:
 				user = grantee.display_name
 			elif grantee.isAllUsers():
 				user = "*anon*"
7f50f846
 			else:
34399925
 				user = grantee.name
68afbd78
 			acl.append({'grantee': user, 'permission': grantee.permission})
7f50f846
 		return acl
 
585c735a
 	def getOwner(self):
 		return { 'id' : self.owner_id, 'nick' : self.owner_nick }
 
34399925
 	def isAnonRead(self):
d07b947c
 		for grantee in self.grantees:
34399925
 			if grantee.isAnonRead():
 				return True
 		return False
 	
 	def grantAnonRead(self):
 		if not self.isAnonRead():
cb0bbaef
 			self.appendGrantee(GranteeAnonRead())
34399925
 	
 	def revokeAnonRead(self):
d07b947c
 		self.grantees = [g for g in self.grantees if not g.isAnonRead()]
34399925
 
cb0bbaef
 	def appendGrantee(self, grantee):
 		self.grantees.append(grantee)
 
99b416bc
 	def hasGrant(self, name, permission):
 		name = name.lower()
 		permission = permission.upper()
 
 		for grantee in self.grantees:
 			if grantee.name.lower() == name:
 				if grantee.permission == "FULL_CONTROL":
 					return True
 				elif grantee.permission.upper() == permission:
 					return True
 
 		return False;
 
 	def grant(self, name, permission):
 		if self.hasGrant(name, permission):
 			return
 
 		name = name.lower()
 		permission = permission.upper()
 
 		if "ALL" == permission:
 			permission = "FULL_CONTROL"
 
 		if "FULL_CONTROL" == permission:
 			self.revoke(name, "ALL")
 
 		grantee = Grantee()
 		grantee.name = name
 		grantee.permission = permission
 
 		if  name.find('@') <= -1: # ultra lame attempt to differenciate emails id from canonical ids
 			grantee.xsi_type = "CanonicalUser"
 			grantee.tag = "ID"
 		else:
 			grantee.xsi_type = "AmazonCustomerByEmail"
 			grantee.tag = "EmailAddress"
 				
 		self.appendGrantee(grantee)
 
 
 	def revoke(self, name, permission):
 		name = name.lower()
 		permission = permission.upper()
 
 		if "ALL" == permission:
 			self.grantees = [g for g in self.grantees if not g.name.lower() == name]
 		else:
 			self.grantees = [g for g in self.grantees if not (g.name.lower() == name and g.permission.upper() ==  permission)]
 
 
34399925
 	def __str__(self):
d07b947c
 		tree = getTreeFromXml(ACL.EMPTY_ACL)
 		tree.attrib['xmlns'] = "http://s3.amazonaws.com/doc/2006-03-01/"
585c735a
 		owner = tree.find(".//Owner//ID")
 		owner.text = self.owner_id
d07b947c
 		acl = tree.find(".//AccessControlList")
 		for grantee in self.grantees:
 			acl.append(grantee.getElement())
 		return ET.tostring(tree)
34399925
 
7f50f846
 if __name__ == "__main__":
 	xml = """<?xml version="1.0" encoding="UTF-8"?>
 <AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
 <Owner>
 	<ID>12345678901234567890</ID>
 	<DisplayName>owner-nickname</DisplayName>
 </Owner>
 <AccessControlList>
 	<Grant>
 		<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
 			<ID>12345678901234567890</ID>
 			<DisplayName>owner-nickname</DisplayName>
 		</Grantee>
 		<Permission>FULL_CONTROL</Permission>
 	</Grant>
 	<Grant>
 		<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group">
 			<URI>http://acs.amazonaws.com/groups/global/AllUsers</URI>
 		</Grantee>
 		<Permission>READ</Permission>
 	</Grant>
 </AccessControlList>
 </AccessControlPolicy>
 	"""
 	acl = ACL(xml)
34399925
 	print "Grants:", acl.getGrantList()
 	acl.revokeAnonRead()
 	print "Grants:", acl.getGrantList()
 	acl.grantAnonRead()
 	print "Grants:", acl.getGrantList()
d07b947c
 	print acl