/boto-2.5.2/boto/sns/connection.py
Python | 441 lines | 375 code | 21 blank | 45 comment | 17 complexity | de4b285a100a64257223f62a52881a01 MD5 | raw file
- # Copyright (c) 2010 Mitch Garnaat http://garnaat.org/
- #
- # Permission is hereby granted, free of charge, to any person obtaining a
- # copy of this software and associated documentation files (the
- # "Software"), to deal in the Software without restriction, including
- # without limitation the rights to use, copy, modify, merge, publish, dis-
- # tribute, sublicense, and/or sell copies of the Software, and to permit
- # persons to whom the Software is furnished to do so, subject to the fol-
- # lowing conditions:
- #
- # The above copyright notice and this permission notice shall be included
- # in all copies or substantial portions of the Software.
- #
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
- # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
- # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- # IN THE SOFTWARE.
- from boto.connection import AWSQueryConnection
- from boto.regioninfo import RegionInfo
- import boto
- import uuid
- try:
- import simplejson as json
- except ImportError:
- import json
- class SNSConnection(AWSQueryConnection):
- DefaultRegionName = 'us-east-1'
- DefaultRegionEndpoint = 'sns.us-east-1.amazonaws.com'
- APIVersion = '2010-03-31'
- def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
- is_secure=True, port=None, proxy=None, proxy_port=None,
- proxy_user=None, proxy_pass=None, debug=0,
- https_connection_factory=None, region=None, path='/',
- security_token=None):
- if not region:
- region = RegionInfo(self, self.DefaultRegionName,
- self.DefaultRegionEndpoint,
- connection_cls=SNSConnection)
- self.region = region
- AWSQueryConnection.__init__(self, aws_access_key_id,
- aws_secret_access_key,
- is_secure, port, proxy, proxy_port,
- proxy_user, proxy_pass,
- self.region.endpoint, debug,
- https_connection_factory, path,
- security_token=security_token)
- def _required_auth_capability(self):
- return ['sns']
- def _credentials_expired(self, response):
- if response.status != 403:
- return False
- try:
- parsed = json.loads(response.read())
- return parsed['Error']['Code'] == 'ExpiredToken'
- except Exception:
- return False
- return False
- def get_all_topics(self, next_token=None):
- """
- :type next_token: string
- :param next_token: Token returned by the previous call to
- this method.
- """
- params = {'ContentType' : 'JSON'}
- if next_token:
- params['NextToken'] = next_token
- response = self.make_request('ListTopics', params, '/', 'GET')
- body = response.read()
- if response.status == 200:
- return json.loads(body)
- else:
- boto.log.error('%s %s' % (response.status, response.reason))
- boto.log.error('%s' % body)
- raise self.ResponseError(response.status, response.reason, body)
-
- def get_topic_attributes(self, topic):
- """
- Get attributes of a Topic
- :type topic: string
- :param topic: The ARN of the topic.
- """
- params = {'ContentType' : 'JSON',
- 'TopicArn' : topic}
- response = self.make_request('GetTopicAttributes', params, '/', 'GET')
- body = response.read()
- if response.status == 200:
- return json.loads(body)
- else:
- boto.log.error('%s %s' % (response.status, response.reason))
- boto.log.error('%s' % body)
- raise self.ResponseError(response.status, response.reason, body)
-
- def set_topic_attributes(self, topic, attr_name, attr_value):
- """
- Get attributes of a Topic
- :type topic: string
- :param topic: The ARN of the topic.
- :type attr_name: string
- :param attr_name: The name of the attribute you want to set.
- Only a subset of the topic's attributes are mutable.
- Valid values: Policy | DisplayName
- :type attr_value: string
- :param attr_value: The new value for the attribute.
- """
- params = {'ContentType' : 'JSON',
- 'TopicArn' : topic,
- 'AttributeName' : attr_name,
- 'AttributeValue' : attr_value}
- response = self.make_request('SetTopicAttributes', params, '/', 'GET')
- body = response.read()
- if response.status == 200:
- return json.loads(body)
- else:
- boto.log.error('%s %s' % (response.status, response.reason))
- boto.log.error('%s' % body)
- raise self.ResponseError(response.status, response.reason, body)
-
- def add_permission(self, topic, label, account_ids, actions):
- """
- Adds a statement to a topic's access control policy, granting
- access for the specified AWS accounts to the specified actions.
- :type topic: string
- :param topic: The ARN of the topic.
- :type label: string
- :param label: A unique identifier for the new policy statement.
- :type account_ids: list of strings
- :param account_ids: The AWS account ids of the users who will be
- give access to the specified actions.
- :type actions: list of strings
- :param actions: The actions you want to allow for each of the
- specified principal(s).
- """
- params = {'ContentType' : 'JSON',
- 'TopicArn' : topic,
- 'Label' : label}
- self.build_list_params(params, account_ids, 'AWSAccountId')
- self.build_list_params(params, actions, 'ActionName')
- response = self.make_request('AddPermission', params, '/', 'GET')
- body = response.read()
- if response.status == 200:
- return json.loads(body)
- else:
- boto.log.error('%s %s' % (response.status, response.reason))
- boto.log.error('%s' % body)
- raise self.ResponseError(response.status, response.reason, body)
-
- def remove_permission(self, topic, label):
- """
- Removes a statement from a topic's access control policy.
- :type topic: string
- :param topic: The ARN of the topic.
- :type label: string
- :param label: A unique identifier for the policy statement
- to be removed.
- """
- params = {'ContentType' : 'JSON',
- 'TopicArn' : topic,
- 'Label' : label}
- response = self.make_request('RemovePermission', params, '/', 'GET')
- body = response.read()
- if response.status == 200:
- return json.loads(body)
- else:
- boto.log.error('%s %s' % (response.status, response.reason))
- boto.log.error('%s' % body)
- raise self.ResponseError(response.status, response.reason, body)
-
- def create_topic(self, topic):
- """
- Create a new Topic.
- :type topic: string
- :param topic: The name of the new topic.
- """
- params = {'ContentType' : 'JSON',
- 'Name' : topic}
- response = self.make_request('CreateTopic', params, '/', 'GET')
- body = response.read()
- if response.status == 200:
- return json.loads(body)
- else:
- boto.log.error('%s %s' % (response.status, response.reason))
- boto.log.error('%s' % body)
- raise self.ResponseError(response.status, response.reason, body)
- def delete_topic(self, topic):
- """
- Delete an existing topic
- :type topic: string
- :param topic: The ARN of the topic
- """
- params = {'ContentType' : 'JSON',
- 'TopicArn' : topic}
- response = self.make_request('DeleteTopic', params, '/', 'GET')
- body = response.read()
- if response.status == 200:
- return json.loads(body)
- else:
- boto.log.error('%s %s' % (response.status, response.reason))
- boto.log.error('%s' % body)
- raise self.ResponseError(response.status, response.reason, body)
- def publish(self, topic, message, subject=None):
- """
- Get properties of a Topic
- :type topic: string
- :param topic: The ARN of the new topic.
- :type message: string
- :param message: The message you want to send to the topic.
- Messages must be UTF-8 encoded strings and
- be at most 4KB in size.
- :type subject: string
- :param subject: Optional parameter to be used as the "Subject"
- line of the email notifications.
- """
- params = {'ContentType' : 'JSON',
- 'TopicArn' : topic,
- 'Message' : message}
- if subject:
- params['Subject'] = subject
- response = self.make_request('Publish', params, '/', 'GET')
- body = response.read()
- if response.status == 200:
- return json.loads(body)
- else:
- boto.log.error('%s %s' % (response.status, response.reason))
- boto.log.error('%s' % body)
- raise self.ResponseError(response.status, response.reason, body)
-
- def subscribe(self, topic, protocol, endpoint):
- """
- Subscribe to a Topic.
- :type topic: string
- :param topic: The name of the new topic.
- :type protocol: string
- :param protocol: The protocol used to communicate with
- the subscriber. Current choices are:
- email|email-json|http|https|sqs
- :type endpoint: string
- :param endpoint: The location of the endpoint for
- the subscriber.
- * For email, this would be a valid email address
- * For email-json, this would be a valid email address
- * For http, this would be a URL beginning with http
- * For https, this would be a URL beginning with https
- * For sqs, this would be the ARN of an SQS Queue
- """
- params = {'ContentType' : 'JSON',
- 'TopicArn' : topic,
- 'Protocol' : protocol,
- 'Endpoint' : endpoint}
- response = self.make_request('Subscribe', params, '/', 'GET')
- body = response.read()
- if response.status == 200:
- return json.loads(body)
- else:
- boto.log.error('%s %s' % (response.status, response.reason))
- boto.log.error('%s' % body)
- raise self.ResponseError(response.status, response.reason, body)
- def subscribe_sqs_queue(self, topic, queue):
- """
- Subscribe an SQS queue to a topic.
- This is convenience method that handles most of the complexity involved
- in using an SQS queue as an endpoint for an SNS topic. To achieve this
- the following operations are performed:
-
- * The correct ARN is constructed for the SQS queue and that ARN is
- then subscribed to the topic.
- * A JSON policy document is contructed that grants permission to
- the SNS topic to send messages to the SQS queue.
- * This JSON policy is then associated with the SQS queue using
- the queue's set_attribute method. If the queue already has
- a policy associated with it, this process will add a Statement to
- that policy. If no policy exists, a new policy will be created.
-
- :type topic: string
- :param topic: The name of the new topic.
- :type queue: A boto Queue object
- :param queue: The queue you wish to subscribe to the SNS Topic.
- """
- t = queue.id.split('/')
- q_arn = 'arn:aws:sqs:%s:%s:%s' % (queue.connection.region.name,
- t[1], t[2])
- resp = self.subscribe(topic, 'sqs', q_arn)
- policy = queue.get_attributes('Policy')
- if 'Version' not in policy:
- policy['Version'] = '2008-10-17'
- if 'Statement' not in policy:
- policy['Statement'] = []
- statement = {'Action' : 'SQS:SendMessage',
- 'Effect' : 'Allow',
- 'Principal' : {'AWS' : '*'},
- 'Resource' : q_arn,
- 'Sid' : str(uuid.uuid4()),
- 'Condition' : {'StringLike' : {'aws:SourceArn' : topic}}}
- policy['Statement'].append(statement)
- queue.set_attribute('Policy', json.dumps(policy))
- return resp
- def confirm_subscription(self, topic, token,
- authenticate_on_unsubscribe=False):
- """
- Get properties of a Topic
- :type topic: string
- :param topic: The ARN of the new topic.
- :type token: string
- :param token: Short-lived token sent to and endpoint during
- the Subscribe operation.
- :type authenticate_on_unsubscribe: bool
- :param authenticate_on_unsubscribe: Optional parameter indicating
- that you wish to disable
- unauthenticated unsubscription
- of the subscription.
- """
- params = {'ContentType' : 'JSON',
- 'TopicArn' : topic,
- 'Token' : token}
- if authenticate_on_unsubscribe:
- params['AuthenticateOnUnsubscribe'] = 'true'
- response = self.make_request('ConfirmSubscription', params, '/', 'GET')
- body = response.read()
- if response.status == 200:
- return json.loads(body)
- else:
- boto.log.error('%s %s' % (response.status, response.reason))
- boto.log.error('%s' % body)
- raise self.ResponseError(response.status, response.reason, body)
-
- def unsubscribe(self, subscription):
- """
- Allows endpoint owner to delete subscription.
- Confirmation message will be delivered.
- :type subscription: string
- :param subscription: The ARN of the subscription to be deleted.
- """
- params = {'ContentType' : 'JSON',
- 'SubscriptionArn' : subscription}
- response = self.make_request('Unsubscribe', params, '/', 'GET')
- body = response.read()
- if response.status == 200:
- return json.loads(body)
- else:
- boto.log.error('%s %s' % (response.status, response.reason))
- boto.log.error('%s' % body)
- raise self.ResponseError(response.status, response.reason, body)
-
- def get_all_subscriptions(self, next_token=None):
- """
- Get list of all subscriptions.
- :type next_token: string
- :param next_token: Token returned by the previous call to
- this method.
- """
- params = {'ContentType' : 'JSON'}
- if next_token:
- params['NextToken'] = next_token
- response = self.make_request('ListSubscriptions', params, '/', 'GET')
- body = response.read()
- if response.status == 200:
- return json.loads(body)
- else:
- boto.log.error('%s %s' % (response.status, response.reason))
- boto.log.error('%s' % body)
- raise self.ResponseError(response.status, response.reason, body)
-
- def get_all_subscriptions_by_topic(self, topic, next_token=None):
- """
- Get list of all subscriptions to a specific topic.
- :type topic: string
- :param topic: The ARN of the topic for which you wish to
- find subscriptions.
- :type next_token: string
- :param next_token: Token returned by the previous call to
- this method.
- """
- params = {'ContentType' : 'JSON',
- 'TopicArn' : topic}
- if next_token:
- params['NextToken'] = next_token
- response = self.make_request('ListSubscriptionsByTopic', params,
- '/', 'GET')
- body = response.read()
- if response.status == 200:
- return json.loads(body)
- else:
- boto.log.error('%s %s' % (response.status, response.reason))
- boto.log.error('%s' % body)
- raise self.ResponseError(response.status, response.reason, body)
-