Skip to content

Commit

Permalink
Add support for oauth authorization (#489)
Browse files Browse the repository at this point in the history
  • Loading branch information
strausr committed Jun 3, 2021
1 parent 4920487 commit 4e3b0f8
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 16 deletions.
16 changes: 11 additions & 5 deletions lib-es5/api_client/call_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,17 @@ var ensurePresenceOf = utils.ensurePresenceOf;
function call_api(method, uri, params, callback, options) {
ensurePresenceOf({ method, uri });
var api_url = utils.base_api_url(uri, options);
var auth = {
key: ensureOption(options, "api_key"),
secret: ensureOption(options, "api_secret")
};

var auth = {};
if (options.oauth_token || config().oauth_token) {
auth = {
oauth_token: ensureOption(options, "oauth_token")
};
} else {
auth = {
key: ensureOption(options, "api_key"),
secret: ensureOption(options, "api_secret")
};
}
return execute_request(method, params, auth, api_url, callback, options);
}

Expand Down
11 changes: 9 additions & 2 deletions lib-es5/api_client/execute_request.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ function execute_request(method, params, auth, api_url, callback) {
handle_response = void 0; // declare to user later
var key = auth.key;
var secret = auth.secret;
var oauth_token = auth.oauth_token;
var content_type = 'application/x-www-form-urlencoded';

if (options.content_type === 'json') {
Expand All @@ -43,9 +44,15 @@ function execute_request(method, params, auth, api_url, callback) {
headers: {
'Content-Type': content_type,
'User-Agent': utils.getUserAgent()
},
auth: key + ":" + secret
}
});

if (oauth_token) {
request_options.headers.Authorization = `Bearer ${oauth_token}`;
} else {
request_options.auth = key + ":" + secret;
}

if (options.agent != null) {
request_options.agent = options.agent;
}
Expand Down
7 changes: 6 additions & 1 deletion lib-es5/uploader.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var https = isSecure ? require('https') : require('http');
var Cache = require('./cache');
var utils = require("./utils");
var UploadStream = require('./upload_stream');
var config = require("./config");

var build_upload_params = utils.build_upload_params,
extend = utils.extend,
Expand Down Expand Up @@ -558,7 +559,6 @@ function call_api(action, callback, options, get_params) {

return Buffer.from(encodeFieldPart(boundary, key, value), 'utf8');
});

var result = post(api_url, post_data, boundary, file, handle_response, options);
if (isObject(result)) {
return result;
Expand All @@ -572,6 +572,7 @@ function call_api(action, callback, options, get_params) {
function post(url, post_data, boundary, file, callback, options) {
var file_header = void 0;
var finish_buffer = Buffer.from("--" + boundary + "--", 'ascii');
var oauth_token = options.oauth_token || config().oauth_token;
if (file != null || options.stream) {
// eslint-disable-next-line no-nested-ternary
var filename = options.stream ? options.filename ? options.filename : "file" : basename(file);
Expand All @@ -588,6 +589,10 @@ function post(url, post_data, boundary, file, callback, options) {
if (options.x_unique_upload_id != null) {
headers['X-Unique-Upload-Id'] = options.x_unique_upload_id;
}
if (oauth_token != null) {
headers.Authorization = `Bearer ${oauth_token}`;
}

post_options = extend(post_options, {
method: 'POST',
headers: headers
Expand Down
3 changes: 3 additions & 0 deletions lib-es5/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1106,11 +1106,14 @@ function process_request_params(params, options) {
if (options.unsigned != null && options.unsigned) {
params = exports.clear_blank(params);
delete params.timestamp;
} else if (options.oauth_token || config().oauth_token) {
params = exports.clear_blank(options);
} else if (options.signature) {
params = exports.clear_blank(options);
} else {
params = exports.sign_request(params, options);
}

return params;
}

Expand Down
16 changes: 11 additions & 5 deletions lib/api_client/call_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ const { ensurePresenceOf } = utils;
function call_api(method, uri, params, callback, options) {
ensurePresenceOf({ method, uri });
const api_url = utils.base_api_url(uri, options);
const auth = {
key: ensureOption(options, "api_key"),
secret: ensureOption(options, "api_secret")
};

let auth = {};
if (options.oauth_token || config().oauth_token){
auth = {
oauth_token: ensureOption(options, "oauth_token")
};
} else {
auth = {
key: ensureOption(options, "api_key"),
secret: ensureOption(options, "api_secret")
};
}
return execute_request(method, params, auth, api_url, callback, options);
}

Expand Down
11 changes: 9 additions & 2 deletions lib/api_client/execute_request.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ function execute_request(method, params, auth, api_url, callback, options = {})
let query_params, handle_response; // declare to user later
let key = auth.key;
let secret = auth.secret;
let oauth_token = auth.oauth_token;
let content_type = 'application/x-www-form-urlencoded';

if (options.content_type === 'json') {
Expand All @@ -36,9 +37,15 @@ function execute_request(method, params, auth, api_url, callback, options = {})
headers: {
'Content-Type': content_type,
'User-Agent': utils.getUserAgent()
},
auth: key + ":" + secret
}
});

if (oauth_token) {
request_options.headers.Authorization = `Bearer ${oauth_token}`;
} else {
request_options.auth = key + ":" + secret
}

if (options.agent != null) {
request_options.agent = options.agent;
}
Expand Down
7 changes: 6 additions & 1 deletion lib/uploader.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const https = isSecure ? require('https') : require('http');
const Cache = require('./cache');
const utils = require("./utils");
const UploadStream = require('./upload_stream');
const config = require("./config");

const {
build_upload_params,
Expand Down Expand Up @@ -459,7 +460,6 @@ function call_api(action, callback, options, get_params) {
.map(
([key, value]) => Buffer.from(encodeFieldPart(boundary, key, value), 'utf8')
);

let result = post(api_url, post_data, boundary, file, handle_response, options);
if (isObject(result)) {
return result;
Expand All @@ -473,6 +473,7 @@ function call_api(action, callback, options, get_params) {
function post(url, post_data, boundary, file, callback, options) {
let file_header;
let finish_buffer = Buffer.from("--" + boundary + "--", 'ascii');
let oauth_token = options.oauth_token || config().oauth_token;
if ((file != null) || options.stream) {
// eslint-disable-next-line no-nested-ternary
let filename = options.stream ? options.filename ? options.filename : "file" : basename(file);
Expand All @@ -489,6 +490,10 @@ function post(url, post_data, boundary, file, callback, options) {
if (options.x_unique_upload_id != null) {
headers['X-Unique-Upload-Id'] = options.x_unique_upload_id;
}
if (oauth_token != null) {
headers.Authorization = `Bearer ${oauth_token}`;
}

post_options = extend(post_options, {
method: 'POST',
headers: headers
Expand Down
3 changes: 3 additions & 0 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1017,11 +1017,14 @@ function process_request_params(params, options) {
if ((options.unsigned != null) && options.unsigned) {
params = exports.clear_blank(params);
delete params.timestamp;
} else if (options.oauth_token || config().oauth_token) {
params = exports.clear_blank(options);
} else if (options.signature) {
params = exports.clear_blank(options);
} else {
params = exports.sign_request(params, options);
}

return params;
}

Expand Down
128 changes: 128 additions & 0 deletions test/integration/api/authorization/oAuth_authorization_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
const sinon = require('sinon');
const cloudinary = require("../../../../cloudinary");
const helper = require("../../../spechelper");
const describe = require('../../../testUtils/suite');
const testConstants = require('../../../testUtils/testConstants');
const { PUBLIC_IDS } = testConstants;
const { PUBLIC_ID } = PUBLIC_IDS;

describe("oauth_token", function(){
it("should send the oauth_token option to the server (admin_api)", function() {
return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => {
cloudinary.v2.api.resource(PUBLIC_ID, { oauth_token: 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4' });
return sinon.assert.calledWith(requestSpy,
sinon.match.has("headers",
sinon.match.has("Authorization", "Bearer MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4")
));
});
});

it("should send the oauth_token config to the server (admin_api)", function() {
return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => {
cloudinary.config({
api_key: undefined,
api_secret: undefined,
oauth_token: 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4'
});
cloudinary.v2.api.resource(PUBLIC_ID);
return sinon.assert.calledWith(requestSpy,
sinon.match.has("headers",
sinon.match.has("Authorization", "Bearer MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4")
));
});
});

it("should not fail when only providing api_key and secret (admin_api)", function() {
cloudinary.config({
api_key: "1234",
api_secret: "1234",
oauth_token: undefined
});
return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => {
cloudinary.v2.api.resource(PUBLIC_ID);
return sinon.assert.calledWith(requestSpy, sinon.match({ auth: "1234:1234" }));
});
});

it("should fail when missing all credentials (admin_api)", function() {
cloudinary.config({
api_key: undefined,
api_secret: undefined,
oauth_token: undefined
});
expect(() => {
cloudinary.v2.api.resource(PUBLIC_ID)
}).to.throwError(/Must supply api_key/);
});

it("oauth_token as option should take priority with secret and key (admin_api)", function() {
cloudinary.config({
api_key: '1234',
api_secret: '1234'
});
return cloudinary.v2.api.resource(PUBLIC_ID, {oauth_token: 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4'})
.then(
() => expect().fail()
).catch(({ error }) => expect(error.message).to.contain("Invalid token"));
});

it("should send the oauth_token option to the server (upload_api)", function() {
return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => {
cloudinary.v2.uploader.upload(PUBLIC_ID, { oauth_token: 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4' });
return sinon.assert.calledWith(requestSpy,
sinon.match.has("headers",
sinon.match.has("Authorization", "Bearer MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4")
));
});
});

it("should send the oauth_token config to the server (upload_api)", function() {
return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => {
cloudinary.config({
api_key: undefined,
api_secret: undefined,
oauth_token: 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4'
});
cloudinary.v2.uploader.upload(PUBLIC_ID);
return sinon.assert.calledWith(requestSpy,
sinon.match.has("headers",
sinon.match.has("Authorization", "Bearer MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4")
));
});
});

it("should not fail when only providing api_key and secret (upload_api)", function() {
cloudinary.config({
api_key: "1234",
api_secret: "1234",
oauth_token: undefined
});
return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => {
cloudinary.v2.uploader.upload(PUBLIC_ID)
return sinon.assert.calledWith(requestSpy, sinon.match({ auth: null }));
});
});

it("should fail when missing all credentials (upload_api)", function() {
cloudinary.config({
api_key: undefined,
api_secret: undefined,
oauth_token: undefined
});
expect(() => {
cloudinary.v2.uploader.upload(PUBLIC_ID)
}).to.throwError(/Must supply api_key/);
});

it("should not need credentials for unsigned upload", function() {
cloudinary.config({
api_key: undefined,
api_secret: undefined,
oauth_token: undefined
});
return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => {
cloudinary.v2.uploader.unsigned_upload(PUBLIC_ID, 'preset')
return sinon.assert.calledWith(requestSpy, sinon.match({ auth: null }));
});
});
});
3 changes: 3 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ declare module 'cloudinary' {
account_id?: string;
provisioning_api_key?: string;
provisioning_api_secret?: string;
oauth_token?: string;

[futureKey: string]: any;
}
Expand Down Expand Up @@ -387,6 +388,7 @@ declare module 'cloudinary' {
export interface AdminApiOptions {
agent?: object;
content_type?: string;
oauth_token?: string;

[futureKey: string]: any;
}
Expand Down Expand Up @@ -509,6 +511,7 @@ declare module 'cloudinary' {
use_filename?: boolean;
chunk_size?: number;
disable_promises?: boolean;
oauth_token?: string;

[futureKey: string]: any;
}
Expand Down

0 comments on commit 4e3b0f8

Please sign in to comment.