Skip to content

Commit 61edd35

Browse files
feat: access key management via provisioning api (#687)
* feat: access key management via provisioning api * feat: access key management via provisioning api * feat: access key management via provisioning api
1 parent 78c51b4 commit 61edd35

File tree

3 files changed

+240
-1
lines changed

3 files changed

+240
-1
lines changed

lib/provisioning/account.js

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,77 @@ function user_group_users(group_id, options = {}, callback) {
291291
return call_account_api('GET', uri, {}, callback, options);
292292
}
293293

294+
/**
295+
* @desc Lists access keys in the given subaccount.
296+
* @param sub_account_id {string} - The ID of the subaccount.
297+
* @param [options] {object} - See {@link https://cloudinary.com/documentation/provisioning_api#tag/access-keys/GET/sub_accounts/{{sub_account_id}}/access_keys|get access keys optional parameters} in the SDK documentation.
298+
* @param [callback] {function}
299+
*/
300+
function access_keys(sub_account_id, options = {}, callback) {
301+
const params = pickOnlyExistingValues({
302+
page_size: options.page_size,
303+
page: options.page,
304+
sort_by: options.sort_by,
305+
sort_order: options.sort_order
306+
}, 'page_size', 'page', 'sort_by', 'sort_order');
307+
const uri = ['sub_accounts', sub_account_id, 'access_keys'];
308+
return call_account_api('GET', uri, params, callback, options);
309+
}
310+
311+
/**
312+
* @desc Generate a new access key pair in the given subaccount.
313+
* @param sub_account_id {string} - The ID of the subaccount.
314+
* @param [options] {object} - See {@link https://cloudinary.com/documentation/provisioning_api#tag/access-keys/POST/sub_accounts/{{sub_account_id}}/access_keys|generate access key optional parameters} in the SDK documentation.
315+
* @param [callback] {function}
316+
*/
317+
function generate_access_key(sub_account_id, options = {}, callback) {
318+
const params = pickOnlyExistingValues({
319+
name: options.name,
320+
enabled: options.enabled
321+
}, 'name', 'enabled');
322+
options.content_type = "json";
323+
const uri = ['sub_accounts', sub_account_id, 'access_keys'];
324+
return call_account_api('POST', uri, params, callback, options);
325+
}
326+
327+
/**
328+
* @desc Update an existing access key pair in the given subaccount.
329+
* @param sub_account_id {string} - The ID of the subaccount.
330+
* @param [options] {object} - See {@link https://cloudinary.com/documentation/provisioning_api#tag/access-keys/PUT/sub_accounts/{sub_account_id}/access_keys/{key}|update access key optional parameters} in the SDK documentation.
331+
* @param [callback] {function}
332+
*/
333+
function update_access_key(sub_account_id, api_key, options = {}, callback) {
334+
const params = pickOnlyExistingValues({
335+
name: options.name,
336+
enabled: options.enabled
337+
}, 'name', 'enabled');
338+
options.content_type = "json";
339+
const uri = ['sub_accounts', sub_account_id, 'access_keys', api_key];
340+
return call_account_api('PUT', uri, params, callback, options);
341+
}
342+
343+
/**
344+
* @desc Delete an existing access key pair in the given subaccount.
345+
* @param sub_account_id {string} - The ID of the subaccount.
346+
* @param [options] {object} - See {@link https://cloudinary.com/documentation/provisioning_api#tag/access-keys/DELETE/sub_accounts/{sub_account_id}/access_keys|delete access key optional parameters} in the SDK documentation.
347+
* @param [callback] {function}
348+
*/
349+
function delete_access_key(sub_account_id, api_key, options = {}, callback) {
350+
const uri = ['sub_accounts', sub_account_id, 'access_keys', api_key];
351+
return call_account_api('DELETE', uri, {}, callback, options);
352+
}
353+
354+
/**
355+
* @desc Delete an existing access key pair in the given subaccount by its name.
356+
* @param sub_account_id {string} - The ID of the subaccount.
357+
* @param [options] {object} - See {@link https://cloudinary.com/documentation/provisioning_api#tag/access-keys/DELETE/sub_accounts/{sub_account_id}/access_keys|delete access key optional parameters} in the SDK documentation.
358+
* @param [callback] {function}
359+
*/
360+
function delete_access_key_by_name(sub_account_id, options = {}, callback) {
361+
const params = { name: options.name };
362+
const uri = ['sub_accounts', sub_account_id, 'access_keys'];
363+
return call_account_api('DELETE', uri, params, callback, options);
364+
}
294365

295366
module.exports = {
296367
sub_accounts,
@@ -310,5 +381,10 @@ module.exports = {
310381
create_user,
311382
create_user_group,
312383
add_user_to_group,
313-
delete_user_group
384+
delete_user_group,
385+
access_keys,
386+
generate_access_key,
387+
update_access_key,
388+
delete_access_key,
389+
delete_access_key_by_name
314390
};
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
const cloudinary = require("../../../../cloudinary");
2+
const TIMEOUT = require('../../../testUtils/testConstants').TIMEOUT;
3+
let runOnlyForInternalPRs = process.env.TRAVIS_SECURE_ENV_VARS ? describe : describe.skip;
4+
5+
6+
describe.skip('Provisioning API - Access Keys Management', function () {
7+
let CLOUD_SECRET;
8+
let CLOUD_API;
9+
let CLOUD_NAME;
10+
let CLOUD_ID;
11+
let CLOUD_NAME_PREFIX = `justaname${process.hrtime()[1] % 10000}`;
12+
this.timeout(TIMEOUT.LONG);
13+
14+
before("Setup the required test", async () => {
15+
let config = cloudinary.config(true);
16+
if (!(config.provisioning_api_key && config.provisioning_api_secret && config.account_id)) {
17+
// For external PRs the env variables are not availble, so we skip the provisioning API
18+
this.skip();
19+
}
20+
21+
let CLOUD_TO_CREATE = CLOUD_NAME_PREFIX + Date.now();
22+
// Create a sub account(sub cloud)
23+
let res = await cloudinary.provisioning.account.create_sub_account(CLOUD_TO_CREATE, CLOUD_TO_CREATE, {}, true).catch((err) => {
24+
throw err;
25+
});
26+
27+
CLOUD_API = res.api_access_keys[0].key;
28+
CLOUD_SECRET = res.api_access_keys[0].secret;
29+
CLOUD_NAME = res.cloud_name;
30+
CLOUD_ID = res.id;
31+
32+
return true;
33+
});
34+
35+
after('Destroy the sub_account and users that were created', async () => {
36+
// Skip 'after' in case we don't have account configuration available
37+
// This means that the beforeHook also didn't run
38+
let config = cloudinary.config(true);
39+
if (!(config.provisioning_api_key && config.provisioning_api_secret && config.account_id)) {
40+
return;
41+
}
42+
43+
const delRes = await cloudinary.provisioning.account.delete_sub_account(CLOUD_ID);
44+
expect(delRes.message).to.eql('ok');
45+
});
46+
47+
it('List access keys', async () => {
48+
const accessKeys = await cloudinary.provisioning.account.access_keys(CLOUD_ID);
49+
expect(Object.keys(accessKeys)).to.eql(['access_keys', 'total']);
50+
expect(accessKeys.access_keys.length).to.eql(1);
51+
expect(Object.keys(accessKeys.access_keys[0])).to.eql(['name', 'api_key', 'api_secret', 'created_at', 'updated_at', 'enabled']);
52+
});
53+
54+
it('Generate new access key', async () => {
55+
const keyName = `test-access-key-${Date.now()}`
56+
const newAccessKey = await cloudinary.provisioning.account.generate_access_key(CLOUD_ID, { name: keyName });
57+
expect(Object.keys(newAccessKey)).to.eql(['name', 'api_key', 'api_secret', 'created_at', 'updated_at', 'enabled']);
58+
expect(newAccessKey.name).to.eql(keyName);
59+
});
60+
61+
it('List access keys with optional query params', async () => {
62+
const keyName1 = `A-test-access-key-${Date.now()}`
63+
const newAccessKey1 = await cloudinary.provisioning.account.generate_access_key(CLOUD_ID, { name: keyName1 });
64+
expect(Object.keys(newAccessKey1)).to.eql(['name', 'api_key', 'api_secret', 'created_at', 'updated_at', 'enabled']);
65+
expect(newAccessKey1.name).to.eql(keyName1);
66+
67+
const keyName2 = `B-test-access-key-${Date.now()}`
68+
const newAccessKey2 = await cloudinary.provisioning.account.generate_access_key(CLOUD_ID, { name: keyName2 });
69+
expect(Object.keys(newAccessKey2)).to.eql(['name', 'api_key', 'api_secret', 'created_at', 'updated_at', 'enabled']);
70+
expect(newAccessKey2.name).to.eql(keyName2);
71+
72+
const keyName3 = `C-test-access-key-${Date.now()}`
73+
const newAccessKey3 = await cloudinary.provisioning.account.generate_access_key(CLOUD_ID, { name: keyName3 });
74+
expect(Object.keys(newAccessKey3)).to.eql(['name', 'api_key', 'api_secret', 'created_at', 'updated_at', 'enabled']);
75+
expect(newAccessKey3.name).to.eql(keyName3);
76+
77+
const pageSize = 2;
78+
const accessKeys = await cloudinary.provisioning.account.access_keys(CLOUD_ID, {
79+
page_size: pageSize,
80+
page: 1,
81+
sort_by: 'name',
82+
sort_order: 'desc'
83+
});
84+
expect(Object.keys(accessKeys)).to.eql(['access_keys', 'total']);
85+
expect(accessKeys.access_keys.length).to.eql(pageSize);
86+
expect(Object.keys(accessKeys.access_keys[0])).to.eql(['name', 'api_key', 'api_secret', 'created_at', 'updated_at', 'enabled']);
87+
});
88+
89+
it('Update access key', async () => {
90+
const keyName = `test-access-key-${Date.now()}`
91+
const newAccessKey = await cloudinary.provisioning.account.generate_access_key(CLOUD_ID, { name: keyName });
92+
expect(Object.keys(newAccessKey)).to.eql(['name', 'api_key', 'api_secret', 'created_at', 'updated_at', 'enabled']);
93+
expect(newAccessKey.name).to.eql(keyName);
94+
95+
const newName = `${keyName}-updated`;
96+
const updatedAccessKey = await cloudinary.provisioning.account.update_access_key(CLOUD_ID, newAccessKey.api_key, { name: newName });
97+
expect(Object.keys(newAccessKey)).to.eql(['name', 'api_key', 'api_secret', 'created_at', 'updated_at', 'enabled']);
98+
expect(updatedAccessKey.name).to.eql(newName);
99+
});
100+
101+
it('Delete access keys', async () => {
102+
const keyName = `test-access-key-${Date.now()}`
103+
const newAccessKey = await cloudinary.provisioning.account.generate_access_key(CLOUD_ID, { name: keyName });
104+
expect(Object.keys(newAccessKey)).to.eql(['name', 'api_key', 'api_secret', 'created_at', 'updated_at', 'enabled']);
105+
expect(newAccessKey.name).to.eql(keyName);
106+
107+
const deleteAccessKey = await cloudinary.provisioning.account.delete_access_key(CLOUD_ID, newAccessKey.api_key);
108+
expect(Object.keys(deleteAccessKey)).to.eql(['message']);
109+
expect(deleteAccessKey.message).to.eql('ok');
110+
});
111+
112+
it('Delete access keys by name', async () => {
113+
const keyName = `test-access-key-${Date.now()}`
114+
const newAccessKey = await cloudinary.provisioning.account.generate_access_key(CLOUD_ID, { name: keyName });
115+
expect(Object.keys(newAccessKey)).to.eql(['name', 'api_key', 'api_secret', 'created_at', 'updated_at', 'enabled']);
116+
expect(newAccessKey.name).to.eql(keyName);
117+
118+
const deleteAccessKey = await cloudinary.provisioning.account.delete_access_key_by_name(CLOUD_ID, { name: keyName });
119+
expect(Object.keys(deleteAccessKey)).to.eql(['message']);
120+
expect(deleteAccessKey.message).to.eql('ok');
121+
});
122+
});

types/index.d.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,24 @@ declare module 'cloudinary' {
557557
[futureKey: string]: any;
558558
}
559559

560+
export interface AccessKeyDetails {
561+
name: string,
562+
api_key: string,
563+
api_secret: string,
564+
created_at: string,
565+
updated_at: string,
566+
enabled: boolean
567+
}
568+
569+
export interface AccessKeysListResponse {
570+
access_keys: Array<AccessKeyDetails>,
571+
total: number
572+
}
573+
574+
export interface DeleteAccessKeyResponse {
575+
message: 'ok' | 'not_found'
576+
}
577+
560578
export interface AuthTokenApiOptions {
561579
key: string;
562580
acl: string;
@@ -1485,6 +1503,29 @@ declare module 'cloudinary' {
14851503
function user_groups(options?: ProvisioningApiOptions, callback?: ResponseCallback): Promise<any>;
14861504

14871505
function user_group_users(groupId: string, options?: ProvisioningApiOptions, callback?: ResponseCallback): Promise<any>;
1506+
1507+
function access_keys(subAccountId: string, options?: ProvisioningApiOptions | {
1508+
page_size?: number,
1509+
page?: number,
1510+
sort_by?: string,
1511+
sort_order?: 'desc' | 'asc'
1512+
}, callback?: ResponseCallback): Promise<AccessKeysListResponse>;
1513+
1514+
function generate_access_key(subAccountId: string, options?: ProvisioningApiOptions | {
1515+
name?: string,
1516+
enabled?: boolean
1517+
}, callback?: ResponseCallback): Promise<AccessKeyDetails>;
1518+
1519+
function update_access_key(subAccountId: string, apiKey: string, options?: ProvisioningApiOptions | {
1520+
name?: string,
1521+
enabled?: boolean
1522+
}, callback?: ResponseCallback): Promise<AccessKeyDetails>;
1523+
1524+
function delete_access_key(subAccountId: string, apiKey: string, options?: ProvisioningApiOptions, callback?: ResponseCallback): Promise<DeleteAccessKeyResponse>;
1525+
1526+
function delete_access_key_by_name(subAccountId: string, options: ProvisioningApiOptions | {
1527+
name: string,
1528+
}, callback?: ResponseCallback): Promise<DeleteAccessKeyResponse>;
14881529
}
14891530
}
14901531

0 commit comments

Comments
 (0)