1 import s3
2 import re
3 import sha
4 import hmac
5 import time
6 import base64
7 import urllib
8
9 PORTS_BY_SECURITY = { True: 443, False: 80 }
10
11 DEFAULT_EXPIRES_IN = 60
12
14 """
15 Generator class
16
17 Objects of this class are used for generating authenticated URLs for accessing
18 Amazon's S3 service.
19 """
20
22 self._pub_key = pub_key
23 self._priv_key = priv_key
24 self._host = host
25 if not port:
26 self._port = PORTS_BY_SECURITY[secure]
27 else:
28 self._port = port
29 if (secure):
30 self.protocol = 'https'
31 else:
32 self.protocol = 'http'
33 self._secure = secure
34 self.server_name = "%s:%d" % (self._host, self._port)
35 self._expires_in = DEFAULT_EXPIRES_IN
36 self._expires = None
37
39 """
40 Set relative expiration time from the url creation moment.
41
42 @param expires_in: Relative expiration time
43 @type expires_in: int
44 """
45 self._expires_in = expires_in
46 self._expires = None
47
49 """
50 Set absolute expiration time.
51
52 @param expires: Absolute expiration time
53 @type expires: time.time()
54 """
55 self._expires = expires
56 self._expires_in = None
57
58
60 """
61 Make an unauthorised URL.
62 """
63 return self.protocol + '://' + self.server_name + '/' + bucket + '/' + key
64
65
67 xamzs = [k for k in headers.keys() if k.startswith("x-amz-")]
68 xamzs.sort()
69 auth_parts = [method,
70 headers.get("Content-MD5", ""),
71 headers.get("Content-Type", ""),
72 headers.get("Date", ""),]
73 auth_parts.extend([k + ":" + headers[k].strip() for k in xamzs])
74
75
76 stripped_path = path.split('?')[0]
77 if re.search("[&?]acl($|=|&)", path):
78 stripped_path += "?acl"
79 elif re.search("[&?]torrent($|=|&)", path):
80 stripped_path += "?torrent"
81
82 auth_parts.append(stripped_path)
83 auth_str = "\n".join(auth_parts)
84 auth_str = base64.encodestring(
85 hmac.new(self._priv_key, auth_str, sha).digest()).strip()
86 return urllib.quote_plus(auth_str)
87
88
90 if not headers:
91 headers = {}
92 headers["Date"] = str(expires)
93 if length is not None:
94 headers["Content-Length"] = length
95 return headers
96
97
98 - def _params(self, params, acl=False):
99 p = ''
100 if params:
101 if acl:
102 arg_div = '&'
103 else:
104 arg_div = '?'
105 p = arg_div + urllib.urlencode(params)
106 return p
107
108
109 - def _path(self, bucket=None, key=None, acl=False):
110 if bucket is None:
111 path = "/"
112 else:
113 path = "/" + bucket
114 if key is not None:
115 path += "/" + urllib.quote(key)
116 if acl:
117 path += '?acl'
118 return path
119
120
122 if hasattr(io, "len"):
123 return io.len
124 o_pos = io.tell()
125 io.seek(0, 2)
126 length = io.tell() - o_pos
127 io.seek(o_pos, 0)
128 return length
129
130
131 - def _generate(self, method, bucket=None, key=None,
132 send_io=None, params=None, headers=None, acl=False):
133 expires = 0
134 if self._expires_in != None:
135 expires = int(time.time() + self._expires_in)
136 elif self._expires != None:
137 expires = int(self._expires)
138
139 path = self._path(bucket, key, acl)
140 length = None
141 if isinstance(headers, dict) and headers.has_key("Content-Length"):
142 length = headers["Content-Length"]
143 elif send_io is not None:
144 length = self._io_len(send_io)
145 headers = self._headers(headers=headers, length=length, expires=expires)
146 signature = self._auth_header_value(method, path, headers)
147 path += self._params(params, acl)
148 if '?' in path:
149 arg_div = '&'
150 else:
151 arg_div = '?'
152 query_part = "Signature=%s&Expires=%d&AWSAccessKeyId=%s" % (signature, expires, self._pub_key)
153
154 return self.protocol + '://' + self.server_name + path + arg_div + query_part
155
156
158 """
159 Create a bucket.
160
161 @param name: Name for the new bucket
162 @type name: string
163 @param headers: Additional headers
164 @type headers: dict
165 @return: Authenticated URL for creating a bucket
166 @rtype: string
167 """
168 return self._generate('PUT', bucket=name, headers=headers)
169
171 """
172 List a bucket's content.
173
174 @param name: Bucket's name
175 @type name: string
176 @param params: Additional parameters
177 @type params: dict
178 @param headers: Additional headers
179 @type headers: dict
180 @return: Authenticated URL for listing bucket's content
181 @rtype: string
182 """
183 return self._generate('GET', bucket=name, params=params, headers=headers)
184
186 """
187 Delete a bucket.
188
189 @param name: Name of the bucket that should be deleted
190 @type name: string
191 @param headers: Additional headers
192 @type headers: dict
193 @return: Authenticated URL for delete a bucket
194 @rtype: string
195 """
196 return self._generate('DELETE', bucket=name, headers=headers)
197
199 """
200 List all buckets
201
202 @param headers: Additional headers
203 @type headers: dict
204 @return: Authenticated URL for listing all buckets
205 @rtype: string
206 """
207 return self._generate('GET', headers=headers)
208
209
210 - def put(self, bucket, key, obj, headers={}):
213
214 - def get(self, bucket, key, headers={}):
215 return self._generate('GET', bucket=bucket, key=key, headers=headers)
216
217 - def delete(self, bucket, key, headers={}):
218 return self._generate('DELETE', bucket=bucket, key=key, headers=headers)
219
221 """
222 Get acl information for a bucket.
223
224 @param name: Bucket name
225 @type name: string
226 @param headers: Additional headers
227 @type headers: dict
228 @return: Authenticated URL for getting acl information for a bucket
229 @rtype: string
230 """
231 return self.get_acl(name, None, headers)
232
233 - def get_acl(self, bucket, key=None, headers={}):
234 """
235 Get acl information for an object.
236
237 @param bucket: Bucket's name
238 @type bucket: string
239 @param key: Object's name
240 @type key: string
241 @param headers: Additional headers
242 @type headers: dict
243 @return: Authenticated URL for getting acl information for an object
244 """
245 return self._generate('GET', acl=True, bucket=bucket, key=key, headers=headers)
246
248 return self.put_acl(name, '', acl_xml_document, headers)
249
250
251 - def put_acl(self, bucket, key, acl_xml_document, headers={}):
252 return self._generate('PUT', acl=True, bucket=bucket, key=key, headers=headers)
253