Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for OCI Authentication Protocol #1109

Draft
wants to merge 144 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
144 commits
Select commit Hold shift + click to select a range
aff9762
Merge branch 'release/2.0.1-rc.1'
shamasis Jul 9, 2016
7b21616
Merge branch 'release/2.0.1-rc.2'
shamasis Jul 9, 2016
6d7d251
Merge branch 'release/v2.1.0'
Jul 12, 2016
59d6bc5
Merge branch 'release/v2.1.1'
Jul 13, 2016
a0e7fec
Merge branch 'release/v2.2.0'
Jul 19, 2016
c30b625
Merge branch 'release/v2.2.1'
Jul 21, 2016
bae9ea7
Merge branch 'release/v2.2.2'
Jul 25, 2016
73ec96b
Merge branch 'release/v2.2.3'
Jul 29, 2016
2a02c7a
Merge branch 'release/v2.2.4'
Jul 29, 2016
b6f698f
Merge branch 'release/v2.2.5'
Jul 30, 2016
90a1eff
Merge branch 'release/v2.3.0'
Aug 4, 2016
5f6272c
Merge branch 'release/v2.3.1'
Aug 5, 2016
794960b
Merge branch 'release/v2.3.2'
Aug 5, 2016
f797baf
Merge branch 'release/v2.4.0'
Aug 12, 2016
73357eb
Merge branch 'release/v2.4.1'
Aug 12, 2016
9662cfb
Merge branch 'release/v2.4.2'
Aug 18, 2016
2d1c422
Merge branch 'release/v2.4.3'
Aug 23, 2016
e43701c
Merge branch 'release/v2.4.4'
Aug 25, 2016
fc105ea
Merge branch 'release/v2.4.5'
Aug 30, 2016
ecafeb7
Merge branch 'release/v2.5.0'
Sep 12, 2016
92e55aa
Merge branch 'release/v2.5.1'
Sep 16, 2016
d519f37
Merge branch 'release/v2.5.2'
Sep 21, 2016
5d9aa1d
Merge branch 'release/v2.5.3'
Sep 26, 2016
83e11f5
Merge branch 'release/v2.5.4'
Sep 28, 2016
302bd63
Merge branch 'release/v3.0.0'
Oct 10, 2016
f11bb0e
Merge branch 'release/v3.0.1'
Oct 13, 2016
42dae80
Merge branch 'release/v3.0.2'
Oct 14, 2016
0a46aea
Merge branch 'release/v3.0.3'
Nov 9, 2016
41c7534
Merge branch 'release/v3.0.4'
Nov 17, 2016
fc10879
Merge branch 'release/v3.0.5'
Nov 17, 2016
dea3b35
Merge branch 'release/v3.0.6'
Nov 29, 2016
a69f4a4
Merge branch 'release/v3.0.7'
Nov 30, 2016
bd3d714
Merge branch 'release/v3.0.8'
Dec 9, 2016
eae8afb
Merge branch 'release/v3.0.9'
Dec 14, 2016
9dd566a
Merge branch 'release/v3.0.10'
Dec 15, 2016
755d503
Merge branch 'release/4.0.0'
shamasis Dec 29, 2016
0c40f59
Merge branch 'release/v4.0.1'
czardoz Jan 2, 2017
d1e7f0d
Merge branch 'release/4.0.2'
shamasis Jan 6, 2017
d0e4ac8
Merge branch 'release/v4.0.3'
czardoz Jan 31, 2017
4a9e594
Merge branch 'release/v4.0.4'
czardoz Feb 20, 2017
d3afb2f
Merge branch 'release/v4.1.0'
czardoz Mar 7, 2017
39c85c7
Merge branch 'release/v4.1.1'
czardoz Mar 14, 2017
3adba22
Merge branch 'release/v5.0.0'
czardoz Mar 16, 2017
c6a4860
Merge branch 'release/v6.0.0'
czardoz Apr 5, 2017
66a16ea
Merge branch 'release/6.0.1'
kunagpal Apr 10, 2017
522480b
Merge branch 'release/v6.1.0'
czardoz Apr 25, 2017
9a396a9
Merge branch 'release/v6.1.1'
czardoz May 8, 2017
88f6bcf
Merge branch 'release/v6.1.2'
czardoz May 8, 2017
c37cc0c
Merge branch 'release/v6.1.3'
czardoz May 9, 2017
83e68e4
Merge branch 'release/v6.1.4'
czardoz May 12, 2017
900e414
Merge branch 'release/6.1.5'
kunagpal May 15, 2017
5a0b09f
Merge branch 'release/6.1.6'
kunagpal May 16, 2017
9089e6d
Merge branch 'release/6.2.0'
kunagpal Jun 15, 2017
0cbbc2d
Merge branch 'release/v6.2.1'
czardoz Jun 23, 2017
464dd57
Merge branch 'release/6.2.2'
kunagpal Jun 28, 2017
e4c5ee4
Merge branch 'release/6.2.3'
kunagpal Jul 5, 2017
c4ba9e4
Merge branch 'release/6.2.4'
kunagpal Jul 11, 2017
57d4e00
Merge branch 'release/6.2.5'
kunagpal Jul 19, 2017
e57e674
Merge branch 'release/6.2.6'
kunagpal Aug 18, 2017
25a8d66
Merge branch 'release/6.3.0'
kunagpal Aug 21, 2017
6558fb3
Merge branch 'release/6.3.1'
kunagpal Aug 28, 2017
1d8e1d4
Merge branch 'release/6.3.2'
kunagpal Aug 28, 2017
a9ce626
Merge branch 'release/6.4.0'
kunagpal Sep 28, 2017
03a212e
Merge branch 'release/6.4.1'
kunagpal Oct 13, 2017
3471697
Merge branch 'release/6.4.2'
kunagpal Nov 2, 2017
9a34087
Merge branch 'release/7.0.0'
kamalaknn Nov 7, 2017
8e08c42
Merge branch 'release/7.0.1'
kamalaknn Nov 8, 2017
62c854c
Merge branch 'release/7.1.0'
kamalaknn Nov 20, 2017
0225945
Merge branch 'release/7.1.1'
kamalaknn Nov 30, 2017
a788757
Merge branch 'release/7.1.2'
kunagpal Dec 8, 2017
0085985
Merge branch 'release/7.1.3'
kunagpal Jan 2, 2018
d2e8cea
Merge branch 'release/7.1.4'
kunagpal Apr 9, 2018
7734c02
Merge branch 'release/7.1.5'
kunagpal Apr 9, 2018
62083e3
Merge branch 'release/7.1.6'
kamalaknn May 24, 2018
3896edb
Merge branch 'release/7.2.0'
kamalaknn Jun 30, 2018
ddd7d4a
Merge branch 'release/7.3.0'
kunagpal Aug 20, 2018
400c198
Merge branch 'release/7.4.0'
kunagpal Sep 21, 2018
e69bfa0
Merge branch 'release/7.4.1'
kunagpal Nov 3, 2018
6fedc6f
Merge branch 'release/7.4.2'
kunagpal Nov 7, 2018
b089359
Merge branch 'release/7.5.0'
codenirvana Dec 6, 2018
28cccf3
Merge branch 'release/7.6.0'
codenirvana Dec 25, 2018
97a953c
Merge branch 'release/7.6.1'
codenirvana Jan 3, 2019
a7b30b8
Merge branch 'release/7.7.0'
codenirvana Feb 1, 2019
491dd93
Merge branch 'release/7.7.0'
codenirvana Feb 1, 2019
f1e36b5
Merge branch 'release/7.7.1'
codenirvana Feb 7, 2019
0bc859e
Merge branch 'release/7.8.0'
codenirvana Feb 15, 2019
27d82d9
Merge branch 'release/7.9.0'
codenirvana Feb 19, 2019
32dc9d9
Merge branch 'release/7.9.1'
codenirvana Feb 20, 2019
a7027d4
Merge branch 'release/7.10.0'
codenirvana Mar 1, 2019
c49a9b5
Merge branch 'release/7.11.0'
codenirvana Mar 19, 2019
1d4c269
Merge branch 'release/7.12.0'
codenirvana Apr 8, 2019
4a82dfc
Merge branch 'release/7.13.0'
codenirvana Apr 25, 2019
0a70256
Merge branch 'release/7.14.0'
codenirvana May 16, 2019
81578d2
Merge branch 'release/7.15.0'
codenirvana Jun 7, 2019
38e9e66
Merge branch 'release/7.15.0'
codenirvana Jun 7, 2019
e659a67
Merge branch 'release/7.15.1'
codenirvana Jun 18, 2019
90681b5
Merge branch 'release/7.15.2'
codenirvana Jun 25, 2019
034d1a7
Merge branch 'release/7.16.0'
codenirvana Aug 1, 2019
ebb855b
Merge branch 'release/7.16.1'
codenirvana Aug 1, 2019
369315e
Merge branch 'release/7.16.2'
codenirvana Aug 14, 2019
915985e
Merge branch 'release/7.16.3'
codenirvana Aug 19, 2019
2c57ea4
Merge branch 'release/7.17.0'
codenirvana Sep 4, 2019
8e7f146
Merge branch 'release/7.17.1'
codenirvana Sep 6, 2019
0ff9200
Merge branch 'release/7.18.0'
codenirvana Oct 1, 2019
240a512
Merge branch 'release/7.19.0'
codenirvana Oct 16, 2019
dd1905c
Merge branch 'release/7.20.0'
codenirvana Nov 12, 2019
72ab1eb
Merge branch 'release/7.20.1'
codenirvana Nov 13, 2019
4ae2bdf
Merge branch 'release/7.21.0'
codenirvana Dec 2, 2019
704f5b9
Merge branch 'release/7.22.0'
codenirvana Jan 10, 2020
3e79375
Merge branch 'release/7.23.0'
codenirvana Mar 2, 2020
2d03d34
Merge branch 'release/7.24.0'
codenirvana Mar 15, 2020
d36b556
Merge branch 'release/7.24.1'
codenirvana Mar 31, 2020
a813cc2
Merge branch 'release/7.24.2'
codenirvana Apr 8, 2020
860b4c6
Merge branch 'release/7.25.0'
codenirvana May 15, 2020
9ef3d82
Merge branch 'release/7.26.0'
codenirvana Jun 5, 2020
a90de54
Merge branch 'release/7.26.1'
codenirvana Jun 14, 2020
59ac23d
Merge branch 'release/7.26.2'
codenirvana Jul 13, 2020
44064f4
Merge branch 'release/7.26.3'
codenirvana Jul 30, 2020
a944b88
Merge branch 'release/7.26.4'
codenirvana Aug 18, 2020
ca3ec1d
Merge branch 'release/7.26.5' into master
codenirvana Aug 31, 2020
9200d60
Merge branch 'release/7.26.6' into master
codenirvana Sep 16, 2020
210a132
Merge branch 'release/7.26.7'
Oct 7, 2020
e7ad8ee
Merge branch 'release/7.26.8'
Oct 21, 2020
7103cc0
Add stub for oci signature v1 auth protocol
quixote911 Nov 19, 2020
f8be798
Implement some parts of oci authorizer
quixote911 Nov 19, 2020
aac783a
Add conditions to handle different types of request body
quixote911 Nov 19, 2020
81c65e4
Remove everything to do with request body in oci auth method
quixote911 Nov 20, 2020
b5f829c
Add polyfill for DOM Headers object, implement conversion function
quixote911 Nov 20, 2020
b733d35
Signature working!
quixote911 Dec 7, 2020
652bbc9
Add headers to postman request object
quixote911 Dec 7, 2020
3d21a3d
GET Request working via test case + postman!
quixote911 Dec 7, 2020
30426a0
Add code to handle request body (only raw)
quixote911 Dec 7, 2020
9b7cbbe
Code cleanup
quixote911 Dec 7, 2020
da900db
Add test case for OCI Auth for request with body
quixote911 Dec 11, 2020
e5d38c4
Cleaner code
quixote911 Dec 14, 2020
4532e6b
Propagate error via done callback
quixote911 Dec 14, 2020
555cbbe
Add test case for incorrect content type
quixote911 Dec 14, 2020
ade7087
Get body content properly
quixote911 Dec 14, 2020
f9fc8f0
Use correct content length
quixote911 Dec 14, 2020
458912e
minor fix
quixote911 Dec 14, 2020
94b7208
Move headers polyfill to separate file
quixote911 Dec 14, 2020
ae6d1c5
Add sshpk dependency back
quixote911 Dec 14, 2020
d87fb1f
Fix headers import
quixote911 Dec 14, 2020
d848664
Add test case for pre
quixote911 Dec 14, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/authorizer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ _.forEach({
oauth2: require('./oauth2'),
ntlm: require('./ntlm'),
apikey: require('./apikey'),
edgegrid: require('./edgegrid')
edgegrid: require('./edgegrid'),
oci: require('./oci-v1')
}, AuthLoader.addHandler);

/**
Expand Down
292 changes: 292 additions & 0 deletions lib/authorizer/oci-v1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
/**
* @fileOverview
*
* Implements the Oracle Cloud Infrastructure Signature v1 authentication method.
* Specification document: https://docs.cloud.oracle.com/en-us/iaas/Content/API/Concepts/signingrequests.htm
*/
var sshpk = require('sshpk'),
UrlParser = require('url'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use postman-url-encoder instead

crypto = require('crypto'),
Headers = require('./util').Headers,
_ = require('lodash'),
httpSignature = require('http-signature'),
urlEncoder = require('postman-url-encoder'),
RequestBody = require('postman-collection').RequestBody,
bodyBuilder = require('../requester/core-body-builder');

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra newline.


const HEADER_CONTENT_SHA = 'x-content-sha256',
HEADER_CONTENT_LEN = 'content-length',
HEADER_CONTENT_TYPE = 'content-type',
EMPTY_SHA = '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=',
HEADER_AUTHORIZATION = 'authorization',
HEADER_DATE = 'x-date',
HEADER_HOST = 'host',
CONTENT_TYPE_JSON = 'application/json',
EXTRA_HEADER_METHODS = ['POST', 'PUT', 'PATCH'],
PSEUDOHEADER_REQUEST_TARGET = '(request-target)';

// -------------------- POLYFILL for input to httpSignature.sign ---------------------------

class SignerRequest {
constructor (method, url, headers) {
this.headers = headers;
this.method = method;
this.path = UrlParser.parse(url).path;
}

getHeader (name) {
return this.headers.get(name);
}

setHeader (name, value) {
this.headers.set(name, value);
}
}

function computeBodyHash (body, algorithm, digestEncoding, callback) {
if (!(body && algorithm && digestEncoding) || body.isEmpty()) { return callback(); }

var hash = crypto.createHash(algorithm),
originalReadStream,
rawBody,
urlencodedBody,
graphqlBody;


if (body.mode === RequestBody.MODES.raw) {
rawBody = bodyBuilder.raw(body.raw).body;
hash.update(rawBody);

// hash.update('\n');
return callback(hash.digest(digestEncoding), rawBody.length);
}

if (body.mode === RequestBody.MODES.urlencoded) {
urlencodedBody = bodyBuilder.urlencoded(body.urlencoded).form;
urlencodedBody = urlEncoder.encodeQueryString(urlencodedBody);
hash.update(urlencodedBody);
hash.update('\n');

return callback(hash.digest(digestEncoding), urlencodedBody.length + 1);
}

if (body.mode === RequestBody.MODES.file) {
originalReadStream = _.get(body, 'file.content');

if (!originalReadStream) {
return callback();
}

return originalReadStream.cloneReadStream(function (err, clonedStream) {
if (err) { return callback(); }
var streamContentLength = 0;

clonedStream.on('data', function (chunk) {
hash.update(chunk);
streamContentLength += chunk.length;
});

clonedStream.on('end', function () {
hash.update('\n');
callback(hash.digest(digestEncoding), streamContentLength);
});
});
}

if (body.mode === RequestBody.MODES.graphql) {
graphqlBody = bodyBuilder.graphql(body.graphql).body;
hash.update(graphqlBody);
hash.update('\n');

return callback(hash.digest(digestEncoding), graphqlBody.length + 1);
}

// @todo: Figure out a way to calculate hash for formdata body type.

// ensure that callback is called if body.mode doesn't match with any of the above modes
return callback();
}
// ------------------------------------------------------------

// eslint-disable-next-line one-var
var convertToDomHeadersObject = function (postmanHeaders) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have to convert PostmanHeaderList?
Facing any issue with the get/set methods exposed by it?

let rawHeaders = {};

postmanHeaders.all().forEach(function (each) {
rawHeaders[each.key.toLowerCase()] = each.value;
});

return new Headers(rawHeaders);
},
getBodyDetails = function (body, callback) {
computeBodyHash(body, 'sha256', 'base64', callback);
},
processRequestForOci = function (request, auth, callback) {
getBodyDetails(request.body, function (bodyHash, bodyContentLength) {
const rawRequestUri = request.url.getRaw(),
domHeadersObject = convertToDomHeadersObject(request.headers),
uppercaseMethod = request.method.toUpperCase(),
minimumHeadersToSign = [HEADER_DATE, PSEUDOHEADER_REQUEST_TARGET, HEADER_HOST],
keyId = auth.get('tenancy') + '/' + auth.get('user') + '/' + auth.get('fingerprint'),
privateKeyBuffer = sshpk.parsePrivateKey(auth.get('privatekey'), 'auto').toBuffer('pem', {});

if (!domHeadersObject.has(HEADER_HOST)) {
const url = UrlParser.parse(rawRequestUri);

if (url.host) {
domHeadersObject.set(HEADER_HOST, url.host);
}
else {
return callback(new Error('Cannot parse host from url'));
}
}

if (!domHeadersObject.has(HEADER_DATE)) {
domHeadersObject.set(HEADER_DATE, new Date().toUTCString());
}
let headersToSign = [...minimumHeadersToSign];

if (!auth.get('forceDisableBodyHashing') && bodyHash && bodyContentLength &&
_.includes(EXTRA_HEADER_METHODS, uppercaseMethod)) {
if (!domHeadersObject.has(HEADER_CONTENT_TYPE)) {
domHeadersObject.set(HEADER_CONTENT_TYPE, CONTENT_TYPE_JSON);
}
else if (domHeadersObject.get(HEADER_CONTENT_TYPE) !== CONTENT_TYPE_JSON) {
return callback(new Error(`Only ${CONTENT_TYPE_JSON} is supported by OCI Auth`));
}

if (bodyHash) {
domHeadersObject.set(HEADER_CONTENT_SHA, bodyHash);
}
if (bodyContentLength === 0) {
// if buffer is empty, it can only be an empty string payload
domHeadersObject.set(HEADER_CONTENT_SHA, EMPTY_SHA);
}
if (!domHeadersObject.has(HEADER_CONTENT_LEN)) {
domHeadersObject.set(HEADER_CONTENT_LEN, `${bodyContentLength}`);
}
headersToSign = headersToSign.concat(HEADER_CONTENT_TYPE, HEADER_CONTENT_LEN, HEADER_CONTENT_SHA);
}

httpSignature.sign(new SignerRequest(uppercaseMethod, rawRequestUri, domHeadersObject), {
key: privateKeyBuffer,
keyId: keyId,
headers: headersToSign
});
// NOTE: OCI needs 'authorization' but our version of httpSignature (1.3.1)
// puts signature in 'Authorization'
// eslint-disable-next-line one-var
const AUTH_HEADER_BACKWARD_COMPATIBLE = 'Authorization',
authorizationHeader = domHeadersObject.get(AUTH_HEADER_BACKWARD_COMPATIBLE);

domHeadersObject.set(HEADER_AUTHORIZATION,
authorizationHeader.replace('Signature ', 'Signature version="1",'));
domHeadersObject.delete(AUTH_HEADER_BACKWARD_COMPATIBLE);

return callback(domHeadersObject);
});
};


/**
* @implements {AuthHandlerInterface}
*/
module.exports = {
/**
* @property {AuthHandlerInterface~AuthManifest}
*/
// TODO: Fix the manifest
manifest: {
info: {
name: 'oci-v1',
version: '1.0.0'
},
updates: [
{
property: HEADER_AUTHORIZATION,
type: 'header'
},
{
property: HEADER_DATE,
type: 'header'
},
{
property: HEADER_CONTENT_LEN,
type: 'header'
},
{
property: HEADER_CONTENT_TYPE,
type: 'header'
},
{
property: HEADER_CONTENT_SHA,
type: 'header'
}
]
},

/**
* Initializes a item (fetches all required parameters, etc) before the actual authorization step.
*
* @param {AuthInterface} auth AuthInterface instance created with request auth
* @param {Response} response Response of intermediate request (it any)
* @param {AuthHandlerInterface~authInitHookCallback} done Callback function called with error as first argument
*/
init: function (auth, response, done) {
done(null);
},

/**
* Checks the item, and fetches any parameters that are not already provided.
*
* @param {AuthInterface} auth AuthInterface instance created with request auth
* @param {AuthHandlerInterface~authPreHookCallback} done Callback function called with error, success and request
*/
pre: function (auth, done) {
done(null, Boolean(auth.get('tenancy') && auth.get('user') &&
auth.get('fingerprint') && auth.get('privatekey')));
},

/**
* Verifies whether the request was successful after being sent.
*
* @param {AuthInterface} auth AuthInterface instance created with request auth
* @param {Requester} response Response of the request
* @param {AuthHandlerInterface~authPostHookCallback} done Callback function called with error and success
*/
post: function (auth, response, done) {
done(null, true);
},

/**
* Signs a request.
*
* @param {AuthInterface} auth AuthInterface instance created with request auth
* @param {Request} request Request to be sent
* @param {AuthHandlerInterface~authSignHookCallback} done Callback function
*/
sign: function (auth, request, done) {
processRequestForOci(request, auth, function (result) {
if (!result) { return done(); }
if (result instanceof Error) { return done(result); }
const newHeaders = result,
setHeaderIfAvailable = function (headerName) {
const currentHeader = newHeaders.get(headerName);

if (currentHeader) {
request.addHeader({
key: headerName,
value: currentHeader,
system: true
});
}
},
allPossibleHeaders = [HEADER_AUTHORIZATION, HEADER_DATE, HEADER_CONTENT_LEN,
HEADER_CONTENT_SHA, HEADER_CONTENT_TYPE];

allPossibleHeaders.forEach(setHeaderIfAvailable);
done();
});
}
};
60 changes: 60 additions & 0 deletions lib/authorizer/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@

function Headers (values) {
this._values = values || {};
}

Headers.prototype.append = function (name, value) {
this._values[name] = value;
};

Headers.prototype.delete = function (name) {
delete this._values[name];
};

Headers.prototype.entries = function () {
const result = [];

for (let key in this._values) {
result.push([key, this._values[key]]);
}

return result;
};

Headers.prototype.get = function (name) {
return this._values[name];
};

Headers.prototype.has = function (name) {
return name in this._values;
};

Headers.prototype.keys = function () {
const result = [];

for (let key in this._values) {
result.push(key);
}

return result;
};

Headers.prototype.set = function (name, value) {
this._values[name] = value;
};

// eslint-disable-next-line no-unused-vars
Headers.prototype.values = function (name, value) {
const result = [];

// eslint-disable-next-line guard-for-in
for (let key in this._values) {
result.push(this._values[key]);
}

return result;
};

module.exports = {
Headers: Headers
};
17 changes: 3 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.