facturas 0.4.2 → 0.5.0
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/README.md +2 -0
- package/dist/errors.d.ts +27 -1
- package/dist/errors.js +25 -0
- package/dist/errors.js.map +1 -1
- package/dist/{index-BLq3d6xg.d.ts → index-CqXVyU0E.d.ts} +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +373 -56
- package/dist/index.js.map +1 -1
- package/dist/padron.d.ts +2 -2
- package/dist/padron.js.map +1 -1
- package/dist/{types-SloiIQkT.d.ts → types-CKKe1Oo4.d.ts} +13 -1
- package/dist/types.d.ts +1 -1
- package/dist/wsfe.d.ts +2 -2
- package/dist/wsfe.js.map +1 -1
- package/dist/wsmtxca.d.ts +2 -2
- package/dist/wsmtxca.js.map +1 -1
- package/package.json +14 -15
package/dist/index.js
CHANGED
|
@@ -24,10 +24,12 @@ var ArcaInputError = class extends ArcaError {
|
|
|
24
24
|
var ArcaTransportError = class extends ArcaError {
|
|
25
25
|
name = "ArcaTransportError";
|
|
26
26
|
statusCode;
|
|
27
|
+
contentType;
|
|
27
28
|
responseBody;
|
|
28
29
|
constructor(message, options) {
|
|
29
30
|
super(message, "ARCA_TRANSPORT_ERROR", options);
|
|
30
31
|
this.statusCode = options?.statusCode;
|
|
32
|
+
this.contentType = options?.contentType;
|
|
31
33
|
this.responseBody = options?.responseBody;
|
|
32
34
|
}
|
|
33
35
|
};
|
|
@@ -41,6 +43,28 @@ var ArcaSoapFaultError = class extends ArcaError {
|
|
|
41
43
|
this.detail = options?.detail;
|
|
42
44
|
}
|
|
43
45
|
};
|
|
46
|
+
var ArcaInvalidSoapResponseError = class extends ArcaError {
|
|
47
|
+
name = "ArcaInvalidSoapResponseError";
|
|
48
|
+
service;
|
|
49
|
+
operation;
|
|
50
|
+
endpointUrl;
|
|
51
|
+
statusCode;
|
|
52
|
+
contentType;
|
|
53
|
+
responseBodyLength;
|
|
54
|
+
responseBodyPreview;
|
|
55
|
+
parsedDetail;
|
|
56
|
+
constructor(message, options) {
|
|
57
|
+
super(message, "ARCA_INVALID_SOAP_RESPONSE", options);
|
|
58
|
+
this.service = options?.service;
|
|
59
|
+
this.operation = options?.operation;
|
|
60
|
+
this.endpointUrl = options?.endpointUrl;
|
|
61
|
+
this.statusCode = options?.statusCode;
|
|
62
|
+
this.contentType = options?.contentType;
|
|
63
|
+
this.responseBodyLength = options?.responseBodyLength;
|
|
64
|
+
this.responseBodyPreview = options?.responseBodyPreview;
|
|
65
|
+
this.parsedDetail = options?.parsedDetail;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
44
68
|
var ArcaServiceError = class extends ArcaError {
|
|
45
69
|
name = "ArcaServiceError";
|
|
46
70
|
serviceCode;
|
|
@@ -125,12 +149,33 @@ function assertArcaClientConfig(config) {
|
|
|
125
149
|
if (normalized.logger?.log !== void 0 && typeof normalized.logger.log !== "function") {
|
|
126
150
|
invalidFields.push("logger.log");
|
|
127
151
|
}
|
|
152
|
+
invalidFields.push(...getInvalidWsaaSessionStoreFields(normalized));
|
|
128
153
|
if (invalidFields.length > 0) {
|
|
129
154
|
throw new ArcaConfigurationError(
|
|
130
155
|
`Missing or invalid ARCA client config fields: ${invalidFields.join(", ")}`
|
|
131
156
|
);
|
|
132
157
|
}
|
|
133
158
|
}
|
|
159
|
+
function getInvalidWsaaSessionStoreFields(config) {
|
|
160
|
+
const store = config.wsaaSessionStore;
|
|
161
|
+
if (store === void 0) {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
const invalidFields = [];
|
|
165
|
+
if (typeof store.get !== "function") {
|
|
166
|
+
invalidFields.push("wsaaSessionStore.get");
|
|
167
|
+
}
|
|
168
|
+
if (typeof store.set !== "function") {
|
|
169
|
+
invalidFields.push("wsaaSessionStore.set");
|
|
170
|
+
}
|
|
171
|
+
if (store.delete !== void 0 && typeof store.delete !== "function") {
|
|
172
|
+
invalidFields.push("wsaaSessionStore.delete");
|
|
173
|
+
}
|
|
174
|
+
if (store.withLock !== void 0 && typeof store.withLock !== "function") {
|
|
175
|
+
invalidFields.push("wsaaSessionStore.withLock");
|
|
176
|
+
}
|
|
177
|
+
return invalidFields;
|
|
178
|
+
}
|
|
134
179
|
var ARCA_WSAA_CONFIG = {
|
|
135
180
|
namespace: "http://wsaa.view.sua.dvadac.desein.afip.gov",
|
|
136
181
|
endpoint: {
|
|
@@ -208,7 +253,8 @@ function normalizeArcaClientConfig(config) {
|
|
|
208
253
|
...config.logger,
|
|
209
254
|
...normalizedLoggerLevel === void 0 ? {} : { level: normalizedLoggerLevel }
|
|
210
255
|
}
|
|
211
|
-
}
|
|
256
|
+
},
|
|
257
|
+
...config.wsaaSessionStore === void 0 ? {} : { wsaaSessionStore: config.wsaaSessionStore }
|
|
212
258
|
};
|
|
213
259
|
}
|
|
214
260
|
function normalizeEnvironmentValue(value) {
|
|
@@ -1219,7 +1265,7 @@ var legacyTlsAgent = new https.Agent({
|
|
|
1219
1265
|
keepAlive: true,
|
|
1220
1266
|
ciphers: "DEFAULT@SECLEVEL=0"
|
|
1221
1267
|
});
|
|
1222
|
-
async function
|
|
1268
|
+
async function postXmlWithMetadata({
|
|
1223
1269
|
url,
|
|
1224
1270
|
body,
|
|
1225
1271
|
contentType,
|
|
@@ -1287,12 +1333,12 @@ async function postXmlOnce({
|
|
|
1287
1333
|
const requestBody = Buffer.from(body, "utf8");
|
|
1288
1334
|
return await new Promise((resolve, reject) => {
|
|
1289
1335
|
let settled = false;
|
|
1290
|
-
const settleResolve = (
|
|
1336
|
+
const settleResolve = (response) => {
|
|
1291
1337
|
if (settled) {
|
|
1292
1338
|
return;
|
|
1293
1339
|
}
|
|
1294
1340
|
settled = true;
|
|
1295
|
-
resolve(
|
|
1341
|
+
resolve(response);
|
|
1296
1342
|
};
|
|
1297
1343
|
const settleReject = (error) => {
|
|
1298
1344
|
if (settled) {
|
|
@@ -1351,11 +1397,19 @@ async function postXmlOnce({
|
|
|
1351
1397
|
response.headers["content-type"]
|
|
1352
1398
|
) ? response.headers["content-type"].join("; ") : response.headers["content-type"];
|
|
1353
1399
|
if (statusCode >= 200 && statusCode < 300) {
|
|
1354
|
-
settleResolve(
|
|
1400
|
+
settleResolve({
|
|
1401
|
+
body: responseBody,
|
|
1402
|
+
statusCode,
|
|
1403
|
+
contentType: responseContentType
|
|
1404
|
+
});
|
|
1355
1405
|
return;
|
|
1356
1406
|
}
|
|
1357
1407
|
if (isXmlLikeResponse(responseBody, responseContentType)) {
|
|
1358
|
-
settleResolve(
|
|
1408
|
+
settleResolve({
|
|
1409
|
+
body: responseBody,
|
|
1410
|
+
statusCode,
|
|
1411
|
+
contentType: responseContentType
|
|
1412
|
+
});
|
|
1359
1413
|
return;
|
|
1360
1414
|
}
|
|
1361
1415
|
settleReject(
|
|
@@ -1363,6 +1417,7 @@ async function postXmlOnce({
|
|
|
1363
1417
|
`ARCA HTTP request failed with status ${statusCode}`,
|
|
1364
1418
|
{
|
|
1365
1419
|
statusCode,
|
|
1420
|
+
contentType: responseContentType,
|
|
1366
1421
|
responseBody
|
|
1367
1422
|
}
|
|
1368
1423
|
)
|
|
@@ -1437,14 +1492,26 @@ function buildSoapEnvelope(soapVersion, operation, namespace, body, options) {
|
|
|
1437
1492
|
};
|
|
1438
1493
|
return `<?xml version="1.0" encoding="utf-8"?>${xmlBuilder.build(payload)}`;
|
|
1439
1494
|
}
|
|
1440
|
-
function parseSoapBody(xml) {
|
|
1441
|
-
|
|
1495
|
+
function parseSoapBody(xml, context = {}) {
|
|
1496
|
+
let parsed;
|
|
1497
|
+
try {
|
|
1498
|
+
parsed = xmlParser.parse(xml);
|
|
1499
|
+
} catch (error) {
|
|
1500
|
+
throw createInvalidSoapResponseError(
|
|
1501
|
+
"Invalid SOAP response: XML parse failed",
|
|
1502
|
+
context,
|
|
1503
|
+
void 0,
|
|
1504
|
+
error instanceof Error ? error : void 0
|
|
1505
|
+
);
|
|
1506
|
+
}
|
|
1442
1507
|
const envelope = parsed.Envelope;
|
|
1443
1508
|
const body = envelope?.Body;
|
|
1444
1509
|
if (!body) {
|
|
1445
|
-
throw
|
|
1446
|
-
|
|
1447
|
-
|
|
1510
|
+
throw createInvalidSoapResponseError(
|
|
1511
|
+
"Invalid SOAP response: missing body",
|
|
1512
|
+
context,
|
|
1513
|
+
parsed
|
|
1514
|
+
);
|
|
1448
1515
|
}
|
|
1449
1516
|
const fault = body.Fault;
|
|
1450
1517
|
if (fault) {
|
|
@@ -1452,18 +1519,40 @@ function parseSoapBody(xml) {
|
|
|
1452
1519
|
}
|
|
1453
1520
|
return body;
|
|
1454
1521
|
}
|
|
1455
|
-
function getSingleBodyEntry(body) {
|
|
1522
|
+
function getSingleBodyEntry(body, context = {}) {
|
|
1456
1523
|
const entries = Object.entries(body).filter(([key]) => key !== "@_xmlns");
|
|
1457
1524
|
if (entries.length !== 1) {
|
|
1458
|
-
throw
|
|
1525
|
+
throw createInvalidSoapResponseError(
|
|
1459
1526
|
`Invalid SOAP response: expected a single body entry, got ${entries.length}`,
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
}
|
|
1527
|
+
context,
|
|
1528
|
+
body
|
|
1463
1529
|
);
|
|
1464
1530
|
}
|
|
1465
1531
|
return entries[0];
|
|
1466
1532
|
}
|
|
1533
|
+
function createInvalidSoapResponseError(message, context, parsedDetail, cause) {
|
|
1534
|
+
const responseBody = context.responseBody ?? "";
|
|
1535
|
+
return new ArcaInvalidSoapResponseError(message, {
|
|
1536
|
+
cause,
|
|
1537
|
+
service: context.service,
|
|
1538
|
+
operation: context.operation,
|
|
1539
|
+
endpointUrl: context.endpointUrl,
|
|
1540
|
+
statusCode: context.statusCode,
|
|
1541
|
+
contentType: context.contentType,
|
|
1542
|
+
responseBodyLength: responseBody.length,
|
|
1543
|
+
responseBodyPreview: sanitizeSoapResponsePreview(
|
|
1544
|
+
responseBody,
|
|
1545
|
+
context.responseBodyPreviewLength
|
|
1546
|
+
),
|
|
1547
|
+
parsedDetail
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
function sanitizeSoapResponsePreview(responseBody, maxLength = 4096) {
|
|
1551
|
+
return responseBody.replace(
|
|
1552
|
+
/<((?:[A-Za-z_][\w.-]*:)?(?:Token|Sign))\b([^>]*)>[\s\S]*?<\/\1>/gi,
|
|
1553
|
+
(_match, tagName, attributes) => `<${tagName}${attributes}>[REDACTED]</${tagName}>`
|
|
1554
|
+
).slice(0, maxLength);
|
|
1555
|
+
}
|
|
1467
1556
|
function parseXmlDocument(xml) {
|
|
1468
1557
|
return xmlParser.parse(xml);
|
|
1469
1558
|
}
|
|
@@ -1522,7 +1611,7 @@ function createSoapTransport(options) {
|
|
|
1522
1611
|
url
|
|
1523
1612
|
});
|
|
1524
1613
|
try {
|
|
1525
|
-
const
|
|
1614
|
+
const response = await postXmlWithMetadata({
|
|
1526
1615
|
url,
|
|
1527
1616
|
body: xml,
|
|
1528
1617
|
contentType,
|
|
@@ -1540,12 +1629,23 @@ function createSoapTransport(options) {
|
|
|
1540
1629
|
operation: request.operation,
|
|
1541
1630
|
durationMs: Date.now() - startedAt
|
|
1542
1631
|
});
|
|
1543
|
-
const
|
|
1544
|
-
|
|
1632
|
+
const parseContext = {
|
|
1633
|
+
service: request.service,
|
|
1634
|
+
operation: request.operation,
|
|
1635
|
+
endpointUrl: url,
|
|
1636
|
+
statusCode: response.statusCode,
|
|
1637
|
+
contentType: response.contentType,
|
|
1638
|
+
responseBody: response.body
|
|
1639
|
+
};
|
|
1640
|
+
const soapBody = parseSoapBody(response.body, parseContext);
|
|
1641
|
+
const [, result] = getSingleBodyEntry(
|
|
1642
|
+
soapBody,
|
|
1643
|
+
parseContext
|
|
1644
|
+
);
|
|
1545
1645
|
return {
|
|
1546
1646
|
service: request.service,
|
|
1547
1647
|
operation: request.operation,
|
|
1548
|
-
raw:
|
|
1648
|
+
raw: response.body,
|
|
1549
1649
|
result
|
|
1550
1650
|
};
|
|
1551
1651
|
} catch (error) {
|
|
@@ -1558,6 +1658,17 @@ function createSoapTransport(options) {
|
|
|
1558
1658
|
error
|
|
1559
1659
|
});
|
|
1560
1660
|
}
|
|
1661
|
+
if (error instanceof ArcaInvalidSoapResponseError) {
|
|
1662
|
+
options.logger?.error("ARCA invalid SOAP response", {
|
|
1663
|
+
service: request.service,
|
|
1664
|
+
operation: request.operation,
|
|
1665
|
+
url,
|
|
1666
|
+
statusCode: error.statusCode,
|
|
1667
|
+
contentType: error.contentType,
|
|
1668
|
+
responseBodyLength: error.responseBodyLength,
|
|
1669
|
+
error
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1561
1672
|
throw error;
|
|
1562
1673
|
}
|
|
1563
1674
|
}
|
|
@@ -1567,48 +1678,117 @@ function createSoapTransport(options) {
|
|
|
1567
1678
|
// src/wsaa/index.ts
|
|
1568
1679
|
import { createHash } from "crypto";
|
|
1569
1680
|
import forge from "node-forge";
|
|
1681
|
+
|
|
1682
|
+
// src/wsaa/session-store.ts
|
|
1683
|
+
var CREDENTIAL_EXPIRY_SAFETY_MARGIN_MS = 6e4;
|
|
1684
|
+
function createMemoryWsaaSessionStore() {
|
|
1685
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
1686
|
+
const locks = /* @__PURE__ */ new Map();
|
|
1687
|
+
return {
|
|
1688
|
+
get(key) {
|
|
1689
|
+
const credentials = sessions.get(serializeWsaaSessionKey(key));
|
|
1690
|
+
if (!(credentials && isWsaaCredentialValid(credentials))) {
|
|
1691
|
+
return Promise.resolve(null);
|
|
1692
|
+
}
|
|
1693
|
+
return Promise.resolve({ ...credentials });
|
|
1694
|
+
},
|
|
1695
|
+
set(key, credentials) {
|
|
1696
|
+
sessions.set(serializeWsaaSessionKey(key), { ...credentials });
|
|
1697
|
+
return Promise.resolve();
|
|
1698
|
+
},
|
|
1699
|
+
delete(key) {
|
|
1700
|
+
sessions.delete(serializeWsaaSessionKey(key));
|
|
1701
|
+
return Promise.resolve();
|
|
1702
|
+
},
|
|
1703
|
+
async withLock(key, fn) {
|
|
1704
|
+
const lockKey = serializeWsaaSessionKey(key);
|
|
1705
|
+
const previous = locks.get(lockKey) ?? Promise.resolve();
|
|
1706
|
+
let release = () => void 0;
|
|
1707
|
+
const current = new Promise((resolve) => {
|
|
1708
|
+
release = resolve;
|
|
1709
|
+
});
|
|
1710
|
+
const queued = previous.catch(() => void 0).then(() => current);
|
|
1711
|
+
locks.set(lockKey, queued);
|
|
1712
|
+
await previous.catch(() => void 0);
|
|
1713
|
+
try {
|
|
1714
|
+
return await fn();
|
|
1715
|
+
} finally {
|
|
1716
|
+
release();
|
|
1717
|
+
if (locks.get(lockKey) === queued) {
|
|
1718
|
+
locks.delete(lockKey);
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
function serializeWsaaSessionKey(key) {
|
|
1725
|
+
return [key.environment, key.service, key.certificateFingerprint].join(":");
|
|
1726
|
+
}
|
|
1727
|
+
function isWsaaCredentialValid(credentials) {
|
|
1728
|
+
return new Date(credentials.expiresAt).getTime() - Date.now() > CREDENTIAL_EXPIRY_SAFETY_MARGIN_MS;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
// src/wsaa/index.ts
|
|
1570
1732
|
function createWsaaAuthModule(options) {
|
|
1571
1733
|
const cache = /* @__PURE__ */ new Map();
|
|
1572
1734
|
const inFlight = /* @__PURE__ */ new Map();
|
|
1573
1735
|
return {
|
|
1574
1736
|
async login(service, authOptions = {}) {
|
|
1575
|
-
const
|
|
1737
|
+
const sessionKey = buildWsaaSessionKey(options.config, service);
|
|
1738
|
+
const cacheKey = serializeWsaaSessionKey(sessionKey);
|
|
1576
1739
|
const running = inFlight.get(cacheKey);
|
|
1577
1740
|
if (running) {
|
|
1578
1741
|
return running;
|
|
1579
1742
|
}
|
|
1580
1743
|
const loginPromise = (async () => {
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
});
|
|
1588
|
-
return cached;
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
options.logger?.debug("Attempting WSAA login", {
|
|
1744
|
+
const reuse = await getReusableCredentials({
|
|
1745
|
+
config: options.config,
|
|
1746
|
+
cache,
|
|
1747
|
+
cacheKey,
|
|
1748
|
+
sessionKey,
|
|
1749
|
+
logger: options.logger,
|
|
1592
1750
|
service,
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
const credentials = await requestCredentials(options.config, service, {
|
|
1596
|
-
logger: options.logger
|
|
1751
|
+
allowStore: !authOptions.forceRefresh,
|
|
1752
|
+
allowCache: !authOptions.forceRefresh
|
|
1597
1753
|
});
|
|
1598
|
-
|
|
1754
|
+
if (reuse) {
|
|
1755
|
+
return reuse;
|
|
1756
|
+
}
|
|
1757
|
+
const refresh = () => refreshWsaaCredentials({
|
|
1758
|
+
config: options.config,
|
|
1759
|
+
cache,
|
|
1760
|
+
cacheKey,
|
|
1761
|
+
sessionKey,
|
|
1762
|
+
logger: options.logger,
|
|
1599
1763
|
service,
|
|
1600
|
-
|
|
1764
|
+
forceRefresh: authOptions.forceRefresh === true
|
|
1601
1765
|
});
|
|
1602
|
-
|
|
1603
|
-
|
|
1766
|
+
if (options.config.wsaaSessionStore?.withLock) {
|
|
1767
|
+
return await withWsaaSessionStoreLock(
|
|
1768
|
+
options.config,
|
|
1769
|
+
sessionKey,
|
|
1770
|
+
service,
|
|
1771
|
+
refresh
|
|
1772
|
+
);
|
|
1773
|
+
}
|
|
1774
|
+
return await refresh();
|
|
1604
1775
|
})();
|
|
1605
1776
|
inFlight.set(cacheKey, loginPromise);
|
|
1606
1777
|
try {
|
|
1607
1778
|
return await loginPromise;
|
|
1608
1779
|
} catch (error) {
|
|
1609
|
-
if (
|
|
1610
|
-
const
|
|
1611
|
-
|
|
1780
|
+
if (error instanceof ArcaSoapFaultError && error.faultCode === "ns1:coe.alreadyAuthenticated") {
|
|
1781
|
+
const recovered = await getReusableCredentials({
|
|
1782
|
+
config: options.config,
|
|
1783
|
+
cache,
|
|
1784
|
+
cacheKey,
|
|
1785
|
+
sessionKey,
|
|
1786
|
+
logger: options.logger,
|
|
1787
|
+
service,
|
|
1788
|
+
allowStore: true,
|
|
1789
|
+
allowCache: true
|
|
1790
|
+
});
|
|
1791
|
+
if (recovered) {
|
|
1612
1792
|
options.logger?.warn(
|
|
1613
1793
|
"Recovered WSAA coe.alreadyAuthenticated fault",
|
|
1614
1794
|
{
|
|
@@ -1616,7 +1796,13 @@ function createWsaaAuthModule(options) {
|
|
|
1616
1796
|
faultCode: error.faultCode
|
|
1617
1797
|
}
|
|
1618
1798
|
);
|
|
1619
|
-
return
|
|
1799
|
+
return recovered;
|
|
1800
|
+
}
|
|
1801
|
+
if (!options.config.wsaaSessionStore) {
|
|
1802
|
+
throw new ArcaConfigurationError(
|
|
1803
|
+
"WSAA login failed because another process likely owns a valid TA. Configure a durable wsaaSessionStore for multi-process or serverless deployments.",
|
|
1804
|
+
{ cause: error }
|
|
1805
|
+
);
|
|
1620
1806
|
}
|
|
1621
1807
|
}
|
|
1622
1808
|
if (error instanceof ArcaSoapFaultError) {
|
|
@@ -1647,7 +1833,8 @@ async function requestCredentials(config, service, options) {
|
|
|
1647
1833
|
ARCA_WSAA_CONFIG.namespace,
|
|
1648
1834
|
{ in0: signedCms }
|
|
1649
1835
|
);
|
|
1650
|
-
const
|
|
1836
|
+
const url = ARCA_WSAA_CONFIG.endpoint[config.environment];
|
|
1837
|
+
const response = await postXmlWithMetadata({
|
|
1651
1838
|
url: ARCA_WSAA_CONFIG.endpoint[config.environment],
|
|
1652
1839
|
body: requestXml,
|
|
1653
1840
|
contentType: 'text/xml; charset="utf-8"',
|
|
@@ -1659,9 +1846,20 @@ async function requestCredentials(config, service, options) {
|
|
|
1659
1846
|
service: "wsaa",
|
|
1660
1847
|
operation: "loginCms"
|
|
1661
1848
|
});
|
|
1662
|
-
const
|
|
1663
|
-
|
|
1664
|
-
|
|
1849
|
+
const parseContext = {
|
|
1850
|
+
service: "wsaa",
|
|
1851
|
+
operation: "loginCms",
|
|
1852
|
+
endpointUrl: url,
|
|
1853
|
+
statusCode: response.statusCode,
|
|
1854
|
+
contentType: response.contentType,
|
|
1855
|
+
responseBody: response.body
|
|
1856
|
+
};
|
|
1857
|
+
const soapBody = parseSoapBody(response.body, parseContext);
|
|
1858
|
+
const [, responseBody] = getSingleBodyEntry(
|
|
1859
|
+
soapBody,
|
|
1860
|
+
parseContext
|
|
1861
|
+
);
|
|
1862
|
+
const loginCmsReturn = responseBody.loginCmsReturn;
|
|
1665
1863
|
if (typeof loginCmsReturn !== "string" || loginCmsReturn.trim().length < 1) {
|
|
1666
1864
|
throw new ArcaTransportError(
|
|
1667
1865
|
"WSAA response did not include loginCmsReturn XML"
|
|
@@ -1669,24 +1867,141 @@ async function requestCredentials(config, service, options) {
|
|
|
1669
1867
|
}
|
|
1670
1868
|
return parseLoginTicketResponse(loginCmsReturn);
|
|
1671
1869
|
}
|
|
1672
|
-
function
|
|
1673
|
-
return
|
|
1674
|
-
|
|
1675
|
-
|
|
1870
|
+
function buildWsaaSessionKey(config, service) {
|
|
1871
|
+
return {
|
|
1872
|
+
environment: config.environment,
|
|
1873
|
+
service,
|
|
1874
|
+
certificateFingerprint: getCertificateFingerprint(config)
|
|
1875
|
+
};
|
|
1676
1876
|
}
|
|
1677
1877
|
function getCertificateFingerprint(config) {
|
|
1678
1878
|
return createHash("sha256").update(config.certificatePem).digest("hex");
|
|
1679
1879
|
}
|
|
1680
|
-
function isCredentialValid(credentials) {
|
|
1681
|
-
return new Date(credentials.expiresAt).getTime() - Date.now() > 6e4;
|
|
1682
|
-
}
|
|
1683
1880
|
function getCachedCredentials(cache, cacheKey) {
|
|
1684
1881
|
const localCached = cache.get(cacheKey);
|
|
1685
|
-
if (localCached &&
|
|
1882
|
+
if (localCached && isWsaaCredentialValid(localCached)) {
|
|
1686
1883
|
return localCached;
|
|
1687
1884
|
}
|
|
1688
1885
|
return null;
|
|
1689
1886
|
}
|
|
1887
|
+
async function getReusableCredentials(options) {
|
|
1888
|
+
if (options.allowCache) {
|
|
1889
|
+
const cached = getCachedCredentials(options.cache, options.cacheKey);
|
|
1890
|
+
if (cached) {
|
|
1891
|
+
options.logger?.debug("Attempting WSAA login", {
|
|
1892
|
+
service: options.service,
|
|
1893
|
+
source: "cached"
|
|
1894
|
+
});
|
|
1895
|
+
return cached;
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
if (!(options.allowStore && options.config.wsaaSessionStore)) {
|
|
1899
|
+
return null;
|
|
1900
|
+
}
|
|
1901
|
+
const stored = await getStoredCredentials(
|
|
1902
|
+
options.config,
|
|
1903
|
+
options.sessionKey,
|
|
1904
|
+
options.service
|
|
1905
|
+
);
|
|
1906
|
+
if (!stored) {
|
|
1907
|
+
return null;
|
|
1908
|
+
}
|
|
1909
|
+
options.cache.set(options.cacheKey, stored);
|
|
1910
|
+
options.logger?.debug("Attempting WSAA login", {
|
|
1911
|
+
service: options.service,
|
|
1912
|
+
source: "store"
|
|
1913
|
+
});
|
|
1914
|
+
return stored;
|
|
1915
|
+
}
|
|
1916
|
+
async function refreshWsaaCredentials(options) {
|
|
1917
|
+
if (!options.forceRefresh) {
|
|
1918
|
+
const reuse = await getReusableCredentials({
|
|
1919
|
+
config: options.config,
|
|
1920
|
+
cache: options.cache,
|
|
1921
|
+
cacheKey: options.cacheKey,
|
|
1922
|
+
sessionKey: options.sessionKey,
|
|
1923
|
+
logger: options.logger,
|
|
1924
|
+
service: options.service,
|
|
1925
|
+
allowStore: true,
|
|
1926
|
+
allowCache: true
|
|
1927
|
+
});
|
|
1928
|
+
if (reuse) {
|
|
1929
|
+
return reuse;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
options.logger?.debug("Attempting WSAA login", {
|
|
1933
|
+
service: options.service,
|
|
1934
|
+
source: "fresh"
|
|
1935
|
+
});
|
|
1936
|
+
const credentials = await requestCredentials(
|
|
1937
|
+
options.config,
|
|
1938
|
+
options.service,
|
|
1939
|
+
{
|
|
1940
|
+
logger: options.logger
|
|
1941
|
+
}
|
|
1942
|
+
);
|
|
1943
|
+
options.logger?.info("WSAA login succeeded", {
|
|
1944
|
+
service: options.service,
|
|
1945
|
+
expiresAt: credentials.expiresAt
|
|
1946
|
+
});
|
|
1947
|
+
options.cache.set(options.cacheKey, credentials);
|
|
1948
|
+
await setStoredCredentials(options.config, options.sessionKey, credentials);
|
|
1949
|
+
return credentials;
|
|
1950
|
+
}
|
|
1951
|
+
async function getStoredCredentials(config, key, service) {
|
|
1952
|
+
if (!config.wsaaSessionStore) {
|
|
1953
|
+
return null;
|
|
1954
|
+
}
|
|
1955
|
+
try {
|
|
1956
|
+
const credentials = await config.wsaaSessionStore.get(key);
|
|
1957
|
+
if (!(credentials && isWsaaCredentialValid(credentials))) {
|
|
1958
|
+
return null;
|
|
1959
|
+
}
|
|
1960
|
+
return credentials;
|
|
1961
|
+
} catch (error) {
|
|
1962
|
+
throw new ArcaConfigurationError(
|
|
1963
|
+
`WSAA session store get failed for service ${service}`,
|
|
1964
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
1965
|
+
);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
async function setStoredCredentials(config, key, credentials) {
|
|
1969
|
+
if (!config.wsaaSessionStore) {
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1972
|
+
try {
|
|
1973
|
+
await config.wsaaSessionStore.set(key, credentials);
|
|
1974
|
+
} catch (error) {
|
|
1975
|
+
throw new ArcaConfigurationError(
|
|
1976
|
+
`WSAA session store set failed for service ${key.service}`,
|
|
1977
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
1978
|
+
);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
async function withWsaaSessionStoreLock(config, key, service, fn) {
|
|
1982
|
+
const store = config.wsaaSessionStore;
|
|
1983
|
+
if (!store?.withLock) {
|
|
1984
|
+
return await fn();
|
|
1985
|
+
}
|
|
1986
|
+
let entered = false;
|
|
1987
|
+
try {
|
|
1988
|
+
return await store.withLock(key, async () => {
|
|
1989
|
+
entered = true;
|
|
1990
|
+
return await fn();
|
|
1991
|
+
});
|
|
1992
|
+
} catch (error) {
|
|
1993
|
+
if (entered) {
|
|
1994
|
+
throw error;
|
|
1995
|
+
}
|
|
1996
|
+
if (error instanceof ArcaConfigurationError) {
|
|
1997
|
+
throw error;
|
|
1998
|
+
}
|
|
1999
|
+
throw new ArcaConfigurationError(
|
|
2000
|
+
`WSAA session store lock failed for service ${service}`,
|
|
2001
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
2002
|
+
);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
1690
2005
|
function buildLoginTicketRequest(service) {
|
|
1691
2006
|
const uniqueId = Math.floor(Date.now() / 1e3);
|
|
1692
2007
|
const generationTime = new Date(Date.now() - 5 * 6e4).toISOString().replace(".000Z", "Z");
|
|
@@ -1771,12 +2086,14 @@ export {
|
|
|
1771
2086
|
ArcaConfigurationError,
|
|
1772
2087
|
ArcaError,
|
|
1773
2088
|
ArcaInputError,
|
|
2089
|
+
ArcaInvalidSoapResponseError,
|
|
1774
2090
|
ArcaServiceError,
|
|
1775
2091
|
ArcaSoapFaultError,
|
|
1776
2092
|
ArcaTransportError,
|
|
1777
2093
|
assertArcaClientConfig,
|
|
1778
2094
|
createArcaClient,
|
|
1779
2095
|
createArcaClientConfigFromEnv,
|
|
2096
|
+
createMemoryWsaaSessionStore,
|
|
1780
2097
|
createPadronService,
|
|
1781
2098
|
createWsfeService,
|
|
1782
2099
|
createWsmtxcaService,
|