Permalink
Browse files

* s3cmd, S3/AccessLog.py, ...: Added [accesslog] command.

git-svn-id: https://s3tools.svn.sourceforge.net/svnroot/s3tools/s3cmd/trunk@402 830e0280-6d2a-0410-9c65-932aecc39d9d
  • Loading branch information...
1 parent d852cbb commit cb0bbaef88a467be38393569597cd98028db5557 @mludvig mludvig committed Mar 19, 2010
Showing with 223 additions and 11 deletions.
  1. +4 −0 ChangeLog
  2. +4 −0 NEWS
  3. +17 −2 S3/ACL.py
  4. +90 −0 S3/AccessLog.py
  5. +2 −0 S3/Config.py
  6. +41 −1 S3/S3.py
  7. +2 −2 S3/S3Uri.py
  8. +28 −1 S3/Utils.py
  9. +35 −5 s3cmd
View
@@ -1,3 +1,7 @@
+2010-03-19 Michal Ludvig <mludvig@logix.net.nz>
+
+ * s3cmd, S3/AccessLog.py, ...: Added [accesslog] command.
+
2009-12-10 Michal Ludvig <mludvig@logix.net.nz>
* s3cmd: Path separator conversion on Windows hosts.
View
@@ -1,3 +1,7 @@
+s3cmd 0.9.9.92 - ???
+==============
+* Added [accesslog] command. (needs manpage!)
+
s3cmd 0.9.9.91 - 2009-10-08
==============
* Fixed invalid reference to a variable in failed upload handling.
View
@@ -3,7 +3,7 @@
## http://www.logix.cz/michal
## License: GPL Version 2
-from Utils import *
+from Utils import getTreeFromXml
try:
import xml.etree.ElementTree as ET
@@ -12,6 +12,7 @@
class Grantee(object):
ALL_USERS_URI = "http://acs.amazonaws.com/groups/global/AllUsers"
+ LOG_DELIVERY_URI = "http://acs.amazonaws.com/groups/s3/LogDelivery"
def __init__(self):
self.xsi_type = None
@@ -53,6 +54,17 @@ def __init__(self):
self.name = Grantee.ALL_USERS_URI
self.permission = "READ"
+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
+
class ACL(object):
EMPTY_ACL = "<AccessControlPolicy><Owner><ID></ID></Owner><AccessControlList></AccessControlList></AccessControlPolicy>"
@@ -109,11 +121,14 @@ def isAnonRead(self):
def grantAnonRead(self):
if not self.isAnonRead():
- self.grantees.append(GranteeAnonRead())
+ self.appendGrantee(GranteeAnonRead())
def revokeAnonRead(self):
self.grantees = [g for g in self.grantees if not g.isAnonRead()]
+ def appendGrantee(self, grantee):
+ self.grantees.append(grantee)
+
def __str__(self):
tree = getTreeFromXml(ACL.EMPTY_ACL)
tree.attrib['xmlns'] = "http://s3.amazonaws.com/doc/2006-03-01/"
View
@@ -0,0 +1,90 @@
+## Amazon S3 - Access Control List representation
+## Author: Michal Ludvig <michal@logix.cz>
+## http://www.logix.cz/michal
+## License: GPL Version 2
+
+import S3Uri
+from Exceptions import ParameterError
+from Utils import getTreeFromXml
+from ACL import GranteeAnonRead
+
+try:
+ import xml.etree.ElementTree as ET
+except ImportError:
+ import elementtree.ElementTree as ET
+
+__all__ = []
+class AccessLog(object):
+ LOG_DISABLED = "<BucketLoggingStatus></BucketLoggingStatus>"
+ LOG_TEMPLATE = "<LoggingEnabled><TargetBucket></TargetBucket><TargetPrefix></TargetPrefix></LoggingEnabled>"
+
+ def __init__(self, xml = None):
+ if not xml:
+ xml = self.LOG_DISABLED
+ self.tree = getTreeFromXml(xml)
+ self.tree.attrib['xmlns'] = "http://doc.s3.amazonaws.com/2006-03-01"
+
+ def isLoggingEnabled(self):
+ return bool(self.tree.find(".//LoggingEnabled"))
+
+ def disableLogging(self):
+ el = self.tree.find(".//LoggingEnabled")
+ if el:
+ self.tree.remove(el)
+
+ def enableLogging(self, target_prefix_uri):
+ el = self.tree.find(".//LoggingEnabled")
+ if not el:
+ el = getTreeFromXml(self.LOG_TEMPLATE)
+ self.tree.append(el)
+ el.find(".//TargetBucket").text = target_prefix_uri.bucket()
+ el.find(".//TargetPrefix").text = target_prefix_uri.object()
+
+ def targetPrefix(self):
+ if self.isLoggingEnabled():
+ el = self.tree.find(".//LoggingEnabled")
+ target_prefix = "s3://%s/%s" % (
+ self.tree.find(".//LoggingEnabled//TargetBucket").text,
+ self.tree.find(".//LoggingEnabled//TargetPrefix").text)
+ return S3Uri.S3Uri(target_prefix)
+ else:
+ return ""
+
+ def setAclPublic(self, acl_public):
+ le = self.tree.find(".//LoggingEnabled")
+ if not le:
+ raise ParameterError("Logging not enabled, can't set default ACL for logs")
+ tg = le.find(".//TargetGrants")
+ if not acl_public:
+ if not tg:
+ ## All good, it's not been there
+ return
+ else:
+ le.remove(tg)
+ else: # acl_public == True
+ anon_read = GranteeAnonRead().getElement()
+ if not tg:
+ tg = ET.SubElement(le, "TargetGrants")
+ ## What if TargetGrants already exists? We should check if
+ ## AnonRead is there before appending a new one. Later...
+ tg.append(anon_read)
+
+ def isAclPublic(self):
+ raise NotImplementedError()
+
+ def __str__(self):
+ return ET.tostring(self.tree)
+__all__.append("AccessLog")
+
+if __name__ == "__main__":
+ from S3Uri import S3Uri
+ log = AccessLog()
+ print log
+ log.enableLogging(S3Uri("s3://targetbucket/prefix/log-"))
+ print log
+ log.setAclPublic(True)
+ print log
+ log.setAclPublic(False)
+ print log
+ log.disableLogging()
+ print log
View
@@ -29,6 +29,7 @@ class Config(object):
human_readable_sizes = False
extra_headers = SortedDict(ignore_case = True)
force = False
+ enable = None
get_continue = False
skip_existing = False
recursive = False
@@ -69,6 +70,7 @@ class Config(object):
debug_include = {}
encoding = "utf-8"
urlencoding_mode = "normal"
+ log_target_prefix = ""
## Creating a singleton
def __new__(self, configfile = None):
View
@@ -9,6 +9,7 @@
import httplib
import logging
import mimetypes
+import re
from logging import debug, info, warning, error
from stat import ST_SIZE
@@ -22,8 +23,11 @@
from BidirMap import BidirMap
from Config import Config
from Exceptions import *
-from ACL import ACL
+from ACL import ACL, GranteeLogDelivery
+from AccessLog import AccessLog
+from S3Uri import S3Uri
+__all__ = []
class S3Request(object):
def __init__(self, s3, method_string, resource, headers, params = {}):
self.s3 = s3
@@ -322,6 +326,41 @@ def set_acl(self, uri, acl):
response = self.send_request(request, body)
return response
+ def get_accesslog(self, uri):
+ request = self.create_request("BUCKET_LIST", bucket = uri.bucket(), extra = "?logging")
+ response = self.send_request(request)
+ accesslog = AccessLog(response['data'])
+ return accesslog
+
+ def set_accesslog_acl(self, uri):
+ acl = self.get_acl(uri)
+ debug("Current ACL(%s): %s" % (uri.uri(), str(acl)))
+ acl.appendGrantee(GranteeLogDelivery("READ_ACP"))
+ acl.appendGrantee(GranteeLogDelivery("WRITE"))
+ debug("Updated ACL(%s): %s" % (uri.uri(), str(acl)))
+ self.set_acl(uri, acl)
+
+ def set_accesslog(self, uri, enable, log_target_prefix_uri = None, acl_public = False):
+ request = self.create_request("BUCKET_CREATE", bucket = uri.bucket(), extra = "?logging")
+ accesslog = AccessLog()
+ if enable:
+ accesslog.enableLogging(log_target_prefix_uri)
+ accesslog.setAclPublic(acl_public)
+ else:
+ accesslog.disableLogging()
+ body = str(accesslog)
+ debug(u"set_accesslog(%s): accesslog-xml: %s" % (uri, body))
+ try:
+ response = self.send_request(request, body)
+ except S3Error, e:
+ if e.info['Code'] == "InvalidTargetBucketForLogging":
+ info("Setting up log-delivery ACL for target bucket.")
+ self.set_accesslog_acl(S3Uri("s3://%s" % log_target_prefix_uri.bucket()))
+ response = self.send_request(request, body)
+ else:
+ raise
+ return accesslog, response
+
## Low level methods
def urlencode_string(self, string, urlencoding_mode = None):
if type(string) == unicode:
@@ -720,3 +759,4 @@ def check_bucket_name_dns_conformity(bucket):
return S3.check_bucket_name(bucket, dns_strict = True)
except ParameterError:
return False
+__all__.append("S3")
View
@@ -8,7 +8,7 @@
import sys
from BidirMap import BidirMap
from logging import debug
-from S3 import S3
+import S3
from Utils import unicodise
class S3Uri(object):
@@ -73,7 +73,7 @@ def uri(self):
return "/".join(["s3:/", self._bucket, self._object])
def is_dns_compatible(self):
- return S3.check_bucket_name_dns_conformity(self._bucket)
+ return S3.S3.check_bucket_name_dns_conformity(self._bucket)
def public_url(self):
if self.is_dns_compatible():
Oops, something went wrong.

0 comments on commit cb0bbae

Please sign in to comment.