samlesa 2.16.6 → 2.17.1
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.
Potentially problematic release.
This version of samlesa might be problematic. Click here for more details.
- package/README.md +30 -50
- package/build/index.js +2 -1
- package/build/src/binding-artifact.js +330 -146
- package/build/src/binding-post.js +45 -31
- package/build/src/binding-redirect.js +0 -10
- package/build/src/binding-simplesign.js +0 -1
- package/build/src/entity-idp.js +1 -5
- package/build/src/entity-sp.js +21 -96
- package/build/src/extractor.js +48 -4
- package/build/src/flow.js +24 -166
- package/build/src/libsaml.js +468 -264
- package/build/src/libsamlSoap.js +115 -0
- package/build/src/schema/xml.xsd +88 -88
- package/build/src/schemaValidator.js +5 -13
- package/build/src/soap.js +123 -3
- package/build/src/utility.js +12 -7
- package/package.json +77 -81
- package/types/api.d.ts +15 -0
- package/types/api.d.ts.map +1 -0
- package/types/binding-post.d.ts +48 -0
- package/types/binding-post.d.ts.map +1 -0
- package/types/binding-redirect.d.ts +54 -0
- package/types/binding-redirect.d.ts.map +1 -0
- package/types/binding-simplesign.d.ts +41 -0
- package/types/binding-simplesign.d.ts.map +1 -0
- package/types/entity-idp.d.ts +38 -0
- package/types/entity-idp.d.ts.map +1 -0
- package/types/entity-sp.d.ts +38 -0
- package/types/entity-sp.d.ts.map +1 -0
- package/types/entity.d.ts +100 -0
- package/types/entity.d.ts.map +1 -0
- package/types/extractor.d.ts +26 -0
- package/types/extractor.d.ts.map +1 -0
- package/types/flow.d.ts +7 -0
- package/types/flow.d.ts.map +1 -0
- package/types/index.d.ts +2 -1
- package/types/index.d.ts.map +1 -1
- package/types/libsaml.d.ts +208 -0
- package/types/libsaml.d.ts.map +1 -0
- package/types/metadata-idp.d.ts +25 -0
- package/types/metadata-idp.d.ts.map +1 -0
- package/types/metadata-sp.d.ts +37 -0
- package/types/metadata-sp.d.ts.map +1 -0
- package/types/metadata.d.ts +58 -0
- package/types/metadata.d.ts.map +1 -0
- package/types/src/api.d.ts +3 -3
- package/types/src/api.d.ts.map +1 -1
- package/types/src/binding-artifact.d.ts +24 -29
- package/types/src/binding-artifact.d.ts.map +1 -1
- package/types/src/binding-post.d.ts +22 -22
- package/types/src/binding-post.d.ts.map +1 -1
- package/types/src/binding-redirect.d.ts.map +1 -1
- package/types/src/binding-simplesign.d.ts.map +1 -1
- package/types/src/entity-idp.d.ts +3 -4
- package/types/src/entity-idp.d.ts.map +1 -1
- package/types/src/entity-sp.d.ts +13 -24
- package/types/src/entity-sp.d.ts.map +1 -1
- package/types/src/entity.d.ts.map +1 -1
- package/types/src/extractor.d.ts +22 -0
- package/types/src/extractor.d.ts.map +1 -1
- package/types/src/flow.d.ts +1 -0
- package/types/src/flow.d.ts.map +1 -1
- package/types/src/libsaml.d.ts +16 -7
- package/types/src/libsaml.d.ts.map +1 -1
- package/types/src/libsamlSoap.d.ts +7 -0
- package/types/src/libsamlSoap.d.ts.map +1 -0
- package/types/src/schemaValidator.d.ts +1 -1
- package/types/src/schemaValidator.d.ts.map +1 -1
- package/types/src/soap.d.ts +33 -0
- package/types/src/soap.d.ts.map +1 -1
- package/types/src/utility.d.ts.map +1 -1
- package/types/src/validator.d.ts.map +1 -1
- package/types/types.d.ts +128 -0
- package/types/types.d.ts.map +1 -0
- package/types/urn.d.ts +195 -0
- package/types/urn.d.ts.map +1 -0
- package/types/utility.d.ts +133 -0
- package/types/utility.d.ts.map +1 -0
- package/types/validator.d.ts +4 -0
- package/types/validator.d.ts.map +1 -0
- package/build/src/schema/XMLSchema.dtd +0 -402
- package/build/src/schema/datatypes.dtd +0 -203
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { getContext } from "./api.js";
|
|
2
|
+
import { select } from "xpath";
|
|
3
|
+
import { SignedXml } from "xml-crypto";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import utility, { flattenDeep } from "./utility.js";
|
|
6
|
+
import libsaml from "./libsaml.js";
|
|
7
|
+
import { wording } from "./urn.js";
|
|
8
|
+
import { DOMParser } from '@xmldom/xmldom';
|
|
9
|
+
const certUse = wording.certUse;
|
|
10
|
+
const docParser = new DOMParser();
|
|
11
|
+
async function verifyAndDecryptSoapMessage(xml, opts) {
|
|
12
|
+
const { dom } = getContext();
|
|
13
|
+
const doc = dom.parseFromString(xml, 'application/xml');
|
|
14
|
+
const docParser = new DOMParser();
|
|
15
|
+
let type = '';
|
|
16
|
+
// 为 SOAP 消息定义 XPath
|
|
17
|
+
const artifactResolveXpath = "/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='ArtifactResolve']";
|
|
18
|
+
const artifactResponseXpath = "/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='ArtifactResponse']";
|
|
19
|
+
// 检测 ArtifactResolve 或 ArtifactResponse 的存在
|
|
20
|
+
// @ts-expect-error
|
|
21
|
+
const artifactResolveNodes = select(artifactResolveXpath, doc);
|
|
22
|
+
// @ts-expect-error
|
|
23
|
+
const artifactResponseNodes = select(artifactResponseXpath, doc);
|
|
24
|
+
// 根据消息类型选择合适的 XPath
|
|
25
|
+
let basePath = "";
|
|
26
|
+
if (artifactResolveNodes?.length > 0) {
|
|
27
|
+
type = 'artifactResolve';
|
|
28
|
+
basePath = "/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='ArtifactResolve']";
|
|
29
|
+
}
|
|
30
|
+
else if (artifactResponseNodes?.length > 0) {
|
|
31
|
+
type = 'artifactResponse';
|
|
32
|
+
basePath = "/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='ArtifactResponse']";
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
throw new Error('ERR_UNSUPPORTED_SOAP_MESSAGE_TYPE');
|
|
36
|
+
}
|
|
37
|
+
// 基于 SOAP 结构重新定义 XPath
|
|
38
|
+
const messageSignatureXpath = `${basePath}/*[local-name(.)='Signature']`;
|
|
39
|
+
// @ts-expect-error
|
|
40
|
+
const messageSignatureNode = select(messageSignatureXpath, doc);
|
|
41
|
+
let selection = [];
|
|
42
|
+
if (messageSignatureNode?.length > 0) {
|
|
43
|
+
selection = selection.concat(messageSignatureNode);
|
|
44
|
+
}
|
|
45
|
+
if (selection.length === 0) {
|
|
46
|
+
throw new Error('ERR_ZERO_SIGNATURE');
|
|
47
|
+
}
|
|
48
|
+
return verifySignature(xml, selection, opts);
|
|
49
|
+
}
|
|
50
|
+
function verifySignature(xml, selection, opts) {
|
|
51
|
+
// 尝试所有签名节点
|
|
52
|
+
for (const signatureNode of selection) {
|
|
53
|
+
const sig = new SignedXml();
|
|
54
|
+
let verified = false;
|
|
55
|
+
sig.signatureAlgorithm = opts.signatureAlgorithm;
|
|
56
|
+
if (!opts.keyFile && !opts.metadata) {
|
|
57
|
+
throw new Error('ERR_UNDEFINED_SIGNATURE_VERIFIER_OPTIONS');
|
|
58
|
+
}
|
|
59
|
+
if (opts.keyFile) {
|
|
60
|
+
sig.publicCert = fs.readFileSync(opts.keyFile);
|
|
61
|
+
}
|
|
62
|
+
if (opts.metadata) {
|
|
63
|
+
const certificateNode = select(".//*[local-name(.)='X509Certificate']", signatureNode);
|
|
64
|
+
// 证书处理逻辑
|
|
65
|
+
let metadataCert = opts.metadata.getX509Certificate(certUse.signing);
|
|
66
|
+
if (Array.isArray(metadataCert)) {
|
|
67
|
+
metadataCert = flattenDeep(metadataCert);
|
|
68
|
+
}
|
|
69
|
+
else if (typeof metadataCert === 'string') {
|
|
70
|
+
metadataCert = [metadataCert];
|
|
71
|
+
}
|
|
72
|
+
metadataCert = metadataCert.map(utility.normalizeCerString);
|
|
73
|
+
// 没有证书的情况
|
|
74
|
+
if (certificateNode.length === 0 && metadataCert.length === 0) {
|
|
75
|
+
throw new Error('NO_SELECTED_CERTIFICATE');
|
|
76
|
+
}
|
|
77
|
+
if (certificateNode.length !== 0) {
|
|
78
|
+
const x509CertificateData = certificateNode[0].firstChild.data;
|
|
79
|
+
const x509Certificate = utility.normalizeCerString(x509CertificateData);
|
|
80
|
+
if (metadataCert.length >= 1 && !metadataCert.includes(x509Certificate)) {
|
|
81
|
+
throw new Error('ERROR_UNMATCH_CERTIFICATE_DECLARATION_IN_METADATA');
|
|
82
|
+
}
|
|
83
|
+
sig.publicCert = libsaml.getKeyInfo(x509Certificate).getKey();
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
sig.publicCert = libsaml.getKeyInfo(metadataCert[0]).getKey();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
sig.loadSignature(signatureNode);
|
|
90
|
+
verified = sig.checkSignature(xml); // 使用原始XML验证
|
|
91
|
+
if (!verified) {
|
|
92
|
+
throw new Error('ERR_FAILED_TO_VERIFY_SIGNATURE');
|
|
93
|
+
}
|
|
94
|
+
if (sig.getSignedReferences().length < 1) {
|
|
95
|
+
throw new Error('NO_SIGNATURE_REFERENCES');
|
|
96
|
+
}
|
|
97
|
+
const signedVerifiedXML = sig.getSignedReferences()[0];
|
|
98
|
+
const rootNode = docParser.parseFromString(signedVerifiedXML, 'application/xml').documentElement;
|
|
99
|
+
// 处理签名的内容
|
|
100
|
+
switch (rootNode?.localName) {
|
|
101
|
+
case 'ArtifactResolve':
|
|
102
|
+
return [true, rootNode.toString(), false, false];
|
|
103
|
+
case 'ArtifactResponse':
|
|
104
|
+
// @ts-expect-error
|
|
105
|
+
const Response = select("/*[local-name()='ArtifactResponse']/*[local-name()='Response']", rootNode);
|
|
106
|
+
return [true, Response?.[0].toString(), false, false]; // 签名验证成功但未找到断言
|
|
107
|
+
default:
|
|
108
|
+
return [true, null, false, true]; // 签名验证成功但未找到可识别的内容
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return [false, null, false, true];
|
|
112
|
+
}
|
|
113
|
+
export default {
|
|
114
|
+
verifyAndDecryptSoapMessage
|
|
115
|
+
};
|
package/build/src/schema/xml.xsd
CHANGED
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
<?xml version="1.0"?>
|
|
2
|
-
<!-- DOCTYPE xs:schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" "XMLSchema.dtd" -->
|
|
3
|
-
<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
|
|
4
|
-
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
|
5
|
-
xml:lang="en">
|
|
6
|
-
|
|
7
|
-
<xs:annotation>
|
|
8
|
-
<xs:documentation>
|
|
9
|
-
See http://www.w3.org/XML/1998/namespace.html and
|
|
10
|
-
http://www.w3.org/TR/REC-xml for information about this namespace.
|
|
11
|
-
</xs:documentation>
|
|
12
|
-
</xs:annotation>
|
|
13
|
-
|
|
14
|
-
<xs:annotation>
|
|
15
|
-
<xs:documentation>
|
|
16
|
-
This schema defines attributes and an attribute group
|
|
17
|
-
suitable for use by schemas wishing to allow xml:base,
|
|
18
|
-
xml:lang or xml:space attributes on elements they define.
|
|
19
|
-
To enable this, such a schema must import this schema
|
|
20
|
-
for the XML namespace, e.g. as follows:
|
|
21
|
-
<schema . . .>
|
|
22
|
-
. . .
|
|
23
|
-
<import namespace="http://www.w3.org/XML/1998/namespace"
|
|
24
|
-
schemaLocation="http://www.w3.org/2001/03/xml.xsd"/>
|
|
25
|
-
|
|
26
|
-
Subsequently, qualified reference to any of the attributes
|
|
27
|
-
or the group defined below will have the desired effect, e.g.
|
|
28
|
-
|
|
29
|
-
<type . . .>
|
|
30
|
-
. . .
|
|
31
|
-
<attributeGroup ref="xml:specialAttrs"/>
|
|
32
|
-
|
|
33
|
-
will define a type which will schema-validate an instance
|
|
34
|
-
element with any of those attributes
|
|
35
|
-
</xs:documentation>
|
|
36
|
-
</xs:annotation>
|
|
37
|
-
|
|
38
|
-
<xs:annotation>
|
|
39
|
-
<xs:documentation>
|
|
40
|
-
In keeping with the XML Schema WG's standard versioning
|
|
41
|
-
policy, this schema document will persist at
|
|
42
|
-
http://www.w3.org/2001/03/xml.xsd.
|
|
43
|
-
At the date of issue it can also be found at
|
|
44
|
-
http://www.w3.org/2001/xml.xsd.
|
|
45
|
-
The schema document at that URI may however change in the future,
|
|
46
|
-
in order to remain compatible with the latest version of XML Schema
|
|
47
|
-
itself. In other words, if the XML Schema namespace changes, the version
|
|
48
|
-
of this document at
|
|
49
|
-
http://www.w3.org/2001/xml.xsd will change
|
|
50
|
-
accordingly; the version at
|
|
51
|
-
http://www.w3.org/2001/03/xml.xsd will not change.
|
|
52
|
-
</xs:documentation>
|
|
53
|
-
</xs:annotation>
|
|
54
|
-
|
|
55
|
-
<xs:attribute name="lang" type="xs:language">
|
|
56
|
-
<xs:annotation>
|
|
57
|
-
<xs:documentation>
|
|
58
|
-
In due course, we should install the relevant ISO 2- and 3-letter
|
|
59
|
-
codes as the enumerated possible values . . .
|
|
60
|
-
</xs:documentation>
|
|
61
|
-
</xs:annotation>
|
|
62
|
-
</xs:attribute>
|
|
63
|
-
|
|
64
|
-
<xs:attribute name="space" default="preserve">
|
|
65
|
-
<xs:simpleType>
|
|
66
|
-
<xs:restriction base="xs:NCName">
|
|
67
|
-
<xs:enumeration value="default"/>
|
|
68
|
-
<xs:enumeration value="preserve"/>
|
|
69
|
-
</xs:restriction>
|
|
70
|
-
</xs:simpleType>
|
|
71
|
-
</xs:attribute>
|
|
72
|
-
|
|
73
|
-
<xs:attribute name="base" type="xs:anyURI">
|
|
74
|
-
<xs:annotation>
|
|
75
|
-
<xs:documentation>
|
|
76
|
-
See http://www.w3.org/TR/xmlbase/ for
|
|
77
|
-
information about this attribute.
|
|
78
|
-
</xs:documentation>
|
|
79
|
-
</xs:annotation>
|
|
80
|
-
</xs:attribute>
|
|
81
|
-
|
|
82
|
-
<xs:attributeGroup name="specialAttrs">
|
|
83
|
-
<xs:attribute ref="xml:base"/>
|
|
84
|
-
<xs:attribute ref="xml:lang"/>
|
|
85
|
-
<xs:attribute ref="xml:space"/>
|
|
86
|
-
</xs:attributeGroup>
|
|
87
|
-
|
|
88
|
-
</xs:schema>
|
|
1
|
+
<?xml version="1.0"?>
|
|
2
|
+
<!-- DOCTYPE xs:schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" "XMLSchema.dtd" -->
|
|
3
|
+
<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
|
|
4
|
+
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
|
5
|
+
xml:lang="en">
|
|
6
|
+
|
|
7
|
+
<xs:annotation>
|
|
8
|
+
<xs:documentation>
|
|
9
|
+
See http://www.w3.org/XML/1998/namespace.html and
|
|
10
|
+
http://www.w3.org/TR/REC-xml for information about this namespace.
|
|
11
|
+
</xs:documentation>
|
|
12
|
+
</xs:annotation>
|
|
13
|
+
|
|
14
|
+
<xs:annotation>
|
|
15
|
+
<xs:documentation>
|
|
16
|
+
This schema defines attributes and an attribute group
|
|
17
|
+
suitable for use by schemas wishing to allow xml:base,
|
|
18
|
+
xml:lang or xml:space attributes on elements they define.
|
|
19
|
+
To enable this, such a schema must import this schema
|
|
20
|
+
for the XML namespace, e.g. as follows:
|
|
21
|
+
<schema . . .>
|
|
22
|
+
. . .
|
|
23
|
+
<import namespace="http://www.w3.org/XML/1998/namespace"
|
|
24
|
+
schemaLocation="http://www.w3.org/2001/03/xml.xsd"/>
|
|
25
|
+
|
|
26
|
+
Subsequently, qualified reference to any of the attributes
|
|
27
|
+
or the group defined below will have the desired effect, e.g.
|
|
28
|
+
|
|
29
|
+
<type . . .>
|
|
30
|
+
. . .
|
|
31
|
+
<attributeGroup ref="xml:specialAttrs"/>
|
|
32
|
+
|
|
33
|
+
will define a type which will schema-validate an instance
|
|
34
|
+
element with any of those attributes
|
|
35
|
+
</xs:documentation>
|
|
36
|
+
</xs:annotation>
|
|
37
|
+
|
|
38
|
+
<xs:annotation>
|
|
39
|
+
<xs:documentation>
|
|
40
|
+
In keeping with the XML Schema WG's standard versioning
|
|
41
|
+
policy, this schema document will persist at
|
|
42
|
+
http://www.w3.org/2001/03/xml.xsd.
|
|
43
|
+
At the date of issue it can also be found at
|
|
44
|
+
http://www.w3.org/2001/xml.xsd.
|
|
45
|
+
The schema document at that URI may however change in the future,
|
|
46
|
+
in order to remain compatible with the latest version of XML Schema
|
|
47
|
+
itself. In other words, if the XML Schema namespace changes, the version
|
|
48
|
+
of this document at
|
|
49
|
+
http://www.w3.org/2001/xml.xsd will change
|
|
50
|
+
accordingly; the version at
|
|
51
|
+
http://www.w3.org/2001/03/xml.xsd will not change.
|
|
52
|
+
</xs:documentation>
|
|
53
|
+
</xs:annotation>
|
|
54
|
+
|
|
55
|
+
<xs:attribute name="lang" type="xs:language">
|
|
56
|
+
<xs:annotation>
|
|
57
|
+
<xs:documentation>
|
|
58
|
+
In due course, we should install the relevant ISO 2- and 3-letter
|
|
59
|
+
codes as the enumerated possible values . . .
|
|
60
|
+
</xs:documentation>
|
|
61
|
+
</xs:annotation>
|
|
62
|
+
</xs:attribute>
|
|
63
|
+
|
|
64
|
+
<xs:attribute name="space" default="preserve">
|
|
65
|
+
<xs:simpleType>
|
|
66
|
+
<xs:restriction base="xs:NCName">
|
|
67
|
+
<xs:enumeration value="default"/>
|
|
68
|
+
<xs:enumeration value="preserve"/>
|
|
69
|
+
</xs:restriction>
|
|
70
|
+
</xs:simpleType>
|
|
71
|
+
</xs:attribute>
|
|
72
|
+
|
|
73
|
+
<xs:attribute name="base" type="xs:anyURI">
|
|
74
|
+
<xs:annotation>
|
|
75
|
+
<xs:documentation>
|
|
76
|
+
See http://www.w3.org/TR/xmlbase/ for
|
|
77
|
+
information about this attribute.
|
|
78
|
+
</xs:documentation>
|
|
79
|
+
</xs:annotation>
|
|
80
|
+
</xs:attribute>
|
|
81
|
+
|
|
82
|
+
<xs:attributeGroup name="specialAttrs">
|
|
83
|
+
<xs:attribute ref="xml:base"/>
|
|
84
|
+
<xs:attribute ref="xml:lang"/>
|
|
85
|
+
<xs:attribute ref="xml:space"/>
|
|
86
|
+
</xs:attributeGroup>
|
|
87
|
+
|
|
88
|
+
</xs:schema>
|
|
@@ -4,9 +4,7 @@ import * as path from 'node:path';
|
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
|
-
let
|
|
8
|
-
'soap-envelope.xsd',
|
|
9
|
-
'xml.xsd',
|
|
7
|
+
let normal = [
|
|
10
8
|
'saml-schema-protocol-2.0.xsd',
|
|
11
9
|
'saml-schema-assertion-2.0.xsd',
|
|
12
10
|
'xmldsig-core-schema.xsd',
|
|
@@ -15,7 +13,7 @@ let obj = [
|
|
|
15
13
|
'saml-schema-ecp-2.0.xsd',
|
|
16
14
|
'saml-schema-dce-2.0.xsd'
|
|
17
15
|
];
|
|
18
|
-
let
|
|
16
|
+
let soapSchema = [
|
|
19
17
|
'soap-envelope.xsd',
|
|
20
18
|
'xml.xsd',
|
|
21
19
|
// 2. SOAP核心模式(所有SOAP消息的基础)
|
|
@@ -32,7 +30,7 @@ let normal = [
|
|
|
32
30
|
'saml-schema-ecp-2.0.xsd', // ECP扩展
|
|
33
31
|
'saml-schema-dce-2.0.xsd' // DCE扩展
|
|
34
32
|
];
|
|
35
|
-
|
|
33
|
+
let schemas = normal;
|
|
36
34
|
function detectXXEIndicators(samlString) {
|
|
37
35
|
const xxePatterns = [
|
|
38
36
|
/<!DOCTYPE\s[^>]*>/i,
|
|
@@ -55,12 +53,12 @@ function detectXXEIndicators(samlString) {
|
|
|
55
53
|
});
|
|
56
54
|
return Object.keys(matches).length > 0 ? matches : null;
|
|
57
55
|
}
|
|
58
|
-
export const validate = async (xml) => {
|
|
56
|
+
export const validate = async (xml, isSoap = false) => {
|
|
59
57
|
const indicators = detectXXEIndicators(xml);
|
|
60
58
|
if (indicators) {
|
|
61
|
-
console.error('XXE风险特征:', indicators);
|
|
62
59
|
throw new Error('ERR_EXCEPTION_VALIDATE_XML');
|
|
63
60
|
}
|
|
61
|
+
schemas = isSoap ? soapSchema : normal;
|
|
64
62
|
const schemaPath = path.resolve(__dirname, 'schema');
|
|
65
63
|
const [xmlParse, ...preload] = await Promise.all(schemas.map(async (file) => ({
|
|
66
64
|
fileName: file,
|
|
@@ -79,17 +77,11 @@ export const validate = async (xml) => {
|
|
|
79
77
|
preload: [xmlParse, ...preload],
|
|
80
78
|
});
|
|
81
79
|
if (validationResult.valid) {
|
|
82
|
-
console.log("---------------------验证通过--------------------");
|
|
83
|
-
console.log("---------------------验证通过--------------------");
|
|
84
80
|
return true;
|
|
85
81
|
}
|
|
86
|
-
console.log('-----------------------没验证通过-----------------------');
|
|
87
|
-
console.debug(validationResult);
|
|
88
82
|
throw validationResult.errors;
|
|
89
83
|
}
|
|
90
84
|
catch (error) {
|
|
91
|
-
console.log('-----------------------没验证通过error-----------------------');
|
|
92
|
-
console.error('[ERROR] validateXML', error);
|
|
93
85
|
throw new Error('ERR_EXCEPTION_VALIDATE_XML');
|
|
94
86
|
}
|
|
95
87
|
};
|
package/build/src/soap.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import https from 'node:https';
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
import { Builder } from 'xml2js';
|
|
5
|
+
import iconv from 'iconv-lite';
|
|
3
6
|
// 2. 配置 Axios 实例(处理自签名证书)
|
|
4
7
|
const axiosInstance = axios.create({
|
|
5
8
|
httpsAgent: new https.Agent({
|
|
@@ -10,16 +13,133 @@ export async function sendArtifactResolve(url, soapRequest) {
|
|
|
10
13
|
try {
|
|
11
14
|
const response = await axiosInstance.post(url, soapRequest, {
|
|
12
15
|
headers: {
|
|
13
|
-
'Content-Type': '
|
|
16
|
+
'Content-Type': 'text/xml',
|
|
14
17
|
'SOAPAction': '"ArtifactResolve"'
|
|
15
18
|
},
|
|
16
19
|
timeout: 5000 // 5秒超时
|
|
17
20
|
});
|
|
18
|
-
console.log('✅ Resolve请求成功');
|
|
19
21
|
return response.data;
|
|
20
22
|
}
|
|
21
23
|
catch (error) {
|
|
22
|
-
console.error('❌ Resolve请求失败');
|
|
23
24
|
throw error.response.data;
|
|
24
25
|
}
|
|
25
26
|
}
|
|
27
|
+
export async function sendArtifactResponse(url, soapRequest) {
|
|
28
|
+
try {
|
|
29
|
+
const response = await axiosInstance.post(url, soapRequest, {
|
|
30
|
+
headers: {
|
|
31
|
+
'Content-Type': 'text/xml',
|
|
32
|
+
'SOAPAction': '"ArtifactResponse"'
|
|
33
|
+
},
|
|
34
|
+
timeout: 5000 // 5秒超时
|
|
35
|
+
});
|
|
36
|
+
return response.data;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
throw error.response.data;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* @desc generate Art id
|
|
44
|
+
*
|
|
45
|
+
* @param entityIDString
|
|
46
|
+
* @param endpointIndex
|
|
47
|
+
*/
|
|
48
|
+
export function createArt(entityIDString, endpointIndex = 0) {
|
|
49
|
+
// 安全获取 sourceEntityId
|
|
50
|
+
let sourceEntityId;
|
|
51
|
+
if (typeof entityIDString === "string") {
|
|
52
|
+
sourceEntityId = entityIDString;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// 确保只在非字符串类型上访问 entityMeta
|
|
56
|
+
sourceEntityId = entityIDString.entityMeta.getEntityID();
|
|
57
|
+
}
|
|
58
|
+
// 1. 固定类型代码 (0x0004 - 2字节)
|
|
59
|
+
const typeCode = Buffer.from([0x00, 0x04]);
|
|
60
|
+
// 2. 端点索引 (2字节,大端序)
|
|
61
|
+
if (endpointIndex < 0 || endpointIndex > 65535) {
|
|
62
|
+
throw new Error("Endpoint index must be between 0 and 65535");
|
|
63
|
+
}
|
|
64
|
+
const endpointBuf = Buffer.alloc(2);
|
|
65
|
+
endpointBuf.writeUInt16BE(endpointIndex);
|
|
66
|
+
// 3. Source ID - 实体ID的SHA-1哈希 (20字节)
|
|
67
|
+
const sourceId = crypto
|
|
68
|
+
.createHash("sha1")
|
|
69
|
+
.update(sourceEntityId)
|
|
70
|
+
.digest();
|
|
71
|
+
// 4. Message Handler - 20字节随机值
|
|
72
|
+
const messageHandler = crypto.randomBytes(20);
|
|
73
|
+
// 组合所有组件 (2+2+20+20 = 44字节)
|
|
74
|
+
const artifact = Buffer.concat([
|
|
75
|
+
typeCode,
|
|
76
|
+
endpointBuf,
|
|
77
|
+
sourceId,
|
|
78
|
+
messageHandler,
|
|
79
|
+
]);
|
|
80
|
+
// 返回Base64编码的Artifact
|
|
81
|
+
return {
|
|
82
|
+
artifact: artifact.toString("base64"),
|
|
83
|
+
origin: {
|
|
84
|
+
typeCode: typeCode.readUInt16BE(0), // 改为整数值
|
|
85
|
+
endpointIndex: endpointIndex, // 修复字段名并赋正确的值
|
|
86
|
+
sourceId: sourceId.toString("hex"), // 转为十六进制
|
|
87
|
+
messageHandle: messageHandler.toString("hex"), // 转为十六进制
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* @desc generate Art id
|
|
93
|
+
* @param artifact
|
|
94
|
+
*/
|
|
95
|
+
export function parseArt(artifact) {
|
|
96
|
+
// 解码 Base64
|
|
97
|
+
console.log(Object.prototype.toString.call(artifact));
|
|
98
|
+
if (Object.prototype.toString.call(artifact) !== '[object String]') {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const decoded = Buffer.from(artifact, 'base64');
|
|
102
|
+
// 确保长度正确(SAML 工件固定为 44 字节)
|
|
103
|
+
if (decoded.length !== 44) {
|
|
104
|
+
throw new Error(`Invalid artifact length: ${decoded.length}, expected 44 bytes`);
|
|
105
|
+
}
|
|
106
|
+
// 读取前 4 字节(TypeCode + EndpointIndex)
|
|
107
|
+
const typeCode = decoded.readUInt16BE(0);
|
|
108
|
+
const endpointIndex = decoded.readUInt16BE(2);
|
|
109
|
+
// 使用 Buffer.from() 替代 slice()
|
|
110
|
+
const sourceId = Buffer.from(decoded.buffer, // 底层 ArrayBuffer
|
|
111
|
+
decoded.byteOffset + 4, // 起始偏移量
|
|
112
|
+
20 // 长度
|
|
113
|
+
).toString('hex');
|
|
114
|
+
const messageHandle = Buffer.from(decoded.buffer, // 底层 ArrayBuffer
|
|
115
|
+
decoded.byteOffset + 24, // 起始偏移量
|
|
116
|
+
20 // 长度
|
|
117
|
+
).toString('hex');
|
|
118
|
+
return { typeCode, endpointIndex, sourceId, messageHandle };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 将对象转换为 ISO-8859-1 编码的 XML 字符串
|
|
122
|
+
* @param {Object} data - 要转换的数据对象
|
|
123
|
+
* @returns {Buffer} - ISO-8859-1 编码的 XML 数据 (Buffer)
|
|
124
|
+
*/
|
|
125
|
+
export function encodeXmlToIso88591(data) {
|
|
126
|
+
try {
|
|
127
|
+
// 1. 创建 XML 构建器
|
|
128
|
+
const builder = new Builder({
|
|
129
|
+
headless: false, // 包含 XML 声明
|
|
130
|
+
renderOpts: { 'pretty': false }, // 紧凑格式
|
|
131
|
+
xmldec: {
|
|
132
|
+
version: '1.0',
|
|
133
|
+
encoding: 'ISO-8859-1',
|
|
134
|
+
standalone: true
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
// 2. 构建 XML 字符串 (UTF-8 格式)
|
|
138
|
+
const utf8Xml = builder.buildObject(data);
|
|
139
|
+
// 3. 转换为 ISO-8859-1 编码的 Buffer
|
|
140
|
+
return iconv.encode(utf8Xml, 'iso-8859-1');
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
throw new Error(`XML 编码失败: ${error.message}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
package/build/src/utility.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* @author tngan
|
|
4
4
|
* @desc Library for some common functions (e.g. de/inflation, en/decoding)
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { createPrivateKey, X509Certificate } from 'node:crypto';
|
|
7
|
+
import { deflateRaw, inflateRaw } from 'pako';
|
|
8
8
|
const BASE64_STR = 'base64';
|
|
9
9
|
/**
|
|
10
10
|
* @desc Mimic lodash.zipObject
|
|
@@ -107,11 +107,16 @@ function deflateString(message) {
|
|
|
107
107
|
* @return {string} decompressed string
|
|
108
108
|
*/
|
|
109
109
|
export function inflateString(compressedString) {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
const base64Encoded = decodeURIComponent(compressedString);
|
|
111
|
+
// 2. Base64解码为Uint8Array
|
|
112
|
+
const binaryStr = atob(base64Encoded) ?? base64Encoded;
|
|
113
|
+
const data = Uint8Array.from(binaryStr, (c) => c.charCodeAt(0));
|
|
114
|
+
try {
|
|
115
|
+
return inflateRaw(data, { to: 'string' });
|
|
116
|
+
}
|
|
117
|
+
catch (e) {
|
|
118
|
+
return e.message;
|
|
119
|
+
}
|
|
115
120
|
}
|
|
116
121
|
/**
|
|
117
122
|
* @desc Abstract the normalizeCerString and normalizePemString
|