1# Copyright (c) 2015 Amazon.com, Inc. or its affiliates.  All Rights Reserved
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#
22
23import boto
24from boto.connection import AWSQueryConnection
25from boto.regioninfo import RegionInfo
26from boto.exception import JSONResponseError
27from boto.cloudtrail import exceptions
28from boto.compat import json
29
30
31class CloudTrailConnection(AWSQueryConnection):
32    """
33    AWS CloudTrail
34    This is the CloudTrail API Reference. It provides descriptions of
35    actions, data types, common parameters, and common errors for
36    CloudTrail.
37
38    CloudTrail is a web service that records AWS API calls for your
39    AWS account and delivers log files to an Amazon S3 bucket. The
40    recorded information includes the identity of the user, the start
41    time of the AWS API call, the source IP address, the request
42    parameters, and the response elements returned by the service.
43
44    As an alternative to using the API, you can use one of the AWS
45    SDKs, which consist of libraries and sample code for various
46    programming languages and platforms (Java, Ruby, .NET, iOS,
47    Android, etc.). The SDKs provide a convenient way to create
48    programmatic access to AWSCloudTrail. For example, the SDKs take
49    care of cryptographically signing requests, managing errors, and
50    retrying requests automatically. For information about the AWS
51    SDKs, including how to download and install them, see the `Tools
52    for Amazon Web Services page`_.
53
54    See the CloudTrail User Guide for information about the data that
55    is included with each AWS API call listed in the log files.
56    """
57    APIVersion = "2013-11-01"
58    DefaultRegionName = "us-east-1"
59    DefaultRegionEndpoint = "cloudtrail.us-east-1.amazonaws.com"
60    ServiceName = "CloudTrail"
61    TargetPrefix = "com.amazonaws.cloudtrail.v20131101.CloudTrail_20131101"
62    ResponseError = JSONResponseError
63
64    _faults = {
65        "InvalidMaxResultsException": exceptions.InvalidMaxResultsException,
66        "InvalidSnsTopicNameException": exceptions.InvalidSnsTopicNameException,
67        "InvalidS3BucketNameException": exceptions.InvalidS3BucketNameException,
68        "TrailAlreadyExistsException": exceptions.TrailAlreadyExistsException,
69        "InvalidTimeRangeException": exceptions.InvalidTimeRangeException,
70        "InvalidLookupAttributesException": exceptions.InvalidLookupAttributesException,
71        "InsufficientSnsTopicPolicyException": exceptions.InsufficientSnsTopicPolicyException,
72        "InvalidCloudWatchLogsLogGroupArnException": exceptions.InvalidCloudWatchLogsLogGroupArnException,
73        "InvalidCloudWatchLogsRoleArnException": exceptions.InvalidCloudWatchLogsRoleArnException,
74        "InvalidTrailNameException": exceptions.InvalidTrailNameException,
75        "CloudWatchLogsDeliveryUnavailableException": exceptions.CloudWatchLogsDeliveryUnavailableException,
76        "TrailNotFoundException": exceptions.TrailNotFoundException,
77        "S3BucketDoesNotExistException": exceptions.S3BucketDoesNotExistException,
78        "InvalidNextTokenException": exceptions.InvalidNextTokenException,
79        "InvalidS3PrefixException": exceptions.InvalidS3PrefixException,
80        "MaximumNumberOfTrailsExceededException": exceptions.MaximumNumberOfTrailsExceededException,
81        "InsufficientS3BucketPolicyException": exceptions.InsufficientS3BucketPolicyException,
82    }
83
84
85    def __init__(self, **kwargs):
86        region = kwargs.pop('region', None)
87        if not region:
88            region = RegionInfo(self, self.DefaultRegionName,
89                                self.DefaultRegionEndpoint)
90
91        if 'host' not in kwargs or kwargs['host'] is None:
92            kwargs['host'] = region.endpoint
93
94        super(CloudTrailConnection, self).__init__(**kwargs)
95        self.region = region
96
97    def _required_auth_capability(self):
98        return ['hmac-v4']
99
100    def create_trail(self, name, s3_bucket_name, s3_key_prefix=None,
101                     sns_topic_name=None, include_global_service_events=None,
102                     cloud_watch_logs_log_group_arn=None,
103                     cloud_watch_logs_role_arn=None):
104        """
105        From the command line, use `create-subscription`.
106
107        Creates a trail that specifies the settings for delivery of
108        log data to an Amazon S3 bucket.
109
110        :type name: string
111        :param name: Specifies the name of the trail.
112
113        :type s3_bucket_name: string
114        :param s3_bucket_name: Specifies the name of the Amazon S3 bucket
115            designated for publishing log files.
116
117        :type s3_key_prefix: string
118        :param s3_key_prefix: Specifies the Amazon S3 key prefix that precedes
119            the name of the bucket you have designated for log file delivery.
120
121        :type sns_topic_name: string
122        :param sns_topic_name: Specifies the name of the Amazon SNS topic
123            defined for notification of log file delivery.
124
125        :type include_global_service_events: boolean
126        :param include_global_service_events: Specifies whether the trail is
127            publishing events from global services such as IAM to the log
128            files.
129
130        :type cloud_watch_logs_log_group_arn: string
131        :param cloud_watch_logs_log_group_arn: Specifies a log group name using
132            an Amazon Resource Name (ARN), a unique identifier that represents
133            the log group to which CloudTrail logs will be delivered. Not
134            required unless you specify CloudWatchLogsRoleArn.
135
136        :type cloud_watch_logs_role_arn: string
137        :param cloud_watch_logs_role_arn: Specifies the role for the CloudWatch
138            Logs endpoint to assume to write to a users log group.
139
140        """
141        params = {'Name': name, 'S3BucketName': s3_bucket_name, }
142        if s3_key_prefix is not None:
143            params['S3KeyPrefix'] = s3_key_prefix
144        if sns_topic_name is not None:
145            params['SnsTopicName'] = sns_topic_name
146        if include_global_service_events is not None:
147            params['IncludeGlobalServiceEvents'] = include_global_service_events
148        if cloud_watch_logs_log_group_arn is not None:
149            params['CloudWatchLogsLogGroupArn'] = cloud_watch_logs_log_group_arn
150        if cloud_watch_logs_role_arn is not None:
151            params['CloudWatchLogsRoleArn'] = cloud_watch_logs_role_arn
152        return self.make_request(action='CreateTrail',
153                                 body=json.dumps(params))
154
155    def delete_trail(self, name):
156        """
157        Deletes a trail.
158
159        :type name: string
160        :param name: The name of a trail to be deleted.
161
162        """
163        params = {'Name': name, }
164        return self.make_request(action='DeleteTrail',
165                                 body=json.dumps(params))
166
167    def describe_trails(self, trail_name_list=None):
168        """
169        Retrieves settings for the trail associated with the current
170        region for your account.
171
172        :type trail_name_list: list
173        :param trail_name_list: The trail returned.
174
175        """
176        params = {}
177        if trail_name_list is not None:
178            params['trailNameList'] = trail_name_list
179        return self.make_request(action='DescribeTrails',
180                                 body=json.dumps(params))
181
182    def get_trail_status(self, name):
183        """
184        Returns a JSON-formatted list of information about the
185        specified trail. Fields include information on delivery
186        errors, Amazon SNS and Amazon S3 errors, and start and stop
187        logging times for each trail.
188
189        :type name: string
190        :param name: The name of the trail for which you are requesting the
191            current status.
192
193        """
194        params = {'Name': name, }
195        return self.make_request(action='GetTrailStatus',
196                                 body=json.dumps(params))
197
198    def lookup_events(self, lookup_attributes=None, start_time=None,
199                      end_time=None, max_results=None, next_token=None):
200        """
201        Looks up API activity events captured by CloudTrail that
202        create, update, or delete resources in your account. Events
203        for a region can be looked up for the times in which you had
204        CloudTrail turned on in that region during the last seven
205        days. Lookup supports five different attributes: time range
206        (defined by a start time and end time), user name, event name,
207        resource type, and resource name. All attributes are optional.
208        The maximum number of attributes that can be specified in any
209        one lookup request are time range and one other attribute. The
210        default number of results returned is 10, with a maximum of 50
211        possible. The response includes a token that you can use to
212        get the next page of results.
213        The rate of lookup requests is limited to one per second per
214        account. If this limit is exceeded, a throttling error occurs.
215        Events that occurred during the selected time range will not
216        be available for lookup if CloudTrail logging was not enabled
217        when the events occurred.
218
219        :type lookup_attributes: list
220        :param lookup_attributes: Contains a list of lookup attributes.
221            Currently the list can contain only one item.
222
223        :type start_time: timestamp
224        :param start_time: Specifies that only events that occur after or at
225            the specified time are returned. If the specified start time is
226            after the specified end time, an error is returned.
227
228        :type end_time: timestamp
229        :param end_time: Specifies that only events that occur before or at the
230            specified time are returned. If the specified end time is before
231            the specified start time, an error is returned.
232
233        :type max_results: integer
234        :param max_results: The number of events to return. Possible values are
235            1 through 50. The default is 10.
236
237        :type next_token: string
238        :param next_token: The token to use to get the next page of results
239            after a previous API call. This token must be passed in with the
240            same parameters that were specified in the the original call. For
241            example, if the original call specified an AttributeKey of
242            'Username' with a value of 'root', the call with NextToken should
243            include those same parameters.
244
245        """
246        params = {}
247        if lookup_attributes is not None:
248            params['LookupAttributes'] = lookup_attributes
249        if start_time is not None:
250            params['StartTime'] = start_time
251        if end_time is not None:
252            params['EndTime'] = end_time
253        if max_results is not None:
254            params['MaxResults'] = max_results
255        if next_token is not None:
256            params['NextToken'] = next_token
257        return self.make_request(action='LookupEvents',
258                                 body=json.dumps(params))
259
260    def start_logging(self, name):
261        """
262        Starts the recording of AWS API calls and log file delivery
263        for a trail.
264
265        :type name: string
266        :param name: The name of the trail for which CloudTrail logs AWS API
267            calls.
268
269        """
270        params = {'Name': name, }
271        return self.make_request(action='StartLogging',
272                                 body=json.dumps(params))
273
274    def stop_logging(self, name):
275        """
276        Suspends the recording of AWS API calls and log file delivery
277        for the specified trail. Under most circumstances, there is no
278        need to use this action. You can update a trail without
279        stopping it first. This action is the only way to stop
280        recording.
281
282        :type name: string
283        :param name: Communicates to CloudTrail the name of the trail for which
284            to stop logging AWS API calls.
285
286        """
287        params = {'Name': name, }
288        return self.make_request(action='StopLogging',
289                                 body=json.dumps(params))
290
291    def update_trail(self, name, s3_bucket_name=None, s3_key_prefix=None,
292                     sns_topic_name=None, include_global_service_events=None,
293                     cloud_watch_logs_log_group_arn=None,
294                     cloud_watch_logs_role_arn=None):
295        """
296        From the command line, use `update-subscription`.
297
298        Updates the settings that specify delivery of log files.
299        Changes to a trail do not require stopping the CloudTrail
300        service. Use this action to designate an existing bucket for
301        log delivery. If the existing bucket has previously been a
302        target for CloudTrail log files, an IAM policy exists for the
303        bucket.
304
305        :type name: string
306        :param name: Specifies the name of the trail.
307
308        :type s3_bucket_name: string
309        :param s3_bucket_name: Specifies the name of the Amazon S3 bucket
310            designated for publishing log files.
311
312        :type s3_key_prefix: string
313        :param s3_key_prefix: Specifies the Amazon S3 key prefix that precedes
314            the name of the bucket you have designated for log file delivery.
315
316        :type sns_topic_name: string
317        :param sns_topic_name: Specifies the name of the Amazon SNS topic
318            defined for notification of log file delivery.
319
320        :type include_global_service_events: boolean
321        :param include_global_service_events: Specifies whether the trail is
322            publishing events from global services such as IAM to the log
323            files.
324
325        :type cloud_watch_logs_log_group_arn: string
326        :param cloud_watch_logs_log_group_arn: Specifies a log group name using
327            an Amazon Resource Name (ARN), a unique identifier that represents
328            the log group to which CloudTrail logs will be delivered. Not
329            required unless you specify CloudWatchLogsRoleArn.
330
331        :type cloud_watch_logs_role_arn: string
332        :param cloud_watch_logs_role_arn: Specifies the role for the CloudWatch
333            Logs endpoint to assume to write to a users log group.
334
335        """
336        params = {'Name': name, }
337        if s3_bucket_name is not None:
338            params['S3BucketName'] = s3_bucket_name
339        if s3_key_prefix is not None:
340            params['S3KeyPrefix'] = s3_key_prefix
341        if sns_topic_name is not None:
342            params['SnsTopicName'] = sns_topic_name
343        if include_global_service_events is not None:
344            params['IncludeGlobalServiceEvents'] = include_global_service_events
345        if cloud_watch_logs_log_group_arn is not None:
346            params['CloudWatchLogsLogGroupArn'] = cloud_watch_logs_log_group_arn
347        if cloud_watch_logs_role_arn is not None:
348            params['CloudWatchLogsRoleArn'] = cloud_watch_logs_role_arn
349        return self.make_request(action='UpdateTrail',
350                                 body=json.dumps(params))
351
352    def make_request(self, action, body):
353        headers = {
354            'X-Amz-Target': '%s.%s' % (self.TargetPrefix, action),
355            'Host': self.region.endpoint,
356            'Content-Type': 'application/x-amz-json-1.1',
357            'Content-Length': str(len(body)),
358        }
359        http_request = self.build_base_http_request(
360            method='POST', path='/', auth_path='/', params={},
361            headers=headers, data=body)
362        response = self._mexe(http_request, sender=None,
363                              override_num_retries=10)
364        response_body = response.read().decode('utf-8')
365        boto.log.debug(response_body)
366        if response.status == 200:
367            if response_body:
368                return json.loads(response_body)
369        else:
370            json_body = json.loads(response_body)
371            fault_name = json_body.get('__type', None)
372            exception_class = self._faults.get(fault_name, self.ResponseError)
373            raise exception_class(response.status, response.reason,
374                                  body=json_body)
375