Qore AwsRestClient Module Reference 1.2
Loading...
Searching...
No Matches
AwsRestClient.qm.dox.h
1// -*- mode: c++; indent-tabs-mode: nil -*-
3
4/* AwsRestClient.qm Copyright (C) 2019 - 2023 Qore Technologies, s.r.o.
5
6 Permission is hereby granted, free of charge, to any person obtaining a
7 copy of this software and associated documentation files (the "Software"),
8 to deal in the Software without restriction, including without limitation
9 the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 and/or sell copies of the Software, and to permit persons to whom the
11 Software is furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 DEALINGS IN THE SOFTWARE.
23*/
24
25// minimum qore version
26
27// require type definitions everywhere
28
29// enable all warnings
30
31// don't use "$" for vars, members, and methods, assume local variable scope
32
33// do not ignore argument errors
34
35
36}
37
95namespace AwsRestClient {
97
104
105public:
107 const DefaultSendEncoding = "gzip";
108
110 const RequiredOptions = ...;
111
112
114 const QoreDigest = CRYPTO_DIGEST_SHA256;
115
117 const AwsSignatureAlgorithm = "AWS4-HMAC-SHA256";
118
120 const AwsTermination = "aws4_request";
121
122private:
123 // the AWS access key ID
124 string aws_keyid;
125 // the AWS secret access key
126 string aws_secret;
127 // the AWS region
128 string aws_region;
129 // the AWS service
130 string aws_service;
131 // is this request for AWS S3?
132 bool aws_s3 = False;
133 // temporary session token
134 string aws_token;
135 // credential scope suffix
136 string credential_scope_suffix;
137
138public:
139
141
207 constructor(hash<auto> opts, *softbool do_not_connect) ;
208
209
210 hash<auto> sendAndDecodeResponse(*data body, string m, string path, hash<auto> hdr, *reference<hash<auto>> info,
211 *softbool decode_errors) {
212 // get request time
213 date gmtime = Qore::gmtime();
214 // get credential scope
215 string scope = sprintf("%s/%s", gmtime.format("YYYYMMDD"), credential_scope_suffix);
216
217 // get signature and signed header string
218 string signed_headers;
219
220 // add configured headers without overriding
221 hdr = headers + hdr;
222
223 // get signature
224 string sig = getSignature(m, path, \hdr, body, gmtime, scope, \signed_headers);
225
226 // add authorization header
227 hdr.Authorization = sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s",
228 AwsSignatureAlgorithm, aws_keyid, scope, signed_headers, sig);
229
230 return RestClient::sendAndDecodeResponse(body, m, path, hdr, \info, decode_errors);
231 }
232
233 string getSignature(string http_method, string path, reference<hash<auto>> hdr, *data body, date gmtime,
234 string scope, reference<string> signed_headers) {
235 string req_string = getRequestString(http_method, path, \hdr, body, gmtime, scope, \signed_headers);
236
237 // calculate a signature
238 string gmdate = gmtime.format("YYYYMMDD");
239 // 1: key, date
240 binary signing_key = hmac(QoreDigest, gmdate, "AWS4" + aws_secret);
241 // 2: hash, region
242 signing_key = hmac(QoreDigest, aws_region, signing_key);
243 // 3: hash, service
244 signing_key = hmac(QoreDigest, aws_service, signing_key);
245 // 4: hash, termination
246 signing_key = hmac(QoreDigest, AwsTermination, signing_key);
247
248 string sig = hmac(QoreDigest, req_string, signing_key).toHex();
249
250 return sig;
251 }
252
253 private string getRequestString(string http_method, string path, reference<hash<auto>> hdr, *data body,
254 date gmtime, string scope, reference<string> signed_headers) {
255 // get AWS date value
256 string aws_date = gmtime.format("YYYYMMDDTHHmmSS") + "Z";
257 hdr."X-Amz-Date" = aws_date;
258 string csig = getCanonicalSignature(http_method, path, hdr, body, \signed_headers);
259
260 string msg = AwsSignatureAlgorithm + "\n"
261 + aws_date + "\n"
262 + scope + "\n"
263 + csig;
264
265 return msg;
266 }
267
268 private string getCanonicalSignature(string http_method, string path, hash<auto> hdr, *data body,
269 reference<string> signed_headers) {
270 // create the canonical string
271 // 1: HTTP request method
272 string cstr = http_method + "\n";
273
274 // make canonical URI
275 hash<UriQueryInfo> uri_info = parse_uri_query(path);
276
277 // 2: canonical URI parameter
278 string uri = uri_info.method ?? "/";
279
280 // normalize the path if not an S3 request
281 if (!aws_s3);
282
283
284 // URI-encode the path
285 uri = encode_url(uri, False);
286 // non-AWS-S3 requests must be URI-encoded twice
287 // https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
288 if (!aws_s3);
289
290 // add to canonical string
291 cstr += uri + "\n";
292
293 // 3: canonical query string
294 if (uri_info.params);
295
296 cstr += "\n";
297
298 // 4: canonical headers
299 // convert header keys to lowercase
300 hdr = map {$1.key.lwr(): $1.value}, hdr.pairIterator();
301 // add host header
302 hdr += {"host": getHostHeaderValue()};
303
304 // sort headers
305 hdr = map {$1: hdr{$1}}, sort(keys hdr);
306
307 cstr += (
308 foldl $1 + $2, (
309 map
310private:
311 static string trimall(string str) { "cls": Class::forName("AwsRestConnection"),
312 "options": RestConnection::ConnectionScheme.options + {
313 "data": <ConnectionOptionInfo>{
314 "type": "string",
315 "desc": "data serialization options are limited to `json` with this object",
316 "allowed_values": (
317 <AllowedValueInfo>{
318 "value": "json",
319 "desc": "use JSON serialization",
320 },
321 ),
322 "default_value": "json",
323 },
324 "aws_keyid": <ConnectionOptionInfo>{
325 "type": "string",
326 "desc": "AWS key ID",
327 },
328 "aws_secret": <ConnectionOptionInfo>{
329 "type": "string",
330 "desc": "the AWS secret access key value",
331 "sensitive": True,
332 },
333 "aws_region": <ConnectionOptionInfo>{
334 "type": "string",
335 "desc": "the AWS region to use (ex: `us-east-1`)",
336 },
337 "aws_service": <ConnectionOptionInfo>{
338 "type": "string",
339 "desc": "the AWS service to use (ex: `iam`)",
340 },
341 "aws_s3": <ConnectionOptionInfo>{
342 "type": "bool",
343 "desc": "set to `True` to flag this object for use with AWS S3, which requires special "
344 "message encoding",
345 "default_value": False,
346 },
347 "aws_token": <ConnectionOptionInfo>{
348 "type": "string",
349 "desc": "a temporary session token from AWS Security Token Service for this HTTP session",
350 },
351 },
352 "required_options": foldl $1 + "," + $2, AwsRestClient::RequiredOptions,
353 };
354 }
355
357
367 constructor(string name, string description, string url, hash<auto> attributes = {}, hash<auto> options = {})
368 ;
369
370
372 string getType();
373
374
376
382 DataProvider::AbstractDataProvider getDataProvider();
383
384
386
391
392
394
399 AwsRestClient getImpl(bool connect = True, *hash<auto> rtopts);
400
402 hash<ConnectionSchemeInfo> getConnectionSchemeInfoImpl();
403};
404};
constructor(hash< auto > opts, *softbool do_not_connect)
creates the object with the given options
bool hasDataProvider()
returns True, as this connection always returns a data provider with the getDataProvider() method
constructor(string name, string description, string url, hash< auto > attributes={}, hash< auto > options={})
creates the AwsRestConnection object
AwsRestClient getImpl(bool connect=True, *hash< auto > rtopts)
returns a AwsRestClient::AwsRestClient object
DataProvider::AbstractDataProvider getDataProvider()
returns a data provider object for this connection
string getType()
returns "awsrest"
const RequiredOptions
required options
Definition: AwsRestClient.qm.dox.h:110
hash< ConnectionSchemeInfo > getConnectionSchemeInfoImpl()
Returns the ConnectionSchemeInfo hash for this object.
const True
const False
date gmtime()
auto sort(auto arg)
string encode_url(string url, softbool encode_all=False)
the AwsRestClient namespace contains all the objects in the AwsRestClient module
Definition: AwsRestClient.qm.dox.h:95