@adobe/acc-js-sdk 1.1.25 → 1.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/changeLog.html +13 -1
- package/docs/connecting.html +62 -4
- package/docs/samples.html +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/client.js +94 -30
- package/src/domUtil.js +8 -10
- package/src/soap.js +12 -4
- package/src/util.js +1 -12
- package/test/domUtil.test.js +14 -0
- package/test/imBearerToken.test.js +174 -0
package/docs/changeLog.html
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
layout: page
|
|
3
3
|
title: Change Log
|
|
4
4
|
---
|
|
5
|
+
|
|
6
|
+
<section class="changelog"><h1>Version 1.1.26</h1>
|
|
7
|
+
<h2>2023/04/17</h2>
|
|
8
|
+
|
|
9
|
+
<li>
|
|
10
|
+
Added support for IMS Bearer tokens without require any session and security tokens. See <a href="https://opensource.adobe.com/acc-js-sdk/connecting.html"> for more details.</a>
|
|
11
|
+
</li>
|
|
12
|
+
<li>
|
|
13
|
+
Fixed a bug causing incorrect JSON to XML transformation when the JSON object has a property named 'length'
|
|
14
|
+
</li>
|
|
15
|
+
</section>
|
|
16
|
+
|
|
5
17
|
<section class="changelog"><h1>Version 1.1.25</h1>
|
|
6
18
|
<h2>2023/03/07</h2>
|
|
7
19
|
|
|
@@ -15,7 +27,7 @@ title: Change Log
|
|
|
15
27
|
</section>
|
|
16
28
|
|
|
17
29
|
<section class="changelog"><h1>Version 1.1.24</h1>
|
|
18
|
-
<h2>2023/
|
|
30
|
+
<h2>2023/04/07</h2>
|
|
19
31
|
|
|
20
32
|
<li>
|
|
21
33
|
Added support for abortable requests. See <a href="https://opensource.adobe.com/acc-js-sdk/abortRequest.html"> for more details.</a>
|
package/docs/connecting.html
CHANGED
|
@@ -51,19 +51,58 @@ await client.logoff();
|
|
|
51
51
|
<li>Informations about the connected user, and it's privileges</li>
|
|
52
52
|
</ul>
|
|
53
53
|
|
|
54
|
-
<p>
|
|
55
|
-
he most convenient way to access this information is by using the <a href="{{ site.baseurl }}/application.html">Application object</a>, but you can
|
|
54
|
+
<p>The most convenient way to access this information is by using the <a href="{{ site.baseurl }}/application.html">Application object</a>, but you can
|
|
56
55
|
also use the <b>client.getSessionInfo().serverInfo</b> and <b>client.getSessionInfo().sessionInfo</b> calls.
|
|
57
56
|
</p>
|
|
58
57
|
|
|
58
|
+
<p class="info">Note: depending on the type of credentials used, user and server info may not be returned.
|
|
59
|
+
</p>
|
|
59
60
|
|
|
61
|
+
<table>
|
|
62
|
+
<thead>
|
|
63
|
+
<tr>
|
|
64
|
+
<th>Credentials Type</th>
|
|
65
|
+
<th>Returns info</th>
|
|
66
|
+
</tr>
|
|
67
|
+
</thead>
|
|
68
|
+
<tbody>
|
|
69
|
+
<tr>
|
|
70
|
+
<td>UserPassword</td>
|
|
71
|
+
<td>Yes</td>
|
|
72
|
+
</tr>
|
|
73
|
+
<tr>
|
|
74
|
+
<td>ImsServiceToken</td>
|
|
75
|
+
<td>Yes</td>
|
|
76
|
+
</tr>
|
|
77
|
+
<tr>
|
|
78
|
+
<td>SessionToken</td>
|
|
79
|
+
<td>No</td>
|
|
80
|
+
</tr>
|
|
81
|
+
<tr>
|
|
82
|
+
<td>AnonymousUser</td>
|
|
83
|
+
<td>No</td>
|
|
84
|
+
</tr>
|
|
85
|
+
<tr>
|
|
86
|
+
<td>SecurityToken</td>
|
|
87
|
+
<td>Yes</td>
|
|
88
|
+
</tr>
|
|
89
|
+
<tr>
|
|
90
|
+
<td>BearerToken</td>
|
|
91
|
+
<td>Yes</td>
|
|
92
|
+
</tr>
|
|
93
|
+
<tr>
|
|
94
|
+
<td>ImsBearerToken</td>
|
|
95
|
+
<td>No</td>
|
|
96
|
+
</tr>
|
|
97
|
+
</tbody>
|
|
98
|
+
</table>
|
|
60
99
|
|
|
61
100
|
<h1>Credentials</h1>
|
|
62
101
|
|
|
63
102
|
<p>
|
|
64
103
|
There are several methods of the <b>sdk.ConnectionParameters</b> depending on the type of authentication you want to use. They are
|
|
65
104
|
described below.
|
|
66
|
-
</p
|
|
105
|
+
</p>
|
|
67
106
|
|
|
68
107
|
<h2>Login with user and password</h2>
|
|
69
108
|
<p>This is the most common method to log in to Campaign. User the <b>ofUserAndPassword</b> function and pass it the user name and password</p>
|
|
@@ -75,7 +114,26 @@ const connectionParameters = sdk.ConnectionParameters.ofUserAndPassword(
|
|
|
75
114
|
|
|
76
115
|
|
|
77
116
|
<h2>Login with IMS access token</h2>
|
|
78
|
-
|
|
117
|
+
|
|
118
|
+
<p>In Campaign 8.5 and above, native support for IMS bearer tokens is avaiable and can be used in the SDK as the preferred connection method.</p>
|
|
119
|
+
<pre class="code">
|
|
120
|
+
const connectionParameters = sdk.ConnectionParameters.ofImsBearerToken(
|
|
121
|
+
"https://myInstance.campaign.adobe.com",
|
|
122
|
+
"ims_bearer_token");
|
|
123
|
+
</pre>
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
<p class="info">Note: You still need to call Logon
|
|
127
|
+
</p>
|
|
128
|
+
|
|
129
|
+
<p>With this authentication method, an IMS Bearer token is obtained outside of Campaign, for example using IMS APIs and is passed directly to
|
|
130
|
+
all Campaign APIs. Campaigns itslef will verify the token and grant the corresponding access.
|
|
131
|
+
</p>
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
<p>For older versions of Campaign, you can still use IMS tokens with the <b>ofBearerToken</b> function. The difference between ofImsBearerToken and ofBearerToken
|
|
135
|
+
is that ofBearerToken will internally call "xtk:session#BearerTokenLogon" to exchange the IMS token for Campaign session and security tokens. Subsequent
|
|
136
|
+
API calls will use Campaign tokens. It's recommended to us <b>ofImsBearerToken</b> logon whenever possible.</p>
|
|
79
137
|
<pre class="code">
|
|
80
138
|
const connectionParameters = sdk.ConnectionParameters.ofBearerToken(
|
|
81
139
|
"https://myInstance.campaign.adobe.com",
|
package/docs/samples.html
CHANGED
|
@@ -3,7 +3,7 @@ layout: page
|
|
|
3
3
|
title: Sample Code
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<p>The <b>samples</b> folder contains several samples illustrating how to use the various
|
|
6
|
+
<p>The <b>samples</b> folder contains several samples illustrating how to use the various Campaign APIs.</p>
|
|
7
7
|
|
|
8
8
|
<p>A sample file looks like this</p>
|
|
9
9
|
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/acc-js-sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.26",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@adobe/acc-js-sdk",
|
|
9
|
-
"version": "1.1.
|
|
9
|
+
"version": "1.1.26",
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"axios": "^1.2.1",
|
package/package.json
CHANGED
package/src/client.js
CHANGED
|
@@ -11,7 +11,7 @@ governing permissions and limitations under the License.
|
|
|
11
11
|
*/
|
|
12
12
|
(function() {
|
|
13
13
|
"use strict";
|
|
14
|
-
|
|
14
|
+
/*jshint sub:true*/
|
|
15
15
|
|
|
16
16
|
/**********************************************************************************
|
|
17
17
|
*
|
|
@@ -244,12 +244,12 @@ class Credentials {
|
|
|
244
244
|
*/
|
|
245
245
|
constructor(type, sessionToken, securityToken) {
|
|
246
246
|
if (type != "UserPassword" && type != "ImsServiceToken" && type != "SessionToken" &&
|
|
247
|
-
type != "AnonymousUser" && type != "SecurityToken" && type != "BearerToken")
|
|
247
|
+
type != "AnonymousUser" && type != "SecurityToken" && type != "BearerToken" && type != "ImsBearerToken")
|
|
248
248
|
throw CampaignException.INVALID_CREDENTIALS_TYPE(type);
|
|
249
249
|
this._type = type;
|
|
250
250
|
this._sessionToken = sessionToken || "";
|
|
251
251
|
this._securityToken = securityToken || "";
|
|
252
|
-
if (type == "BearerToken") {
|
|
252
|
+
if (type == "BearerToken" || type === "ImsBearerToken") {
|
|
253
253
|
this._bearerToken = sessionToken || "";
|
|
254
254
|
this._sessionToken = "";
|
|
255
255
|
}
|
|
@@ -397,8 +397,13 @@ class ConnectionParameters {
|
|
|
397
397
|
}
|
|
398
398
|
|
|
399
399
|
/**
|
|
400
|
-
* Creates connection parameters for a Campaign instance from bearer token
|
|
401
|
-
*
|
|
400
|
+
* Creates connection parameters for a Campaign instance from bearer token.
|
|
401
|
+
* This authentication method uses an IMS Bearer token and calls the xtk:session#BearerTokenLogin method
|
|
402
|
+
* which will exchange the IMS bearer token with Campaign session and security tokens.
|
|
403
|
+
* This is a legacy method. Campaign 8.5 will has an authentication method which allows to directly
|
|
404
|
+
* use the IMS bearer token and which is recommended.
|
|
405
|
+
* To avoid an extra call to "BearerTokenLogin", use the "ofImsBearerToken" method.
|
|
406
|
+
*
|
|
402
407
|
* @param {string} endpoint The campaign endpoint (URL)
|
|
403
408
|
* @param {string} bearerToken IMS bearer token
|
|
404
409
|
* @param {*} options connection options
|
|
@@ -408,6 +413,23 @@ class ConnectionParameters {
|
|
|
408
413
|
const credentials = new Credentials("BearerToken", bearerToken);
|
|
409
414
|
return new ConnectionParameters(endpoint, credentials, options);
|
|
410
415
|
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Creates connection parameters for a Campaign instance from a IMS bearer token.
|
|
419
|
+
* This authentication method does not require exchange the IMS token with session and security tokens
|
|
420
|
+
* and only works in ACC 8.5 and above.
|
|
421
|
+
* For older version of ACC or to use session/seurity tokens, use the "ofBearerToken" method.
|
|
422
|
+
*
|
|
423
|
+
* @param {string} endpoint The campaign endpoint (URL)
|
|
424
|
+
* @param {string} bearerToken IMS bearer token
|
|
425
|
+
* @param {*} options connection options
|
|
426
|
+
* @returns {ConnectionParameters} a ConnectionParameters object which can be used to create a Client
|
|
427
|
+
*/
|
|
428
|
+
static ofImsBearerToken(endpoint, bearerToken, options) {
|
|
429
|
+
const credentials = new Credentials("ImsBearerToken", bearerToken);
|
|
430
|
+
return new ConnectionParameters(endpoint, credentials, options);
|
|
431
|
+
}
|
|
432
|
+
|
|
411
433
|
/**
|
|
412
434
|
* Creates connection parameters for a Campaign instance, using an IMS service token and a user name (the user to impersonate)
|
|
413
435
|
*
|
|
@@ -557,15 +579,13 @@ const fileUploader = (client) => {
|
|
|
557
579
|
}
|
|
558
580
|
const data = new FormData();
|
|
559
581
|
data.append('file_noMd5', file);
|
|
582
|
+
const headers = client._getAuthHeaders(false);
|
|
560
583
|
client._makeHttpCall({
|
|
561
584
|
url: `${client._connectionParameters._endpoint}/nl/jsp/uploadFile.jsp`,
|
|
562
585
|
processData: false,
|
|
563
586
|
method: 'POST',
|
|
564
587
|
data: data,
|
|
565
|
-
headers:
|
|
566
|
-
'X-Security-Token': client._securityToken,
|
|
567
|
-
'X-Session-Token': client._sessionToken,
|
|
568
|
-
}
|
|
588
|
+
headers: headers
|
|
569
589
|
}).then((okay) => {
|
|
570
590
|
if (!okay.startsWith('Ok')) {
|
|
571
591
|
throw okay;
|
|
@@ -645,13 +665,28 @@ class Client {
|
|
|
645
665
|
*/
|
|
646
666
|
constructor(sdk, connectionParameters) {
|
|
647
667
|
this.sdk = sdk;
|
|
668
|
+
this.reinit(connectionParameters);
|
|
669
|
+
|
|
670
|
+
this._observers = [];
|
|
671
|
+
this._cacheChangeListeners = [];
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Re-initialize a client with new connection parameters.
|
|
676
|
+
* Typically called from the refreshClient callback after a connection expires.
|
|
677
|
+
* Conserves observers
|
|
678
|
+
*
|
|
679
|
+
* @param {Campaign.ConnectionParameters} user user name, for instance admin
|
|
680
|
+
*/
|
|
681
|
+
reinit(connectionParameters) {
|
|
648
682
|
this._connectionParameters = connectionParameters; // ## TODO security concern (password kept in memory)
|
|
649
683
|
this._representation = connectionParameters._options.representation;
|
|
650
684
|
|
|
651
685
|
this._sessionInfo = undefined;
|
|
652
686
|
this._sessionToken = undefined;
|
|
653
687
|
this._securityToken = undefined;
|
|
654
|
-
this.
|
|
688
|
+
this._bearerToken = undefined; // set when using Bearer authentication and "ImsBearer" credential type
|
|
689
|
+
this._installedPackages = {}; // package set (key and value = package id, ex: "nms:amp")
|
|
655
690
|
|
|
656
691
|
this._secretKeyCipher = undefined;
|
|
657
692
|
|
|
@@ -667,7 +702,7 @@ class Client {
|
|
|
667
702
|
const rootKeyType = connectionParameters._options.cacheRootKey;
|
|
668
703
|
let rootKey = "";
|
|
669
704
|
if (!rootKeyType || rootKeyType === "default")
|
|
670
|
-
rootKey = `acc.js.sdk.${sdk.getSDKVersion().version}.${instanceKey}.cache.`;
|
|
705
|
+
rootKey = `acc.js.sdk.${this.sdk.getSDKVersion().version}.${instanceKey}.cache.`;
|
|
671
706
|
|
|
672
707
|
// Clear storage cache if the sdk versions or the instances are different
|
|
673
708
|
if (this._storage && typeof this._storage.removeItem === 'function') {
|
|
@@ -687,8 +722,6 @@ class Client {
|
|
|
687
722
|
|
|
688
723
|
this._transport = connectionParameters._options.transport;
|
|
689
724
|
this._traceAPICalls = connectionParameters._options.traceAPICalls;
|
|
690
|
-
this._observers = [];
|
|
691
|
-
this._cacheChangeListeners = [];
|
|
692
725
|
this._refreshClient = connectionParameters._options.refreshClient;
|
|
693
726
|
|
|
694
727
|
// expose utilities
|
|
@@ -749,6 +782,26 @@ class Client {
|
|
|
749
782
|
return `${version.name}/${version.version} ${version.description}`;
|
|
750
783
|
}
|
|
751
784
|
|
|
785
|
+
/**
|
|
786
|
+
* Get HTTP authentication headers
|
|
787
|
+
* @param
|
|
788
|
+
* @returns {Object} the headers
|
|
789
|
+
*/
|
|
790
|
+
_getAuthHeaders(setCookie) {
|
|
791
|
+
const headers = {};
|
|
792
|
+
if (this._bearerToken) {
|
|
793
|
+
headers['Authorization'] = `Bearer ${this._bearerToken}`;
|
|
794
|
+
}
|
|
795
|
+
else {
|
|
796
|
+
headers['X-Security-Token'] = this._securityToken;
|
|
797
|
+
headers['X-Session-Token'] = this._sessionToken;
|
|
798
|
+
if (setCookie) {
|
|
799
|
+
headers['Cookie'] = '__sessiontoken=' + this._sessionToken;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return headers;
|
|
803
|
+
}
|
|
804
|
+
|
|
752
805
|
/**
|
|
753
806
|
* Convert an XML object into a representation
|
|
754
807
|
*
|
|
@@ -929,6 +982,9 @@ class Client {
|
|
|
929
982
|
const credentialsType = this._connectionParameters._credentials._type;
|
|
930
983
|
if (credentialsType == "AnonymousUser")
|
|
931
984
|
return true;
|
|
985
|
+
else if (credentialsType == "ImsBearerToken") {
|
|
986
|
+
return !!this._bearerToken;
|
|
987
|
+
}
|
|
932
988
|
|
|
933
989
|
// When using bearer token authentication we are considered logged only after
|
|
934
990
|
// the bearer token has been converted into session token and security token
|
|
@@ -967,7 +1023,8 @@ class Client {
|
|
|
967
1023
|
this._sessionToken, this._securityToken,
|
|
968
1024
|
this._getUserAgentString(),
|
|
969
1025
|
Object.assign({}, this._connectionParameters._options, pushDownOptions),
|
|
970
|
-
extraHttpHeaders
|
|
1026
|
+
extraHttpHeaders,
|
|
1027
|
+
this._bearerToken);
|
|
971
1028
|
soapCall.internal = !!internal;
|
|
972
1029
|
soapCall.isStatic = isStatic;
|
|
973
1030
|
return soapCall;
|
|
@@ -1324,6 +1381,7 @@ class Client {
|
|
|
1324
1381
|
this.application = null;
|
|
1325
1382
|
this._sessionToken = "";
|
|
1326
1383
|
this._securityToken = "";
|
|
1384
|
+
this._bearerToken = undefined;
|
|
1327
1385
|
const credentials = this._connectionParameters._credentials;
|
|
1328
1386
|
|
|
1329
1387
|
const sdkVersion = this.sdk.getSDKVersion();
|
|
@@ -1349,6 +1407,7 @@ class Client {
|
|
|
1349
1407
|
that._installedPackages = {};
|
|
1350
1408
|
that._sessionToken = credentials._sessionToken;
|
|
1351
1409
|
that._securityToken = "";
|
|
1410
|
+
that._bearerToken = undefined;
|
|
1352
1411
|
that._onLogon();
|
|
1353
1412
|
return Promise.resolve();
|
|
1354
1413
|
}
|
|
@@ -1357,6 +1416,16 @@ class Client {
|
|
|
1357
1416
|
that._installedPackages = {};
|
|
1358
1417
|
that._sessionToken = "";
|
|
1359
1418
|
that._securityToken = credentials._securityToken;
|
|
1419
|
+
that._bearerToken = undefined;
|
|
1420
|
+
that._onLogon();
|
|
1421
|
+
return Promise.resolve();
|
|
1422
|
+
}
|
|
1423
|
+
else if (credentials._type == "ImsBearerToken") {
|
|
1424
|
+
that._sessionInfo = undefined;
|
|
1425
|
+
that._installedPackages = {};
|
|
1426
|
+
that._sessionToken = "";
|
|
1427
|
+
that._securityToken = "";
|
|
1428
|
+
that._bearerToken = credentials._bearerToken;
|
|
1360
1429
|
that._onLogon();
|
|
1361
1430
|
return Promise.resolve();
|
|
1362
1431
|
}
|
|
@@ -1407,6 +1476,7 @@ class Client {
|
|
|
1407
1476
|
// store member variables after all parameters are decode the ensure atomicity
|
|
1408
1477
|
that._sessionToken = sessionToken;
|
|
1409
1478
|
that._securityToken = securityToken;
|
|
1479
|
+
that._bearerToken = undefined;
|
|
1410
1480
|
|
|
1411
1481
|
that._onLogon();
|
|
1412
1482
|
});
|
|
@@ -1444,6 +1514,7 @@ class Client {
|
|
|
1444
1514
|
return this._makeSoapCall(soapCall).then(function() {
|
|
1445
1515
|
that._sessionToken = "";
|
|
1446
1516
|
that._securityToken = "";
|
|
1517
|
+
that._bearerToken = undefined;
|
|
1447
1518
|
that.application = null;
|
|
1448
1519
|
soapCall.checkNoMoreArgs();
|
|
1449
1520
|
});
|
|
@@ -1451,6 +1522,7 @@ class Client {
|
|
|
1451
1522
|
else {
|
|
1452
1523
|
that._sessionToken = "";
|
|
1453
1524
|
that._securityToken = "";
|
|
1525
|
+
that._bearerToken = undefined;
|
|
1454
1526
|
that.application = null;
|
|
1455
1527
|
}
|
|
1456
1528
|
} finally {
|
|
@@ -1914,13 +1986,10 @@ class Client {
|
|
|
1914
1986
|
* @returns {Campaign.PingStatus} an object describing the server status
|
|
1915
1987
|
*/
|
|
1916
1988
|
async ping() {
|
|
1989
|
+
const headers = this._getAuthHeaders(true);
|
|
1917
1990
|
const request = {
|
|
1918
1991
|
url: `${this._connectionParameters._endpoint}/nl/jsp/ping.jsp`,
|
|
1919
|
-
headers:
|
|
1920
|
-
'X-Security-Token': this._securityToken,
|
|
1921
|
-
'X-Session-Token': this._sessionToken,
|
|
1922
|
-
'Cookie': '__sessiontoken=' + this._sessionToken
|
|
1923
|
-
}
|
|
1992
|
+
headers: headers,
|
|
1924
1993
|
};
|
|
1925
1994
|
for (let h in this._connectionParameters._options.extraHttpHeaders)
|
|
1926
1995
|
request.headers[h] = this._connectionParameters._options.extraHttpHeaders[h];
|
|
@@ -1953,14 +2022,12 @@ class Client {
|
|
|
1953
2022
|
callContext.formData.ctx = DomUtil.toXMLString(xmlCtx);
|
|
1954
2023
|
}
|
|
1955
2024
|
const selectionCount = callContext.selection.split(',').length;
|
|
1956
|
-
|
|
2025
|
+
|
|
2026
|
+
const headers = this._getAuthHeaders(false);
|
|
2027
|
+
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
1957
2028
|
const request = {
|
|
1958
2029
|
url: `${this._connectionParameters._endpoint}/report/${callContext.reportName}?${encodeURI(`_noRender=true&_schema=${callContext.schema}&_context=${callContext.context}&_selection=${callContext.selection}`)}&_selectionCount=${selectionCount}`,
|
|
1959
|
-
headers:
|
|
1960
|
-
'X-Security-Token': this._securityToken,
|
|
1961
|
-
'X-Session-Token': this._sessionToken,
|
|
1962
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
1963
|
-
},
|
|
2030
|
+
headers: headers,
|
|
1964
2031
|
method: 'POST',
|
|
1965
2032
|
data : qsStringify(callContext.formData)
|
|
1966
2033
|
};
|
|
@@ -1985,13 +2052,10 @@ class Client {
|
|
|
1985
2052
|
* @returns {Campaign.McPingStatus} an object describing Message Center server status
|
|
1986
2053
|
*/
|
|
1987
2054
|
async mcPing() {
|
|
2055
|
+
const headers = this._getAuthHeaders(true);
|
|
1988
2056
|
const request = {
|
|
1989
2057
|
url: `${this._connectionParameters._endpoint}/nl/jsp/mcPing.jsp`,
|
|
1990
|
-
headers:
|
|
1991
|
-
'X-Security-Token': this._securityToken,
|
|
1992
|
-
'X-Session-Token': this._sessionToken,
|
|
1993
|
-
'Cookie': '__sessiontoken=' + this._sessionToken
|
|
1994
|
-
}
|
|
2058
|
+
headers: headers
|
|
1995
2059
|
};
|
|
1996
2060
|
for (let h in this._connectionParameters._options.extraHttpHeaders)
|
|
1997
2061
|
request.headers[h] = this._connectionParameters._options.extraHttpHeaders[h];
|
package/src/domUtil.js
CHANGED
|
@@ -341,20 +341,18 @@ class DomUtil {
|
|
|
341
341
|
xmlElement.textContent = value;
|
|
342
342
|
xmlRoot.appendChild(xmlElement);
|
|
343
343
|
}
|
|
344
|
-
else if (
|
|
345
|
-
|
|
346
|
-
for (var i=0; i<value.length; i++) {
|
|
347
|
-
const xmlElement = doc.createElement(att);
|
|
348
|
-
this._fromJSON(doc, xmlElement, value[i], flavor);
|
|
349
|
-
xmlRoot.appendChild(xmlElement);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
344
|
+
else if (Util.isArray(value)) {
|
|
345
|
+
for (var i=0; i<value.length; i++) {
|
|
353
346
|
const xmlElement = doc.createElement(att);
|
|
354
|
-
this._fromJSON(doc, xmlElement, value, flavor);
|
|
347
|
+
this._fromJSON(doc, xmlElement, value[i], flavor);
|
|
355
348
|
xmlRoot.appendChild(xmlElement);
|
|
356
349
|
}
|
|
357
350
|
}
|
|
351
|
+
else if (t == "object") {
|
|
352
|
+
const xmlElement = doc.createElement(att);
|
|
353
|
+
this._fromJSON(doc, xmlElement, value, flavor);
|
|
354
|
+
xmlRoot.appendChild(xmlElement);
|
|
355
|
+
}
|
|
358
356
|
else
|
|
359
357
|
throw new DomException(`Cannot cast JSON to XML: element '${att}' type '${t}' is unknown or not supported yet`);
|
|
360
358
|
}
|
package/src/soap.js
CHANGED
|
@@ -11,7 +11,7 @@ governing permissions and limitations under the License.
|
|
|
11
11
|
*/
|
|
12
12
|
(function() {
|
|
13
13
|
"use strict";
|
|
14
|
-
|
|
14
|
+
/*jshint sub:true*/
|
|
15
15
|
|
|
16
16
|
/**********************************************************************************
|
|
17
17
|
*
|
|
@@ -80,11 +80,12 @@ const NS_XSD = "http://www.w3.org/2001/XMLSchema";
|
|
|
80
80
|
* @param {string} userAgentString The user agent string to use for HTTP requests
|
|
81
81
|
* @param {string} pushDownOptions Options to push down to the request (comes from connectionParameters._options)
|
|
82
82
|
* @param {{ name:string, value:string}} extraHttpHeaders key/value pair of HTTP header (will override any other headers)
|
|
83
|
+
* @param {string} bearerToken The bearer token to use for HTTP requests. Only required for ImsBearerToken authentication
|
|
83
84
|
* @memberof SOAP
|
|
84
85
|
*/
|
|
85
86
|
class SoapMethodCall {
|
|
86
87
|
|
|
87
|
-
constructor(transport, urn, methodName, sessionToken, securityToken, userAgentString, pushDownOptions, extraHttpHeaders) {
|
|
88
|
+
constructor(transport, urn, methodName, sessionToken, securityToken, userAgentString, pushDownOptions, extraHttpHeaders, bearerToken) {
|
|
88
89
|
this.request = undefined; // The HTTP request (object literal passed to the transport layer)
|
|
89
90
|
this.requestOptions = undefined;
|
|
90
91
|
this.response = undefined; // The HTTP response object (in case of success)
|
|
@@ -103,6 +104,7 @@ class SoapMethodCall {
|
|
|
103
104
|
|
|
104
105
|
this._sessionToken = sessionToken || "";
|
|
105
106
|
this._securityToken = securityToken || "";
|
|
107
|
+
this._bearerToken = bearerToken; // may be undefined if not using bearer token authentication
|
|
106
108
|
this._userAgentString = userAgentString;
|
|
107
109
|
this._pushDownOptions = pushDownOptions || {};
|
|
108
110
|
this._charset = this._pushDownOptions.charset || '';
|
|
@@ -538,9 +540,14 @@ class SoapMethodCall {
|
|
|
538
540
|
const headers = {
|
|
539
541
|
'Content-type': `application/soap+xml${this._charset ? ";charset=" + this._charset : ""}`,
|
|
540
542
|
'SoapAction': `${this.urn}#${this.methodName}`,
|
|
541
|
-
'X-Security-Token': this._securityToken,
|
|
542
|
-
'X-Session-Token': this._sessionToken,
|
|
543
543
|
};
|
|
544
|
+
if (this._bearerToken) {
|
|
545
|
+
headers['Authorization'] = `Bearer ${this._bearerToken}`;
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
headers['X-Security-Token'] = this._securityToken;
|
|
549
|
+
headers['X-Session-Token'] = this._sessionToken;
|
|
550
|
+
}
|
|
544
551
|
|
|
545
552
|
// Add HTTP headers specific to the SOAP call for better tracing/troubleshooting
|
|
546
553
|
if (this._extraHttpHeaders && this._extraHttpHeaders['ACC-SDK-Version']) {
|
|
@@ -579,6 +586,7 @@ class SoapMethodCall {
|
|
|
579
586
|
if (client) {
|
|
580
587
|
this._sessionToken = client._sessionToken;
|
|
581
588
|
this._securityToken = client._securityToken;
|
|
589
|
+
this._bearerToken = client._bearerToken;
|
|
582
590
|
}
|
|
583
591
|
|
|
584
592
|
var cookieHeader = DomUtil.findElement(this._header, "Cookie");
|
package/src/util.js
CHANGED
|
@@ -51,18 +51,7 @@ class Util {
|
|
|
51
51
|
* @returns {boolean} true if the object is an array
|
|
52
52
|
*/
|
|
53
53
|
static isArray(obj) {
|
|
54
|
-
|
|
55
|
-
// JavaScript arrays are objects
|
|
56
|
-
if (typeof obj != "object") return false;
|
|
57
|
-
// They also have a length property. But checking the length is not enough
|
|
58
|
-
// since, it can also be an object literal with a "length" property. Campaign
|
|
59
|
-
// schema attributes typically have a "length" attribute and are not arrays
|
|
60
|
-
if (obj.length === undefined || obj.length === null) return false;
|
|
61
|
-
// So check for a "push" function
|
|
62
|
-
if (obj.push === undefined || obj.push === null) return false;
|
|
63
|
-
if (typeof obj.push != "function")
|
|
64
|
-
return false;
|
|
65
|
-
return true;
|
|
54
|
+
return Array.isArray(obj);
|
|
66
55
|
}
|
|
67
56
|
|
|
68
57
|
// Helper function for trim() to replace text between 2 indices
|
package/test/domUtil.test.js
CHANGED
|
@@ -157,6 +157,20 @@ describe('DomUtil', function() {
|
|
|
157
157
|
assert.strictEqual(fromJSON({ "a": null }), '<root/>');
|
|
158
158
|
assert.strictEqual(fromJSON({ "a": undefined }), '<root/>');
|
|
159
159
|
});
|
|
160
|
+
|
|
161
|
+
it("Should support attributes named 'length'", () => {
|
|
162
|
+
const json = {
|
|
163
|
+
element: {
|
|
164
|
+
attribute: {
|
|
165
|
+
length: "256",
|
|
166
|
+
name: "id",
|
|
167
|
+
},
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
const doc = DomUtil.fromJSON("extension", json);
|
|
171
|
+
const xml = DomUtil.toXMLString(doc);
|
|
172
|
+
expect(xml).toEqual('<extension><element><attribute length="256" name="id"/></element></extension>');
|
|
173
|
+
});
|
|
160
174
|
});
|
|
161
175
|
|
|
162
176
|
describe('fromJSON (default)', function() {
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2023 Adobe. All rights reserved.
|
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
|
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**********************************************************************************
|
|
15
|
+
*
|
|
16
|
+
* Unit tests for IMS Bearer Token authentication
|
|
17
|
+
*
|
|
18
|
+
*********************************************************************************/
|
|
19
|
+
const sdk = require('../src/index.js');
|
|
20
|
+
const Mock = require('./mock.js').Mock;
|
|
21
|
+
|
|
22
|
+
describe('IMS Bearer Toekn', function () {
|
|
23
|
+
|
|
24
|
+
async function makeImsClient(options) {
|
|
25
|
+
const connectionParameters = sdk.ConnectionParameters.ofImsBearerToken("http://acc-sdk:8080", "ey...", options);
|
|
26
|
+
const client = await sdk.init(connectionParameters);
|
|
27
|
+
if (!options || !options.transport) // allow tests to explicitely set the transport
|
|
28
|
+
client._transport = jest.fn();
|
|
29
|
+
return client;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
it('Should logon with IMS Bearer Token', async () => {
|
|
33
|
+
const client = await makeImsClient();
|
|
34
|
+
// No "Logon" API call is made when using IMS Bearer Token
|
|
35
|
+
//client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
36
|
+
await client.NLWS.xtkSession.logon();
|
|
37
|
+
expect(client.isLogged()).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// The logoff API invalidates the session created on the server side and does not invalidate
|
|
41
|
+
// the bearer token. To invalidate the bearer token, IMS should be used
|
|
42
|
+
it('Should logoff', async () => {
|
|
43
|
+
const client = await makeImsClient();
|
|
44
|
+
await client.NLWS.xtkSession.logon();
|
|
45
|
+
|
|
46
|
+
client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
|
|
47
|
+
await client.NLWS.xtkSession.logoff();
|
|
48
|
+
expect(client.isLogged()).toBe(false);
|
|
49
|
+
|
|
50
|
+
expect(client._transport).toBeCalledTimes(1);
|
|
51
|
+
const calls = client._transport.mock.calls;
|
|
52
|
+
expect(calls[0][0].headers).toMatchObject({
|
|
53
|
+
"ACC-SDK-Auth": "ImsBearerToken",
|
|
54
|
+
"Authorization": "Bearer ey..."
|
|
55
|
+
});
|
|
56
|
+
expect(calls[0][0].headers["X-Security-Token"]).toBeUndefined();
|
|
57
|
+
expect(calls[0][0].headers["X-Session-Token"]).toBeUndefined();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('Should call API with Bearer Token', async () => {
|
|
61
|
+
const client = await makeImsClient();
|
|
62
|
+
await client.NLWS.xtkSession.logon();
|
|
63
|
+
|
|
64
|
+
// Get Option
|
|
65
|
+
client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
|
|
66
|
+
client._transport.mockReturnValueOnce(Mock.GET_DATABASEID_RESPONSE);
|
|
67
|
+
var databaseId = await client.getOption("XtkDatabaseId");
|
|
68
|
+
expect(databaseId).toBe("uFE80000000000000F1FA913DD7CC7C480041161C");
|
|
69
|
+
|
|
70
|
+
// Check that headers were correctly populated for both calls
|
|
71
|
+
expect(client._transport).toBeCalledTimes(2);
|
|
72
|
+
const calls = client._transport.mock.calls;
|
|
73
|
+
expect(calls[0][0].headers).toMatchObject({
|
|
74
|
+
"ACC-SDK-Auth": "ImsBearerToken",
|
|
75
|
+
"ACC-SDK-Call-Internal": "1",
|
|
76
|
+
"Authorization": "Bearer ey..."
|
|
77
|
+
});
|
|
78
|
+
expect(calls[0][0].headers["X-Security-Token"]).toBeUndefined();
|
|
79
|
+
expect(calls[0][0].headers["X-Session-Token"]).toBeUndefined();
|
|
80
|
+
|
|
81
|
+
expect(calls[1][0].headers).toMatchObject({
|
|
82
|
+
"ACC-SDK-Auth": "ImsBearerToken",
|
|
83
|
+
"Authorization": "Bearer ey..."
|
|
84
|
+
});
|
|
85
|
+
expect(calls[1][0].headers["ACC-SDK-Call-Internal"]).toBeUndefined();
|
|
86
|
+
expect(calls[1][0].headers["X-Security-Token"]).toBeUndefined();
|
|
87
|
+
expect(calls[1][0].headers["X-Session-Token"]).toBeUndefined();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("Expired session refresh client callback", async () => {
|
|
91
|
+
|
|
92
|
+
const refreshClient = async (client) => {
|
|
93
|
+
const connectionParameters = sdk.ConnectionParameters.ofImsBearerToken("http://acc-sdk:8080", "ey2...", options);
|
|
94
|
+
client.reinit(connectionParameters);
|
|
95
|
+
await client.NLWS.xtkSession.logon();
|
|
96
|
+
return client;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const transport = jest.fn();
|
|
100
|
+
const options = {
|
|
101
|
+
transport: transport,
|
|
102
|
+
refreshClient: refreshClient,
|
|
103
|
+
};
|
|
104
|
+
const connectionParameters = sdk.ConnectionParameters.ofImsBearerToken("http://acc-sdk:8080", "ey1...", options);
|
|
105
|
+
const client = await sdk.init(connectionParameters);
|
|
106
|
+
await client.NLWS.xtkSession.logon();
|
|
107
|
+
|
|
108
|
+
client._transport.mockReturnValueOnce(Promise.resolve(`XSV-350008 Session has expired or is invalid. Please reconnect.`));
|
|
109
|
+
client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
|
|
110
|
+
client._transport.mockReturnValueOnce(Mock.GET_DATABASEID_RESPONSE);
|
|
111
|
+
var databaseId = await client.getOption("XtkDatabaseId");
|
|
112
|
+
expect(databaseId).toBe("uFE80000000000000F1FA913DD7CC7C480041161C");
|
|
113
|
+
const lastCall = client._transport.mock.calls[client._transport.mock.calls.length - 1];
|
|
114
|
+
expect(lastCall[0].headers).toMatchObject({
|
|
115
|
+
"ACC-SDK-Auth": "ImsBearerToken",
|
|
116
|
+
"Authorization": "Bearer ey2..."
|
|
117
|
+
});
|
|
118
|
+
expect(lastCall[0].headers["X-Security-Token"]).toBeUndefined();
|
|
119
|
+
expect(lastCall[0].headers["X-Session-Token"]).toBeUndefined();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
it("Should call ping API", async () => {
|
|
124
|
+
const client = await makeImsClient();
|
|
125
|
+
await client.NLWS.xtkSession.logon();
|
|
126
|
+
|
|
127
|
+
client._transport.mockReturnValueOnce(Mock.PING);
|
|
128
|
+
const ping = await client.ping();
|
|
129
|
+
expect(ping.status).toBe("OK");
|
|
130
|
+
expect(ping.timestamp).toBe("2021-08-27 15:43:48.862Z");
|
|
131
|
+
|
|
132
|
+
const lastCall = client._transport.mock.calls[client._transport.mock.calls.length - 1];
|
|
133
|
+
expect(lastCall[0].headers).toMatchObject({
|
|
134
|
+
"ACC-SDK-Auth": "ImsBearerToken",
|
|
135
|
+
"Authorization": "Bearer ey..."
|
|
136
|
+
});
|
|
137
|
+
expect(lastCall[0].headers["X-Security-Token"]).toBeUndefined();
|
|
138
|
+
expect(lastCall[0].headers["X-Session-Token"]).toBeUndefined();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("Should call mcPing API", async () => {
|
|
142
|
+
const client = await makeImsClient();
|
|
143
|
+
await client.NLWS.xtkSession.logon();
|
|
144
|
+
|
|
145
|
+
client._transport.mockReturnValueOnce(Mock.MC_PING);
|
|
146
|
+
const ping = await client.mcPing();
|
|
147
|
+
expect(ping.status).toBe("Ok");
|
|
148
|
+
|
|
149
|
+
const lastCall = client._transport.mock.calls[client._transport.mock.calls.length - 1];
|
|
150
|
+
expect(lastCall[0].headers).toMatchObject({
|
|
151
|
+
"ACC-SDK-Auth": "ImsBearerToken",
|
|
152
|
+
"Authorization": "Bearer ey..."
|
|
153
|
+
});
|
|
154
|
+
expect(lastCall[0].headers["X-Security-Token"]).toBeUndefined();
|
|
155
|
+
expect(lastCall[0].headers["X-Session-Token"]).toBeUndefined();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("Should allow to use the application object", async () => {
|
|
159
|
+
const client = await makeImsClient();
|
|
160
|
+
await client.NLWS.xtkSession.logon();
|
|
161
|
+
const application = client.application;
|
|
162
|
+
expect(application).toBeDefined();
|
|
163
|
+
|
|
164
|
+
client._transport.mockReturnValueOnce(Mock.GET_NMS_EXTACCOUNT_SCHEMA_RESPONSE);
|
|
165
|
+
const schema = await application.getSchema("nms:extAccount");
|
|
166
|
+
expect(schema).toBeDefined();
|
|
167
|
+
expect(schema.name).toBe("extAccount");
|
|
168
|
+
|
|
169
|
+
// But the application object does not have any info about packages
|
|
170
|
+
// since we are using a bearer token. Packages are only available
|
|
171
|
+
// when using one of the Logon methods (Logno or BearertokenLogon)
|
|
172
|
+
expect(application.packages).toBeUndefined();
|
|
173
|
+
});
|
|
174
|
+
});
|