Seditio Source
Root |
./othercms/dle15_0/upload/engine/classes/composer/vendor/aws/aws-sdk-php/src/CloudTrail/LogFileIterator.php
<?php
namespace Aws\CloudTrail;

use
Aws\S3\S3Client;
use
Aws\CloudTrail\Exception\CloudTrailException;

/**
 * The `Aws\CloudTrail\LogFileIterator` provides an easy way to iterate over
 * log file generated by AWS CloudTrail.
 *
 * CloudTrail log files contain data about your AWS API calls and are stored in
 * Amazon S3 at a predictable path based on a bucket name, a key prefix, an
 * account ID, a region, and date information. This class allows you to specify
 * options, including a date range, and emits each log file that match the
 * provided options.
 *
 * Yields an array containing the Amazon S3 bucket and key of the log file.
 */
class LogFileIterator extends \IteratorIterator
{
   
// For internal use
   
const DEFAULT_TRAIL_NAME = 'Default';
    const
PREFIX_TEMPLATE = 'prefix/AWSLogs/account/CloudTrail/region/date/';
    const
PREFIX_WILDCARD = '*';

   
// Option names used internally or externally
   
const TRAIL_NAME = 'trail_name';
    const
KEY_PREFIX = 'key_prefix';
    const
START_DATE = 'start_date';
    const
END_DATE = 'end_date';
    const
ACCOUNT_ID = 'account_id';
    const
LOG_REGION = 'log_region';

   
/** @var S3Client S3 client used to perform ListObjects operations */
   
private $s3Client;

   
/** @var string S3 bucket that contains the log files */
   
private $s3BucketName;

   
/**
     * Constructs a LogRecordIterator. This factory method is used if the name
     * of the S3 bucket containing your logs is not known. This factory method
     * uses a CloudTrail client and the trail name (or "Default") to find the
     * information about the trail necessary for constructing the
     * LogRecordIterator.
     *
     * @param S3Client         $s3Client
     * @param CloudTrailClient $cloudTrailClient
     * @param array            $options
     *
     * @return LogRecordIterator
     * @throws \InvalidArgumentException
     * @see LogRecordIterator::__contruct
     */
   
public static function forTrail(
       
S3Client $s3Client,
       
CloudTrailClient $cloudTrailClient,
        array
$options = []
    ) {
       
$trailName = isset($options[self::TRAIL_NAME])
            ?
$options[self::TRAIL_NAME]
            :
self::DEFAULT_TRAIL_NAME;

       
$s3BucketName = null;

       
// Use the CloudTrail client to get information about the trail,
        // including the bucket name.
       
try {
           
$result = $cloudTrailClient->describeTrails([
               
'trailNameList' => [$trailName]
            ]);
           
$s3BucketName = $result->search('trailList[0].S3BucketName');
           
$options[self::KEY_PREFIX] = $result->search(
               
'trailList[0].S3KeyPrefix'
           
);
        } catch (
CloudTrailException $e) {
           
// There was an error describing the trail
       
}

       
// If the bucket name is still unknown, then throw an exception
       
if (!$s3BucketName) {
           
$prev = isset($e) ? $e : null;
            throw new \
InvalidArgumentException('The bucket name could not '
               
. 'be determined from the trail.', 0, $prev);
        }

        return new
self($s3Client, $s3BucketName, $options);
    }

   
/**
     * Constructs a LogFileIterator using the specified options:
     *
     * - trail_name: The name of the trail that is generating our logs. If
     *   none is provided, then "Default" will be used, since that is the name
     *   of the trail created in the AWS Management Console.
     * - key_prefix: The S3 key prefix of your log files. This value will be
     *   overwritten when using the `fromTrail()` method. However, if you are
     *   using the constructor, then this value will be used.
     * - start_date: The timestamp of the beginning of date range of the log
     *   records you want to read. You can pass this in as a `DateTime` object,
     *   integer (unix timestamp), or a string compatible with `strtotime()`.
     * - end_date: The timestamp of the end of date range of the log records
     *   you want to read. You can pass this in as a `DateTime` object, integer
     *   (unix timestamp), or a string compatible with `strtotime()`.
     * - account_id: This is your AWS account ID, which is the 12-digit number
     *   found on the *Account Identifiers* section of the *AWS Security
     *   Credentials* page. See https://console.aws.amazon.com/iam/home?#security_credential
     * - log_region: Region of the services of the log records you want to read.
     *
     * @param S3Client $s3Client
     * @param string   $s3BucketName
     * @param array    $options
     */
   
public function __construct(
       
S3Client $s3Client,
       
$s3BucketName,
        array
$options = []
    ) {
       
$this->s3Client = $s3Client;
       
$this->s3BucketName = $s3BucketName;
       
parent::__construct($this->buildListObjectsIterator($options));
    }

   
/**
     * An override of the typical current behavior of \IteratorIterator to
     * format the output such that the bucket and key are returned in an array
     *
     * @return array|bool
     */
   
public function current()
    {
        if (
$object = parent::current()) {
            return [
               
'Bucket' => $this->s3BucketName,
               
'Key'    => $object['Key']
            ];
        }

        return
false;
    }

   
/**
     * Constructs an S3 ListObjects iterator, optionally decorated with
     * FilterIterators, based on the provided options.
     *
     * @param array $options
     *
     * @return \Iterator
     */
   
private function buildListObjectsIterator(array $options)
    {
       
// Extract and normalize the date values from the options
       
$startDate = isset($options[self::START_DATE])
            ?
$this->normalizeDateValue($options[self::START_DATE])
            :
null;
       
$endDate = isset($options[self::END_DATE])
            ?
$this->normalizeDateValue($options[self::END_DATE])
            :
null;

       
// Determine the parts of the key prefix of the log files being read
       
$parts = [
           
'prefix' => isset($options[self::KEY_PREFIX])
                    ?
$options[self::KEY_PREFIX]
                    :
null,
           
'account' => isset($options[self::ACCOUNT_ID])
                    ?
$options[self::ACCOUNT_ID]
                    :
self::PREFIX_WILDCARD,
           
'region' => isset($options[self::LOG_REGION])
                    ?
$options[self::LOG_REGION]
                    :
self::PREFIX_WILDCARD,
           
'date' => $this->determineDateForPrefix($startDate, $endDate),
        ];

       
// Determine the longest key prefix that can be used to retrieve all
        // of the relevant log files.
       
$candidatePrefix = ltrim(strtr(self::PREFIX_TEMPLATE, $parts), '/');
       
$logKeyPrefix = $candidatePrefix;
       
$index = strpos($candidatePrefix, self::PREFIX_WILDCARD);

        if (
$index !== false) {
           
$logKeyPrefix = substr($candidatePrefix, 0, $index);
        }

       
// Create an iterator that will emit all of the objects matching the
        // key prefix.
       
$objectsIterator = $this->s3Client->getIterator('ListObjects', [
           
'Bucket' => $this->s3BucketName,
           
'Prefix' => $logKeyPrefix,
        ]);

       
// Apply regex and/or date filters to the objects iterator to emit only
        // log files matching the options.
       
$objectsIterator = $this->applyRegexFilter(
           
$objectsIterator,
           
$logKeyPrefix,
           
$candidatePrefix
       
);

       
$objectsIterator = $this->applyDateFilter(
           
$objectsIterator,
           
$startDate,
           
$endDate
       
);

        return
$objectsIterator;
    }

   
/**
     * Normalizes a date value to a unix timestamp
     *
     * @param int|string|\DateTimeInterface $date
     *
     * @return int
     * @throws \InvalidArgumentException if the value cannot be converted to
     *     a timestamp
     */
   
private function normalizeDateValue($date)
    {
        if (
is_string($date)) {
           
$date = strtotime($date);
        } elseif (
$date instanceof \DateTimeInterface) {
           
$date = $date->format('U');
        } elseif (!
is_int($date)) {
            throw new \
InvalidArgumentException('Date values must be a '
               
. 'string, an int, or a DateTime object.');
        }

        return
$date;
    }

   
/**
     * Uses the provided date values to determine the date portion of the prefix
     */
   
private function determineDateForPrefix($startDate, $endDate)
    {
       
// The default date value should look like "*/*/*" after joining
       
$dateParts = array_fill_keys(['Y', 'm', 'd'], self::PREFIX_WILDCARD);

       
// Narrow down the date by replacing the WILDCARDs with values if they
        // are the same for the start and end date.
       
if ($startDate && $endDate) {
            foreach (
$dateParts as $key => &$value) {
               
$candidateValue = date($key, $startDate);
                if (
$candidateValue === date($key, $endDate)) {
                   
$value = $candidateValue;
                } else {
                    break;
                }
            }
        }

        return
join('/', $dateParts);
    }

   
/**
     * Applies a regex iterator filter that limits the ListObjects result set
     * based on the provided options.
     *
     * @param \Iterator $objectsIterator
     * @param string    $logKeyPrefix
     * @param string    $candidatePrefix
     *
     * @return \Iterator
     */
   
private function applyRegexFilter(
       
$objectsIterator,
       
$logKeyPrefix,
       
$candidatePrefix
   
) {
       
// If the prefix and candidate prefix are not the same, then there were
        // WILDCARDs.
       
if ($logKeyPrefix !== $candidatePrefix) {
           
// Turn the candidate prefix into a regex by trimming and
            // converting WILDCARDs to regex notation.
           
$regex = rtrim($candidatePrefix, '/' . self::PREFIX_WILDCARD) . '/';
           
$regex = strtr($regex, [self::PREFIX_WILDCARD => '[^/]+']);

           
// After trimming WILDCARDs or the end, if the regex is the same as
            // the prefix, then no regex is needed.
           
if ($logKeyPrefix !== $regex) {
               
// Apply a regex filter iterator to remove files that don't
                // match the provided options.
               
$objectsIterator = new \CallbackFilterIterator(
                   
$objectsIterator,
                    function (
$object) use ($regex) {
                        return
preg_match("#{$regex}#", $object['Key']);
                    }
                );
            }
        }

        return
$objectsIterator;
    }

   
/**
     * Applies an iterator filter to restrict the ListObjects result set to the
     * specified date range.
     *
     * @param \Iterator $objectsIterator
     * @param int       $startDate
     * @param int       $endDate
     *
     * @return \Iterator
     */
   
private function applyDateFilter($objectsIterator, $startDate, $endDate)
    {
       
// If either a start or end date was provided, filter out dates that
        // don't match the date range.
       
if ($startDate || $endDate) {
           
$fn = function ($object) use ($startDate, $endDate) {
                if (!
preg_match('/[0-9]{8}T[0-9]{4}Z/', $object['Key'], $m)) {
                    return
false;
                }
               
$date = strtotime($m[0]);

                return (!
$startDate || $date >= $startDate)
                    && (!
$endDate || $date <= $endDate);
            };
           
$objectsIterator = new \CallbackFilterIterator($objectsIterator, $fn);
        }

        return
$objectsIterator;
    }
}