LCOV - code coverage report
Current view: top level - core - awsabstractsignature.cpp (source / functions) Hit Total Coverage
Test: libqtaws 0.1.0 Lines: 45 45 100.0 %
Date: 2015-06-16 07:50:35 Functions: 10 11 90.9 %

          Line data    Source code
       1             : /*
       2             :     Copyright 2013-2015 Paul Colby
       3             : 
       4             :     This file is part of libqtaws.
       5             : 
       6             :     Libqtaws is free software: you can redistribute it and/or modify
       7             :     it under the terms of the GNU Lesser General Public License as published by
       8             :     the Free Software Foundation, either version 3 of the License, or
       9             :     (at your option) any later version.
      10             : 
      11             :     Libqtaws is distributed in the hope that it will be useful,
      12             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :     GNU Lesser General Public License for more details.
      15             : 
      16             :     You should have received a copy of the GNU Lesser General Public License
      17             :     along with libqtaws.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : #include "awsabstractsignature.h"
      21             : #include "awsabstractsignature_p.h"
      22             : 
      23             : #include <QDebug>
      24             : #include <QDir>
      25             : 
      26             : QTAWS_BEGIN_NAMESPACE
      27             : 
      28             : /**
      29             :  * @class  AwsAbstractSignature
      30             :  *
      31             :  * @brief  Interface class for providing AWS signatures.
      32             :  */
      33             : 
      34             : /**
      35             :  * @internal
      36             :  *
      37             :  * @brief  Initialises an AwsAbstractSignature object.
      38             :  *
      39             :  * This internal constrcutor is used by derived classes that do not wish to
      40             :  * provider their own private implementations.
      41             :  */
      42          26 : AwsAbstractSignature::AwsAbstractSignature() : d_ptr(new AwsAbstractSignaturePrivate(this))
      43             : {
      44             : 
      45          26 : }
      46             : 
      47             : /**
      48             :  * @internal
      49             :  *
      50             :  * @brief  Initialises an AwsAbstractSignature object.
      51             :  *
      52             :  * This internal constrcutor is used by derived classes to provide their own
      53             :  * private implementations if they wish to.
      54             :  *
      55             :  * @param  d  Internal private implementation to use.
      56             :  */
      57         236 : AwsAbstractSignature::AwsAbstractSignature(AwsAbstractSignaturePrivate * const d) : d_ptr(d)
      58             : {
      59             : 
      60         236 : }
      61             : 
      62             : /**
      63             :  * @brief  AwsAbstractSignature destructor.
      64             :  */
      65         262 : AwsAbstractSignature::~AwsAbstractSignature() {
      66         262 :     delete d_ptr;
      67         262 : }
      68             : 
      69             : /**
      70             :  * @fn     void AwsAbstractSignature::sign() const
      71             :  *
      72             :  * @brief  Sign an AWS request.
      73             :  *
      74             :  * Note, \a credentials must be valid before calling this function.  So, for
      75             :  * example, if \a credentials has expired, and is refreshable, it is the
      76             :  * caller's responsibility to refresh the credentials before calling this
      77             :  * function.
      78             :  *
      79             :  * @param  operation     The network operation to sign \a request for.
      80             :  * @param  request       The network request to be signed.
      81             :  * @param  credentials   The credentials to use for signing.
      82             :  * @param  data          Optional POST / PUT data to sign \a request for.
      83             :  */
      84             : 
      85             : /**
      86             :  * @internal
      87             :  *
      88             :  * @class  AwsAbstractSignaturePrivate
      89             :  *
      90             :  * @brief  Private implementation for AwsAbstractSignature.
      91             :  *
      92             :  * @warning  This is an internal private implementation class, and as such external should
      93             :  *           code should **not** depend directly on anything contained within this class.
      94             :  */
      95             : 
      96             : /**
      97             :  * @internal
      98             :  *
      99             :  * @brief  Constructs a new AwsAbstractSignaturePrivate object.
     100             :  *
     101             :  * @param  q  Pointer to this object's public AwsAbstractSignature instance.
     102             :  */
     103         262 : AwsAbstractSignaturePrivate::AwsAbstractSignaturePrivate(AwsAbstractSignature * const q) : q_ptr(q)
     104             : {
     105             : 
     106         262 : }
     107             : 
     108             : /**
     109             :  * @internal
     110             :  *
     111             :  * @brief  AwsAbstractSignaturePrivate destructor.
     112             :  *
     113             :  * This virtual destructor does nothing (yet) - its here to allow for safe
     114             :  * polymorphic destruction.
     115             :  */
     116         288 : AwsAbstractSignaturePrivate::~AwsAbstractSignaturePrivate()
     117             : {
     118             : 
     119         288 : }
     120             : 
     121             : /**
     122             :  * @brief  Create an AWS Signature canonical path.
     123             :  *
     124             :  * This function simply returns the fully-URL-encoded path.  However, if the path
     125             :  * is empty, then a single '/' is returned, as is required by Amazon for both V2
     126             :  * and V4 signatures (and presumably other versions too).
     127             :  *
     128             :  * @param  url  URL from which to extract the path.
     129             :  *
     130             :  * @return An AWS Signature canonical path.
     131             :  *
     132             :  * @see    http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html
     133             :  * @see    http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
     134             :  */
     135         114 : QString AwsAbstractSignaturePrivate::canonicalPath(const QUrl &url) const
     136             : {
     137         114 :     QString path = QDir::cleanPath(QLatin1Char('/') + url.path(QUrl::FullyEncoded));
     138             : 
     139             :     // If the path begins with "//", remove one of the redundant slashes.
     140             :     // Note, this is only needed on Windows, because there QDir::speparator is
     141             :     // '\', and internally QDir::cleanPath swaps all separators to '/', before
     142             :     // calling qt_normalizePathSegments with allowUncPaths set to true, so that
     143             :     // '//' is preserved to allow of Windows UNC paths beginning with '\\'.
     144             :     // This should probably be reported as a bug in Qt::cleanPath("//...").
     145             : #ifdef Q_OS_WIN
     146             :     if (path.startsWith(QLatin1String("//"))) {
     147             :         path.remove(0, 1); // Remove the first of two forward slashes.
     148             :     }
     149             : #endif
     150             : 
     151             :     // Restore the trailing '/' if QDir::cleanPath (rightly) removed one.
     152         114 :     if ((url.path().endsWith(QLatin1Char('/'))) && (!path.endsWith(QLatin1Char('/')))) {
     153           4 :         path += QLatin1Char('/');
     154             :     }
     155             : 
     156         114 :     return path;
     157             : }
     158             : 
     159             : /**
     160             :  * @brief  Create an AWS Signature canonical query.
     161             :  *
     162             :  * This function returns an HTTP query string in Amazon's canonical form.  That is,
     163             :  * all query parameters are sorted by keys (**but not keys-then-values**), then
     164             :  * joined with `&` separators, in `key=value` pairs with both keys and values being
     165             :  * URL percent encoded.
     166             :  *
     167             :  * @note   The canonical form produced by this function is used by Amazon's later
     168             :  *         signature formats (versions 2, 3 and 4), but not their earlier formats
     169             :  *         (versions 0 and 1).
     170             :  *
     171             :  * @param  query  Query to encode the HTTP query string from.
     172             :  *
     173             :  * @return An AWS Signature canonical query string.
     174             :  *
     175             :  * @see    http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html
     176             :  * @see    http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
     177             :  */
     178         110 : QByteArray AwsAbstractSignaturePrivate::canonicalQuery(const QUrlQuery &query) const
     179             : {
     180             :     typedef QPair<QString, QString> QStringPair;
     181         110 :     QList<QStringPair> list = query.queryItems(QUrl::FullyDecoded);
     182         110 :     qSort(list);
     183         220 :     QString result;
     184         179 :     foreach (const QStringPair &pair, list) {
     185          69 :         if (!result.isEmpty()) result += QLatin1Char('&');
     186         138 :         result += QString::fromUtf8(QUrl::toPercentEncoding(pair.first)) + QLatin1Char('=') +
     187          69 :                   QString::fromUtf8(QUrl::toPercentEncoding(pair.second));
     188         110 :     }
     189         220 :     return result.toUtf8();
     190             : }
     191             : 
     192             : /**
     193             :  * @brief  Create an AWS Signature request method string.
     194             :  *
     195             :  * This function simply converts QNetworkAccessManager operations (enum values)
     196             :  * to strings appropriate to use in AWS signatures.
     197             :  *
     198             :  * @param  operation  The network operation to convert to string.
     199             :  *
     200             :  * @return A string representation of \p operation, or an empty string if the
     201             :  *         operation is not recognised or otherwise unsupported.
     202             :  */
     203         114 : QString AwsAbstractSignaturePrivate::httpMethod(const QNetworkAccessManager::Operation operation) const
     204             : {
     205         114 :     switch (operation) {
     206           4 :         case QNetworkAccessManager::DeleteOperation: return QLatin1String("DELETE");
     207           1 :         case QNetworkAccessManager::HeadOperation:   return QLatin1String("HEAD");
     208          61 :         case QNetworkAccessManager::GetOperation:    return QLatin1String("GET");
     209          46 :         case QNetworkAccessManager::PostOperation:   return QLatin1String("POST");
     210           1 :         case QNetworkAccessManager::PutOperation:    return QLatin1String("PUT");
     211             :         case QNetworkAccessManager::CustomOperation: // Fall through.
     212             :         default:
     213             :             // Catch this in debug mode for easier development / debugging.
     214           1 :             Q_ASSERT_X(false, Q_FUNC_INFO, "invalid operation");
     215             :     }
     216           1 :     return QString(); // Operation was invalid / unsupported.
     217             : }
     218             : 
     219             : /**
     220             :  * @brief   Set a query item, checking for existing values first.
     221             :  *
     222             :  * This function is a light wrapper around QUrlQuery::addQueryItem() that first
     223             :  * checks for existing values.  Existing values will not be overwritten, instead
     224             :  * if existing values are found, this function will simply check if the exsting
     225             :  * value matches the desired \a value, and if not, will return `false` and
     226             :  * optionally (according to \a warnOnNonIdenticalDuplicate) issue a qWarning().
     227             :  *
     228             :  * Typically, when setting something that must be a specific value, such as an
     229             :  * access key ID, \a warnOnNonIdenticalDuplicate would be `true`. However, when
     230             :  * setting query items as a fall-back default, such as a current timestamp,
     231             :  * \a warnOnNonIdenticalDuplicate would typically be set to `false`.
     232             :  *
     233             :  * @param   query  URL query to add the query item to.
     234             :  * @param   key    Query item key to add to \a query.
     235             :  * @param   value  Query item value to add to \a query.
     236             :  * @param   warnOnNonIdenticalDuplicate  If `true`, and an exisiting \a key
     237             :  *          value is found in \a query that has a value other than \a value,
     238             :  *          then a qWarning() is issued, otherwise the duplicate is silently
     239             :  *          ignored.
     240             :  *
     241             :  * @return  `true` if the query item was set successfully or was already set to
     242             :  *          the requested value previously, `false` otherwise.
     243             :  */
     244          37 : bool AwsAbstractSignaturePrivate::setQueryItem(QUrlQuery &query, const QString &key, const QString &value,
     245             :                                                const bool warnOnNonIdenticalDuplicate) const
     246             : {
     247          37 :     if (query.hasQueryItem(key)) {
     248          20 :         const QString existingValue = query.queryItemValue(key, QUrl::FullyEncoded);
     249          20 :         const bool existingQueryItemIsIdentical = (existingValue == value);
     250          20 :         if ((warnOnNonIdenticalDuplicate) && (!existingQueryItemIsIdentical)) {
     251           2 :             qWarning() << "AwsAbstractSignature::setQueryItem Not overwriting existing value for key"
     252           1 :                        << key << ':' << existingValue;
     253             :         }
     254          20 :         return existingQueryItemIsIdentical;
     255             :     }
     256          17 :     query.addQueryItem(key, value);
     257          17 :     return true;
     258             : }
     259             : 
     260             : /**
     261             :  * @fn     int AwsAbstractSignature::version() const
     262             :  *
     263             :  * @brief  AWS Signature version implemented by this class.
     264             :  *
     265             :  * Derived classes must implement this function to report the version of
     266             :  * the AWS Signature implemented by the class.
     267             :  *
     268             :  * @return The AWS Signature version implemented by this class.
     269             :  */
     270             : 
     271             : QTAWS_END_NAMESPACE

Generated by: LCOV version 1.11