PageRenderTime 45ms CodeModel.GetById 18ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/boto-2.5.2/boto/sns/connection.py

#
Python | 441 lines | 375 code | 21 blank | 45 comment | 11 complexity | de4b285a100a64257223f62a52881a01 MD5 | raw file
  1# Copyright (c) 2010 Mitch Garnaat http://garnaat.org/
  2#
  3# Permission is hereby granted, free of charge, to any person obtaining a
  4# copy of this software and associated documentation files (the
  5# "Software"), to deal in the Software without restriction, including
  6# without limitation the rights to use, copy, modify, merge, publish, dis-
  7# tribute, sublicense, and/or sell copies of the Software, and to permit
  8# persons to whom the Software is furnished to do so, subject to the fol-
  9# lowing conditions:
 10#
 11# The above copyright notice and this permission notice shall be included
 12# in all copies or substantial portions of the Software.
 13#
 14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 15# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
 16# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
 17# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 18# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 20# IN THE SOFTWARE.
 21
 22from boto.connection import AWSQueryConnection
 23from boto.regioninfo import RegionInfo
 24import boto
 25import uuid
 26try:
 27    import simplejson as json
 28except ImportError:
 29    import json
 30
 31class SNSConnection(AWSQueryConnection):
 32
 33    DefaultRegionName = 'us-east-1'
 34    DefaultRegionEndpoint = 'sns.us-east-1.amazonaws.com'
 35    APIVersion = '2010-03-31'
 36
 37    def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
 38                 is_secure=True, port=None, proxy=None, proxy_port=None,
 39                 proxy_user=None, proxy_pass=None, debug=0,
 40                 https_connection_factory=None, region=None, path='/',
 41                 security_token=None):
 42        if not region:
 43            region = RegionInfo(self, self.DefaultRegionName,
 44                                self.DefaultRegionEndpoint,
 45                                connection_cls=SNSConnection)
 46        self.region = region
 47        AWSQueryConnection.__init__(self, aws_access_key_id,
 48                                    aws_secret_access_key,
 49                                    is_secure, port, proxy, proxy_port,
 50                                    proxy_user, proxy_pass,
 51                                    self.region.endpoint, debug,
 52                                    https_connection_factory, path,
 53                                    security_token=security_token)
 54
 55    def _required_auth_capability(self):
 56        return ['sns']
 57
 58    def _credentials_expired(self, response):
 59        if response.status != 403:
 60            return False
 61        try:
 62            parsed = json.loads(response.read())
 63            return parsed['Error']['Code'] == 'ExpiredToken'
 64        except Exception:
 65            return False
 66        return False
 67
 68    def get_all_topics(self, next_token=None):
 69        """
 70        :type next_token: string
 71        :param next_token: Token returned by the previous call to
 72                           this method.
 73
 74        """
 75        params = {'ContentType' : 'JSON'}
 76        if next_token:
 77            params['NextToken'] = next_token
 78        response = self.make_request('ListTopics', params, '/', 'GET')
 79        body = response.read()
 80        if response.status == 200:
 81            return json.loads(body)
 82        else:
 83            boto.log.error('%s %s' % (response.status, response.reason))
 84            boto.log.error('%s' % body)
 85            raise self.ResponseError(response.status, response.reason, body)
 86        
 87    def get_topic_attributes(self, topic):
 88        """
 89        Get attributes of a Topic
 90
 91        :type topic: string
 92        :param topic: The ARN of the topic.
 93
 94        """
 95        params = {'ContentType' : 'JSON',
 96                  'TopicArn' : topic}
 97        response = self.make_request('GetTopicAttributes', params, '/', 'GET')
 98        body = response.read()
 99        if response.status == 200:
100            return json.loads(body)
101        else:
102            boto.log.error('%s %s' % (response.status, response.reason))
103            boto.log.error('%s' % body)
104            raise self.ResponseError(response.status, response.reason, body)
105        
106    def set_topic_attributes(self, topic, attr_name, attr_value):
107        """
108        Get attributes of a Topic
109
110        :type topic: string
111        :param topic: The ARN of the topic.
112
113        :type attr_name: string
114        :param attr_name: The name of the attribute you want to set.
115                          Only a subset of the topic's attributes are mutable.
116                          Valid values: Policy | DisplayName
117
118        :type attr_value: string
119        :param attr_value: The new value for the attribute.
120
121        """
122        params = {'ContentType' : 'JSON',
123                  'TopicArn' : topic,
124                  'AttributeName' : attr_name,
125                  'AttributeValue' : attr_value}
126        response = self.make_request('SetTopicAttributes', params, '/', 'GET')
127        body = response.read()
128        if response.status == 200:
129            return json.loads(body)
130        else:
131            boto.log.error('%s %s' % (response.status, response.reason))
132            boto.log.error('%s' % body)
133            raise self.ResponseError(response.status, response.reason, body)
134        
135    def add_permission(self, topic, label, account_ids, actions):
136        """
137        Adds a statement to a topic's access control policy, granting
138        access for the specified AWS accounts to the specified actions.
139
140        :type topic: string
141        :param topic: The ARN of the topic.
142
143        :type label: string
144        :param label: A unique identifier for the new policy statement.
145
146        :type account_ids: list of strings
147        :param account_ids: The AWS account ids of the users who will be
148                            give access to the specified actions.
149
150        :type actions: list of strings
151        :param actions: The actions you want to allow for each of the
152                        specified principal(s).
153
154        """
155        params = {'ContentType' : 'JSON',
156                  'TopicArn' : topic,
157                  'Label' : label}
158        self.build_list_params(params, account_ids, 'AWSAccountId')
159        self.build_list_params(params, actions, 'ActionName')
160        response = self.make_request('AddPermission', params, '/', 'GET')
161        body = response.read()
162        if response.status == 200:
163            return json.loads(body)
164        else:
165            boto.log.error('%s %s' % (response.status, response.reason))
166            boto.log.error('%s' % body)
167            raise self.ResponseError(response.status, response.reason, body)
168        
169    def remove_permission(self, topic, label):
170        """
171        Removes a statement from a topic's access control policy.
172
173        :type topic: string
174        :param topic: The ARN of the topic.
175
176        :type label: string
177        :param label: A unique identifier for the policy statement
178                      to be removed.
179
180        """
181        params = {'ContentType' : 'JSON',
182                  'TopicArn' : topic,
183                  'Label' : label}
184        response = self.make_request('RemovePermission', params, '/', 'GET')
185        body = response.read()
186        if response.status == 200:
187            return json.loads(body)
188        else:
189            boto.log.error('%s %s' % (response.status, response.reason))
190            boto.log.error('%s' % body)
191            raise self.ResponseError(response.status, response.reason, body)
192        
193    def create_topic(self, topic):
194        """
195        Create a new Topic.
196
197        :type topic: string
198        :param topic: The name of the new topic.
199
200        """
201        params = {'ContentType' : 'JSON',
202                  'Name' : topic}
203        response = self.make_request('CreateTopic', params, '/', 'GET')
204        body = response.read()
205        if response.status == 200:
206            return json.loads(body)
207        else:
208            boto.log.error('%s %s' % (response.status, response.reason))
209            boto.log.error('%s' % body)
210            raise self.ResponseError(response.status, response.reason, body)
211
212    def delete_topic(self, topic):
213        """
214        Delete an existing topic
215
216        :type topic: string
217        :param topic: The ARN of the topic
218
219        """
220        params = {'ContentType' : 'JSON',
221                  'TopicArn' : topic}
222        response = self.make_request('DeleteTopic', params, '/', 'GET')
223        body = response.read()
224        if response.status == 200:
225            return json.loads(body)
226        else:
227            boto.log.error('%s %s' % (response.status, response.reason))
228            boto.log.error('%s' % body)
229            raise self.ResponseError(response.status, response.reason, body)
230
231
232
233    def publish(self, topic, message, subject=None):
234        """
235        Get properties of a Topic
236
237        :type topic: string
238        :param topic: The ARN of the new topic.
239
240        :type message: string
241        :param message: The message you want to send to the topic.
242                        Messages must be UTF-8 encoded strings and
243                        be at most 4KB in size.
244
245        :type subject: string
246        :param subject: Optional parameter to be used as the "Subject"
247                        line of the email notifications.
248
249        """
250        params = {'ContentType' : 'JSON',
251                  'TopicArn' : topic,
252                  'Message' : message}
253        if subject:
254            params['Subject'] = subject
255        response = self.make_request('Publish', params, '/', 'GET')
256        body = response.read()
257        if response.status == 200:
258            return json.loads(body)
259        else:
260            boto.log.error('%s %s' % (response.status, response.reason))
261            boto.log.error('%s' % body)
262            raise self.ResponseError(response.status, response.reason, body)
263        
264    def subscribe(self, topic, protocol, endpoint):
265        """
266        Subscribe to a Topic.
267
268        :type topic: string
269        :param topic: The name of the new topic.
270
271        :type protocol: string
272        :param protocol: The protocol used to communicate with
273                         the subscriber.  Current choices are:
274                         email|email-json|http|https|sqs
275
276        :type endpoint: string
277        :param endpoint: The location of the endpoint for
278                         the subscriber.
279                         * For email, this would be a valid email address
280                         * For email-json, this would be a valid email address
281                         * For http, this would be a URL beginning with http
282                         * For https, this would be a URL beginning with https
283                         * For sqs, this would be the ARN of an SQS Queue
284
285        """
286        params = {'ContentType' : 'JSON',
287                  'TopicArn' : topic,
288                  'Protocol' : protocol,
289                  'Endpoint' : endpoint}
290        response = self.make_request('Subscribe', params, '/', 'GET')
291        body = response.read()
292        if response.status == 200:
293            return json.loads(body)
294        else:
295            boto.log.error('%s %s' % (response.status, response.reason))
296            boto.log.error('%s' % body)
297            raise self.ResponseError(response.status, response.reason, body)
298
299    def subscribe_sqs_queue(self, topic, queue):
300        """
301        Subscribe an SQS queue to a topic.
302
303        This is convenience method that handles most of the complexity involved
304        in using an SQS queue as an endpoint for an SNS topic.  To achieve this
305        the following operations are performed:
306        
307        * The correct ARN is constructed for the SQS queue and that ARN is
308          then subscribed to the topic.
309        * A JSON policy document is contructed that grants permission to
310          the SNS topic to send messages to the SQS queue.
311        * This JSON policy is then associated with the SQS queue using
312          the queue's set_attribute method.  If the queue already has
313          a policy associated with it, this process will add a Statement to
314          that policy.  If no policy exists, a new policy will be created.
315          
316        :type topic: string
317        :param topic: The name of the new topic.
318
319        :type queue: A boto Queue object
320        :param queue: The queue you wish to subscribe to the SNS Topic.
321        """
322        t = queue.id.split('/')
323        q_arn = 'arn:aws:sqs:%s:%s:%s' % (queue.connection.region.name,
324                                          t[1], t[2])
325        resp = self.subscribe(topic, 'sqs', q_arn)
326        policy = queue.get_attributes('Policy')
327        if 'Version' not in policy:
328            policy['Version'] = '2008-10-17'
329        if 'Statement' not in policy:
330            policy['Statement'] = []
331        statement = {'Action' : 'SQS:SendMessage',
332                     'Effect' : 'Allow',
333                     'Principal' : {'AWS' : '*'},
334                     'Resource' : q_arn,
335                     'Sid' : str(uuid.uuid4()),
336                     'Condition' : {'StringLike' : {'aws:SourceArn' : topic}}}
337        policy['Statement'].append(statement)
338        queue.set_attribute('Policy', json.dumps(policy))
339        return resp
340
341    def confirm_subscription(self, topic, token,
342                             authenticate_on_unsubscribe=False):
343        """
344        Get properties of a Topic
345
346        :type topic: string
347        :param topic: The ARN of the new topic.
348
349        :type token: string
350        :param token: Short-lived token sent to and endpoint during
351                      the Subscribe operation.
352
353        :type authenticate_on_unsubscribe: bool
354        :param authenticate_on_unsubscribe: Optional parameter indicating
355                                            that you wish to disable
356                                            unauthenticated unsubscription
357                                            of the subscription.
358
359        """
360        params = {'ContentType' : 'JSON',
361                  'TopicArn' : topic,
362                  'Token' : token}
363        if authenticate_on_unsubscribe:
364            params['AuthenticateOnUnsubscribe'] = 'true'
365        response = self.make_request('ConfirmSubscription', params, '/', 'GET')
366        body = response.read()
367        if response.status == 200:
368            return json.loads(body)
369        else:
370            boto.log.error('%s %s' % (response.status, response.reason))
371            boto.log.error('%s' % body)
372            raise self.ResponseError(response.status, response.reason, body)
373        
374    def unsubscribe(self, subscription):
375        """
376        Allows endpoint owner to delete subscription.
377        Confirmation message will be delivered.
378
379        :type subscription: string
380        :param subscription: The ARN of the subscription to be deleted.
381
382        """
383        params = {'ContentType' : 'JSON',
384                  'SubscriptionArn' : subscription}
385        response = self.make_request('Unsubscribe', params, '/', 'GET')
386        body = response.read()
387        if response.status == 200:
388            return json.loads(body)
389        else:
390            boto.log.error('%s %s' % (response.status, response.reason))
391            boto.log.error('%s' % body)
392            raise self.ResponseError(response.status, response.reason, body)
393        
394    def get_all_subscriptions(self, next_token=None):
395        """
396        Get list of all subscriptions.
397
398        :type next_token: string
399        :param next_token: Token returned by the previous call to
400                           this method.
401
402        """
403        params = {'ContentType' : 'JSON'}
404        if next_token:
405            params['NextToken'] = next_token
406        response = self.make_request('ListSubscriptions', params, '/', 'GET')
407        body = response.read()
408        if response.status == 200:
409            return json.loads(body)
410        else:
411            boto.log.error('%s %s' % (response.status, response.reason))
412            boto.log.error('%s' % body)
413            raise self.ResponseError(response.status, response.reason, body)
414        
415    def get_all_subscriptions_by_topic(self, topic, next_token=None):
416        """
417        Get list of all subscriptions to a specific topic.
418
419        :type topic: string
420        :param topic: The ARN of the topic for which you wish to
421                      find subscriptions.
422
423        :type next_token: string
424        :param next_token: Token returned by the previous call to
425                           this method.
426
427        """
428        params = {'ContentType' : 'JSON',
429                  'TopicArn' : topic}
430        if next_token:
431            params['NextToken'] = next_token
432        response = self.make_request('ListSubscriptionsByTopic', params,
433                                     '/', 'GET')
434        body = response.read()
435        if response.status == 200:
436            return json.loads(body)
437        else:
438            boto.log.error('%s %s' % (response.status, response.reason))
439            boto.log.error('%s' % body)
440            raise self.ResponseError(response.status, response.reason, body)
441