libqtaws  0.1.0
UnofficialAWSlibraryforQt-InternalDocumentation
awsabstractsignature.cpp
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  */
43 {
44 
45 }
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  */
58 {
59 
60 }
61 
62 /**
63  * @brief AwsAbstractSignature destructor.
64  */
66  delete d_ptr;
67 }
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  */
104 {
105 
106 }
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  */
117 {
118 
119 }
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 QString AwsAbstractSignaturePrivate::canonicalPath(const QUrl &url) const
136 {
137  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  if ((url.path().endsWith(QLatin1Char('/'))) && (!path.endsWith(QLatin1Char('/')))) {
153  path += QLatin1Char('/');
154  }
155 
156  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 QByteArray AwsAbstractSignaturePrivate::canonicalQuery(const QUrlQuery &query) const
179 {
180  typedef QPair<QString, QString> QStringPair;
181  QList<QStringPair> list = query.queryItems(QUrl::FullyDecoded);
182  qSort(list);
183  QString result;
184  foreach (const QStringPair &pair, list) {
185  if (!result.isEmpty()) result += QLatin1Char('&');
186  result += QString::fromUtf8(QUrl::toPercentEncoding(pair.first)) + QLatin1Char('=') +
187  QString::fromUtf8(QUrl::toPercentEncoding(pair.second));
188  }
189  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 QString AwsAbstractSignaturePrivate::httpMethod(const QNetworkAccessManager::Operation operation) const
204 {
205  switch (operation) {
206  case QNetworkAccessManager::DeleteOperation: return QLatin1String("DELETE");
207  case QNetworkAccessManager::HeadOperation: return QLatin1String("HEAD");
208  case QNetworkAccessManager::GetOperation: return QLatin1String("GET");
209  case QNetworkAccessManager::PostOperation: return QLatin1String("POST");
210  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  Q_ASSERT_X(false, Q_FUNC_INFO, "invalid operation");
215  }
216  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 bool AwsAbstractSignaturePrivate::setQueryItem(QUrlQuery &query, const QString &key, const QString &value,
245  const bool warnOnNonIdenticalDuplicate) const
246 {
247  if (query.hasQueryItem(key)) {
248  const QString existingValue = query.queryItemValue(key, QUrl::FullyEncoded);
249  const bool existingQueryItemIsIdentical = (existingValue == value);
250  if ((warnOnNonIdenticalDuplicate) && (!existingQueryItemIsIdentical)) {
251  qWarning() << "AwsAbstractSignature::setQueryItem Not overwriting existing value for key"
252  << key << ':' << existingValue;
253  }
254  return existingQueryItemIsIdentical;
255  }
256  query.addQueryItem(key, value);
257  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
bool setQueryItem(QUrlQuery &query, const QString &key, const QString &value, const bool warnOnNonIdenticalDuplicate=true) const
Set a query item, checking for existing values first.
AwsAbstractSignaturePrivate *const d_ptr
Internal d-pointer.
Private implementation for AwsAbstractSignature.
QByteArray canonicalQuery(const QUrlQuery &query) const
Create an AWS Signature canonical query.
AwsAbstractSignature()
Initialises an AwsAbstractSignature object.
virtual ~AwsAbstractSignature()
AwsAbstractSignature destructor.
virtual ~AwsAbstractSignaturePrivate()
AwsAbstractSignaturePrivate destructor.
QString httpMethod(const QNetworkAccessManager::Operation operation) const
Create an AWS Signature request method string.
Interface class for providing AWS signatures.
AwsAbstractSignaturePrivate(AwsAbstractSignature *const q)
Constructs a new AwsAbstractSignaturePrivate object.
QString canonicalPath(const QUrl &url) const
Create an AWS Signature canonical path.