samlesa 2.16.5 → 2.16.6
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/build/src/binding-redirect.js +97 -2
- package/build/src/entity-sp.js +117 -23
- package/build/src/extractor.js +13 -0
- package/build/src/flow.js +21 -35
- package/build/src/libsaml.js +8 -4
- package/build/src/metadata-sp.js +2 -0
- package/build/src/metadata.js +0 -2
- package/build/src/schema/saml-schema-ecp-2.0.xsd +1 -1
- package/build/src/schema/saml-schema-metadata-2.0.xsd +3 -3
- package/build/src/schema/saml-schema-protocol-2.0.xsd +1 -1
- package/build/src/schema/{env.xsd → soap-envelope.xsd} +1 -33
- package/build/src/schema/xml.xsd +88 -0
- package/build/src/schemaValidator.js +29 -8
- package/package.json +1 -1
- package/types/src/binding-redirect.d.ts +14 -1
- package/types/src/binding-redirect.d.ts.map +1 -1
- package/types/src/entity-sp.d.ts +44 -21
- package/types/src/entity-sp.d.ts.map +1 -1
- package/types/src/extractor.d.ts +5 -0
- package/types/src/extractor.d.ts.map +1 -1
- package/types/src/flow.d.ts.map +1 -1
- package/types/src/libsaml.d.ts +3 -0
- package/types/src/libsaml.d.ts.map +1 -1
- package/types/src/metadata-sp.d.ts.map +1 -1
- package/types/src/metadata.d.ts.map +1 -1
- package/types/src/schemaValidator.d.ts.map +1 -1
- package/build/index.js.map +0 -1
- package/build/src/api.js.map +0 -1
- package/build/src/binding-post.js.map +0 -1
- package/build/src/binding-redirect.js.map +0 -1
- package/build/src/binding-simplesign.js.map +0 -1
- package/build/src/entity-idp.js.map +0 -1
- package/build/src/entity-sp.js.map +0 -1
- package/build/src/entity.js.map +0 -1
- package/build/src/extractor.js.map +0 -1
- package/build/src/flow.js.map +0 -1
- package/build/src/libsaml.js.map +0 -1
- package/build/src/metadata-idp.js.map +0 -1
- package/build/src/metadata-sp.js.map +0 -1
- package/build/src/metadata.js.map +0 -1
- package/build/src/types.js.map +0 -1
- package/build/src/urn.js.map +0 -1
- package/build/src/utility.js.map +0 -1
- package/build/src/validator.js.map +0 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import utility, { get } from './utility.js';
|
|
7
7
|
import libsaml from './libsaml.js';
|
|
8
|
-
import {
|
|
8
|
+
import { namespace, wording } from './urn.js';
|
|
9
9
|
const binding = wording.binding;
|
|
10
10
|
const urlParams = wording.urlParams;
|
|
11
11
|
/**
|
|
@@ -59,8 +59,9 @@ function buildRedirectURL(opts) {
|
|
|
59
59
|
* @param {function} customTagReplacement used when developers have their own login response template
|
|
60
60
|
* @return {string} redirect URL
|
|
61
61
|
*/
|
|
62
|
+
// @ts-ignore
|
|
62
63
|
function loginRequestRedirectURL(entity, customTagReplacement) {
|
|
63
|
-
const metadata = { idp: entity.idp.entityMeta, sp: entity.sp.entityMeta };
|
|
64
|
+
const metadata = { idp: entity.idp.entityMeta, sp: entity.sp.entityMeta, soap: entity.soap ?? false };
|
|
64
65
|
const spSetting = entity.sp.entitySetting;
|
|
65
66
|
let id = '';
|
|
66
67
|
if (metadata && metadata.idp && metadata.sp) {
|
|
@@ -100,6 +101,99 @@ function loginRequestRedirectURL(entity, customTagReplacement) {
|
|
|
100
101
|
}
|
|
101
102
|
throw new Error('ERR_GENERATE_REDIRECT_LOGIN_REQUEST_MISSING_METADATA');
|
|
102
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* @desc Redirect URL for login request
|
|
106
|
+
* @param {object} entity object includes both idp and sp
|
|
107
|
+
* @param {function} customTagReplacement used when developers have their own login response template
|
|
108
|
+
* @return {string} redirect URL
|
|
109
|
+
*/
|
|
110
|
+
// @ts-ignore
|
|
111
|
+
function loginRequestRedirectURLArt(entity, customTagReplacement) {
|
|
112
|
+
const metadata = { idp: entity.idp.entityMeta, sp: entity.sp.entityMeta, inResponse: entity.inResponse ?? false };
|
|
113
|
+
const spSetting = entity.sp.entitySetting;
|
|
114
|
+
let id = '';
|
|
115
|
+
if (metadata && metadata.idp && metadata.sp) {
|
|
116
|
+
const base = metadata.idp.getSingleSignOnService(binding.redirect);
|
|
117
|
+
let rawSamlRequest;
|
|
118
|
+
if (spSetting.loginRequestTemplate && customTagReplacement) {
|
|
119
|
+
const info = customTagReplacement(spSetting.loginRequestTemplate);
|
|
120
|
+
id = get(info, 'id', null);
|
|
121
|
+
rawSamlRequest = get(info, 'context', null);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const nameIDFormat = spSetting.nameIDFormat;
|
|
125
|
+
const selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
|
|
126
|
+
id = spSetting.generateID();
|
|
127
|
+
rawSamlRequest = libsaml.replaceTagsByValue(libsaml.defaultLoginRequestTemplate.context, {
|
|
128
|
+
ID: id,
|
|
129
|
+
Destination: base,
|
|
130
|
+
Issuer: metadata.sp.getEntityID(),
|
|
131
|
+
IssueInstant: new Date().toISOString(),
|
|
132
|
+
NameIDFormat: selectedNameIDFormat,
|
|
133
|
+
AssertionConsumerServiceURL: metadata.sp.getAssertionConsumerService(binding.post),
|
|
134
|
+
EntityID: metadata.sp.getEntityID(),
|
|
135
|
+
AllowCreate: spSetting.allowCreate,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
console.log(rawSamlRequest);
|
|
139
|
+
console.log("-----------------这是原始请求模板-------------------");
|
|
140
|
+
const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm, transformationAlgorithms } = spSetting;
|
|
141
|
+
if (metadata.idp.isWantAuthnRequestsSigned()) {
|
|
142
|
+
let signAuthnRequest = libsaml.constructSAMLSignature({
|
|
143
|
+
referenceTagXPath: "/*[local-name(.)='AuthnRequest']",
|
|
144
|
+
privateKey,
|
|
145
|
+
privateKeyPass,
|
|
146
|
+
signatureAlgorithm,
|
|
147
|
+
transformationAlgorithms,
|
|
148
|
+
isBase64Output: false,
|
|
149
|
+
rawSamlMessage: rawSamlRequest,
|
|
150
|
+
signingCert: metadata.sp.getX509Certificate('signing'),
|
|
151
|
+
signatureConfig: spSetting.signatureConfig || {
|
|
152
|
+
prefix: 'ds',
|
|
153
|
+
location: {
|
|
154
|
+
reference: "/*[local-name(.)='AuthnRequest']/*[local-name(.)='Issuer']",
|
|
155
|
+
action: 'after'
|
|
156
|
+
},
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
console.log(signAuthnRequest);
|
|
160
|
+
console.log("签名后的模板");
|
|
161
|
+
rawSamlRequest = signAuthnRequest;
|
|
162
|
+
}
|
|
163
|
+
/* console.log(metadata.idp)
|
|
164
|
+
console.log(entity.idp.getEntitySetting())*/
|
|
165
|
+
let soapTemplate = libsaml.replaceTagsByValue(libsaml.defaultArtAuthnRequestTemplate.context, {
|
|
166
|
+
ID: id,
|
|
167
|
+
IssueInstant: new Date().toISOString(),
|
|
168
|
+
InResponseTo: metadata.inResponse ?? "",
|
|
169
|
+
Issuer: metadata.sp.getEntityID(),
|
|
170
|
+
AuthnRequest: rawSamlRequest
|
|
171
|
+
});
|
|
172
|
+
console.log(soapTemplate);
|
|
173
|
+
console.log("======================最后结果========================");
|
|
174
|
+
console.log("======================开始签名根节点========================");
|
|
175
|
+
let rootSignSoap = libsaml.constructSAMLSignature({
|
|
176
|
+
isMessageSigned: true,
|
|
177
|
+
isBase64Output: false,
|
|
178
|
+
privateKey,
|
|
179
|
+
privateKeyPass,
|
|
180
|
+
signatureAlgorithm,
|
|
181
|
+
transformationAlgorithms,
|
|
182
|
+
rawSamlMessage: soapTemplate,
|
|
183
|
+
signingCert: metadata.sp.getX509Certificate('signing'),
|
|
184
|
+
signatureConfig: {
|
|
185
|
+
prefix: 'ds',
|
|
186
|
+
location: { reference: "//*[local-name()='Header']", action: 'after' },
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
console.log(rootSignSoap);
|
|
190
|
+
console.log("======================已经签名========================");
|
|
191
|
+
return {
|
|
192
|
+
authnRequest: rootSignSoap
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
throw new Error('ERR_GENERATE_REDIRECT_LOGIN_REQUEST_MISSING_METADATA');
|
|
196
|
+
}
|
|
103
197
|
/**
|
|
104
198
|
* @desc Redirect URL for login response
|
|
105
199
|
* @param {object} requestInfo corresponding request, used to obtain the id
|
|
@@ -304,6 +398,7 @@ function logoutResponseRedirectURL(requestInfo, entity, relayState, customTagRep
|
|
|
304
398
|
throw new Error('ERR_GENERATE_REDIRECT_LOGOUT_RESPONSE_MISSING_METADATA');
|
|
305
399
|
}
|
|
306
400
|
const redirectBinding = {
|
|
401
|
+
loginRequestRedirectURLArt,
|
|
307
402
|
loginRequestRedirectURL,
|
|
308
403
|
loginResponseRedirectURL,
|
|
309
404
|
logoutRequestRedirectURL,
|
package/build/src/entity-sp.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file entity-sp.ts
|
|
3
|
-
* @author tngan
|
|
4
|
-
* @desc Declares the actions taken by service provider
|
|
5
|
-
*/
|
|
2
|
+
* @file entity-sp.ts
|
|
3
|
+
* @author tngan
|
|
4
|
+
* @desc Declares the actions taken by service provider
|
|
5
|
+
*/
|
|
6
6
|
import Entity from './entity.js';
|
|
7
|
+
import * as crypto from "node:crypto";
|
|
7
8
|
import { namespace } from './urn.js';
|
|
8
9
|
import redirectBinding from './binding-redirect.js';
|
|
9
10
|
import postBinding from './binding-post.js';
|
|
@@ -17,15 +18,15 @@ export default function (props) {
|
|
|
17
18
|
return new ServiceProvider(props);
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
20
|
-
* @desc Service provider can be configured using either metadata importing or spSetting
|
|
21
|
-
* @param {object} spSettingimport { FlowResult } from '../types/src/flow.d';
|
|
21
|
+
* @desc Service provider can be configured using either metadata importing or spSetting
|
|
22
|
+
* @param {object} spSettingimport { FlowResult } from '../types/src/flow.d';
|
|
22
23
|
|
|
23
|
-
*/
|
|
24
|
+
*/
|
|
24
25
|
export class ServiceProvider extends Entity {
|
|
25
26
|
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
* @desc Inherited from Entity
|
|
28
|
+
* @param {object} spSetting setting of service provider
|
|
29
|
+
*/
|
|
29
30
|
constructor(spSetting) {
|
|
30
31
|
const entitySetting = Object.assign({
|
|
31
32
|
authnRequestsSigned: false,
|
|
@@ -35,11 +36,11 @@ export class ServiceProvider extends Entity {
|
|
|
35
36
|
super(entitySetting, 'sp');
|
|
36
37
|
}
|
|
37
38
|
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
* @desc Generates the login request for developers to design their own method
|
|
40
|
+
* @param {IdentityProvider} idp object of identity provider
|
|
41
|
+
* @param {string} binding protocol binding
|
|
42
|
+
* @param {function} customTagReplacement used when developers have their own login response template
|
|
43
|
+
*/
|
|
43
44
|
createLoginRequest(idp, binding = 'redirect', customTagReplacement) {
|
|
44
45
|
const nsBinding = namespace.binding;
|
|
45
46
|
const protocol = nsBinding[binding];
|
|
@@ -51,14 +52,20 @@ export class ServiceProvider extends Entity {
|
|
|
51
52
|
case nsBinding.redirect:
|
|
52
53
|
return redirectBinding.loginRequestRedirectURL({ idp, sp: this }, customTagReplacement);
|
|
53
54
|
case nsBinding.post:
|
|
54
|
-
context = postBinding.base64LoginRequest("/*[local-name(.)='AuthnRequest']", {
|
|
55
|
+
context = postBinding.base64LoginRequest("/*[local-name(.)='AuthnRequest']", {
|
|
56
|
+
idp,
|
|
57
|
+
sp: this
|
|
58
|
+
}, customTagReplacement);
|
|
55
59
|
break;
|
|
56
60
|
case nsBinding.simpleSign:
|
|
57
61
|
// Object context = {id, context, signature, sigAlg}
|
|
58
62
|
context = simpleSignBinding.base64LoginRequest({ idp, sp: this }, customTagReplacement);
|
|
59
63
|
break;
|
|
60
64
|
case nsBinding.artifact:
|
|
61
|
-
context = artifactSignBinding.base64LoginRequest("/*[local-name(.)='AuthnRequest']", {
|
|
65
|
+
context = artifactSignBinding.base64LoginRequest("/*[local-name(.)='AuthnRequest']", {
|
|
66
|
+
idp,
|
|
67
|
+
sp: this
|
|
68
|
+
}, customTagReplacement);
|
|
62
69
|
break;
|
|
63
70
|
default:
|
|
64
71
|
// Will support artifact in the next release
|
|
@@ -72,11 +79,45 @@ export class ServiceProvider extends Entity {
|
|
|
72
79
|
};
|
|
73
80
|
}
|
|
74
81
|
/**
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
* @desc Generates the Art login request for developers to design their own method
|
|
83
|
+
* @param {IdentityProvider} idp object of identity provider
|
|
84
|
+
* @param {string} binding protocol binding
|
|
85
|
+
* @param {function} customTagReplacement used when developers have their own login response template
|
|
86
|
+
*/
|
|
87
|
+
createLoginRequestArt(idp, binding = 'redirect', customTagReplacement) {
|
|
88
|
+
const nsBinding = namespace.binding;
|
|
89
|
+
const protocol = nsBinding[binding];
|
|
90
|
+
if (this.entityMeta.isAuthnRequestSigned() !== idp.entityMeta.isWantAuthnRequestsSigned()) {
|
|
91
|
+
throw new Error('ERR_METADATA_CONFLICT_REQUEST_SIGNED_FLAG');
|
|
92
|
+
}
|
|
93
|
+
let context = null;
|
|
94
|
+
switch (protocol) {
|
|
95
|
+
case nsBinding.redirect:
|
|
96
|
+
return redirectBinding.loginRequestRedirectURLArt({ idp, sp: this }, customTagReplacement);
|
|
97
|
+
case nsBinding.post:
|
|
98
|
+
context = postBinding.base64LoginRequest("/*[local-name(.)='AuthnRequest']", {
|
|
99
|
+
idp,
|
|
100
|
+
sp: this,
|
|
101
|
+
soap: true
|
|
102
|
+
}, customTagReplacement);
|
|
103
|
+
break;
|
|
104
|
+
default:
|
|
105
|
+
// Will support artifact in the next release
|
|
106
|
+
throw new Error('ERR_SP_LOGIN_REQUEST_UNDEFINED_BINDING');
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
...context,
|
|
110
|
+
relayState: this.entitySetting.relayState,
|
|
111
|
+
entityEndpoint: idp.entityMeta.getSingleSignOnService(binding),
|
|
112
|
+
type: 'SAMLRequest',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* @desc Validation of the parsed the URL parameters
|
|
117
|
+
* @param {IdentityProvider} idp object of identity provider
|
|
118
|
+
* @param {string} binding protocol binding
|
|
119
|
+
* @param {request} req request
|
|
120
|
+
*/
|
|
80
121
|
parseLoginResponse(idp, binding, request) {
|
|
81
122
|
const self = this;
|
|
82
123
|
return flow({
|
|
@@ -95,7 +136,7 @@ export class ServiceProvider extends Entity {
|
|
|
95
136
|
* @param {string} binding protocol binding
|
|
96
137
|
* @param {request} req request
|
|
97
138
|
*/
|
|
98
|
-
|
|
139
|
+
parseLoginResponseArt(idp, binding, request) {
|
|
99
140
|
const self = this;
|
|
100
141
|
return flow({
|
|
101
142
|
soap: true,
|
|
@@ -108,4 +149,57 @@ export class ServiceProvider extends Entity {
|
|
|
108
149
|
request: request
|
|
109
150
|
});
|
|
110
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* @desc generate Art id
|
|
154
|
+
*
|
|
155
|
+
* @param entityIDString
|
|
156
|
+
*/
|
|
157
|
+
createArt(entityIDString, endpointIndex = 0) {
|
|
158
|
+
let sourceEntityId = entityIDString ? entityIDString : this.entityMeta.getEntityID();
|
|
159
|
+
console.log(sourceEntityId);
|
|
160
|
+
console.log("0000000000000000000000000000000000000000");
|
|
161
|
+
// 1. 固定类型代码 (0x0004 - 2字节)
|
|
162
|
+
const typeCode = Buffer.from([0x00, 0x04]);
|
|
163
|
+
// 2. 端点索引 (2字节,大端序)
|
|
164
|
+
if (endpointIndex < 0 || endpointIndex > 65535) {
|
|
165
|
+
throw new Error('Endpoint index must be between 0 and 65535');
|
|
166
|
+
}
|
|
167
|
+
const endpointBuf = Buffer.alloc(2);
|
|
168
|
+
endpointBuf.writeUInt16BE(endpointIndex);
|
|
169
|
+
// 3. Source ID - 实体ID的SHA-1哈希 (20字节)
|
|
170
|
+
const sourceId = crypto.createHash('sha1')
|
|
171
|
+
.update(sourceEntityId)
|
|
172
|
+
.digest();
|
|
173
|
+
// 4. Message Handler - 20字节随机值
|
|
174
|
+
const messageHandler = crypto.randomBytes(20);
|
|
175
|
+
// 组合所有组件 (2+2+20+20 = 44字节)
|
|
176
|
+
const artifact = Buffer.concat([typeCode, endpointBuf, sourceId, messageHandler]);
|
|
177
|
+
// 返回Base64编码的Artifact
|
|
178
|
+
return artifact.toString('base64');
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* @desc generate Art id
|
|
182
|
+
* @param artifact
|
|
183
|
+
*/
|
|
184
|
+
parseArt(artifact) {
|
|
185
|
+
// 解码 Base64
|
|
186
|
+
const decoded = Buffer.from(artifact, 'base64');
|
|
187
|
+
// 确保长度正确(SAML 工件固定为 44 字节)
|
|
188
|
+
if (decoded.length !== 44) {
|
|
189
|
+
throw new Error(`Invalid artifact length: ${decoded.length}, expected 44 bytes`);
|
|
190
|
+
}
|
|
191
|
+
// 读取前 4 字节(TypeCode + EndpointIndex)
|
|
192
|
+
const typeCode = decoded.readUInt16BE(0);
|
|
193
|
+
const endpointIndex = decoded.readUInt16BE(2);
|
|
194
|
+
// 使用 Buffer.from() 替代 slice()
|
|
195
|
+
const sourceId = Buffer.from(decoded.buffer, // 底层 ArrayBuffer
|
|
196
|
+
decoded.byteOffset + 4, // 起始偏移量
|
|
197
|
+
20 // 长度
|
|
198
|
+
).toString('hex');
|
|
199
|
+
const messageHandle = Buffer.from(decoded.buffer, // 底层 ArrayBuffer
|
|
200
|
+
decoded.byteOffset + 24, // 起始偏移量
|
|
201
|
+
20 // 长度
|
|
202
|
+
).toString('hex');
|
|
203
|
+
return { typeCode, endpointIndex, sourceId, messageHandle };
|
|
204
|
+
}
|
|
111
205
|
}
|
package/build/src/extractor.js
CHANGED
|
@@ -68,6 +68,19 @@ export const loginResponseStatusFields = [
|
|
|
68
68
|
}
|
|
69
69
|
];
|
|
70
70
|
// support two-tiers status code
|
|
71
|
+
export const loginArtifactResponseStatusFields = [
|
|
72
|
+
{
|
|
73
|
+
key: 'top',
|
|
74
|
+
localPath: ['Envelope', 'Body', 'ArtifactResponse', 'Status', 'StatusCode'],
|
|
75
|
+
attributes: ['Value'],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
key: 'second',
|
|
79
|
+
localPath: ['Envelope', 'Body', 'ArtifactResponse', 'Status', 'StatusCode', 'StatusCode'],
|
|
80
|
+
attributes: ['Value'],
|
|
81
|
+
}
|
|
82
|
+
];
|
|
83
|
+
// support two-tiers status code
|
|
71
84
|
export const logoutResponseStatusFields = [
|
|
72
85
|
{
|
|
73
86
|
key: 'top',
|
package/build/src/flow.js
CHANGED
|
@@ -5,8 +5,8 @@ import * as uuid from 'uuid';
|
|
|
5
5
|
import { select } from 'xpath';
|
|
6
6
|
import { DOMParser } from '@xmldom/xmldom';
|
|
7
7
|
import { sendArtifactResolve } from "./soap.js";
|
|
8
|
-
import { extract, loginRequestFields, loginResponseFields, logoutRequestFields, logoutResponseFields, logoutResponseStatusFields
|
|
9
|
-
import { BindingNamespace, ParserType,
|
|
8
|
+
import { extract, loginRequestFields, loginResponseFields, loginResponseStatusFields, loginArtifactResponseStatusFields, logoutRequestFields, logoutResponseFields, logoutResponseStatusFields } from './extractor.js';
|
|
9
|
+
import { BindingNamespace, ParserType, StatusCode, wording } from './urn.js';
|
|
10
10
|
const bindDict = wording.binding;
|
|
11
11
|
const urlParams = wording.urlParams;
|
|
12
12
|
// get the default extractor fields based on the parserType
|
|
@@ -146,12 +146,15 @@ async function postFlow(options) {
|
|
|
146
146
|
let ID = '_' + uuid.v4();
|
|
147
147
|
let url = metadata.idp.getArtifactResolutionService(bindDict.soap);
|
|
148
148
|
let samlSoapRaw = libsaml.replaceTagsByValue(libsaml.defaultArtifactResolveTemplate.context, {
|
|
149
|
-
ID:
|
|
149
|
+
ID: ID,
|
|
150
150
|
Destination: url,
|
|
151
151
|
Issuer: metadata.sp.getEntityID(),
|
|
152
152
|
IssueInstant: new Date().toISOString(),
|
|
153
153
|
Art: request.Art
|
|
154
154
|
});
|
|
155
|
+
if (!metadata.idp.isWantAuthnRequestsSigned()) {
|
|
156
|
+
samlContent = await sendArtifactResolve(url, samlSoapRaw);
|
|
157
|
+
}
|
|
155
158
|
if (metadata.idp.isWantAuthnRequestsSigned()) {
|
|
156
159
|
const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm, transformationAlgorithms } = spSetting;
|
|
157
160
|
let signatureSoap = libsaml.constructSAMLSignature({
|
|
@@ -172,14 +175,8 @@ async function postFlow(options) {
|
|
|
172
175
|
}
|
|
173
176
|
}
|
|
174
177
|
});
|
|
175
|
-
|
|
176
|
-
/* console.log(signatureSoap)
|
|
177
|
-
console.log("签过名的")*/
|
|
178
|
-
console.log(data);
|
|
179
|
-
console.log("keycloak数据----------------------");
|
|
180
|
-
samlContent = data;
|
|
178
|
+
samlContent = await sendArtifactResolve(url, signatureSoap);
|
|
181
179
|
}
|
|
182
|
-
// No need to embeded XML signature
|
|
183
180
|
}
|
|
184
181
|
const verificationOptions = {
|
|
185
182
|
metadata: from.entityMeta,
|
|
@@ -189,23 +186,17 @@ async function postFlow(options) {
|
|
|
189
186
|
let decryptRequired = from.entitySetting.isAssertionEncrypted;
|
|
190
187
|
let extractorFields = [];
|
|
191
188
|
// validate the xml first
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
console.log("验证和结果-----------------------")
|
|
199
|
-
console.log("验证和结果-----------------------")
|
|
200
|
-
console.log("验证和结果-----------------------")
|
|
201
|
-
});
|
|
202
|
-
console.log(res);
|
|
203
|
-
console.log("验证和结果-----------------------")*/
|
|
189
|
+
let res = await libsaml.isValidXml(samlContent).catch((error) => {
|
|
190
|
+
return Promise.reject('ERR_EXCEPTION_VALIDATE_XML');
|
|
191
|
+
});
|
|
192
|
+
if (res !== true) {
|
|
193
|
+
return Promise.reject('ERR_EXCEPTION_VALIDATE_XML');
|
|
194
|
+
}
|
|
204
195
|
if (parserType !== urlParams.samlResponse) {
|
|
205
196
|
extractorFields = getDefaultExtractorFields(parserType, null);
|
|
206
197
|
}
|
|
207
198
|
// check status based on different scenarios
|
|
208
|
-
|
|
199
|
+
await checkStatus(samlContent, parserType, soap);
|
|
209
200
|
/**检查签名顺序 */
|
|
210
201
|
/* if (
|
|
211
202
|
checkSignature &&
|
|
@@ -230,14 +221,11 @@ async function postFlow(options) {
|
|
|
230
221
|
return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
|
|
231
222
|
}
|
|
232
223
|
if (!decryptRequired) {
|
|
233
|
-
console.log("-------------------走到了这里----------------------");
|
|
234
224
|
extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
|
|
235
225
|
}
|
|
236
226
|
if (parserType === 'SAMLResponse' && decryptRequired) {
|
|
237
227
|
// 1. 解密断言
|
|
238
228
|
const [decryptedSAML, decryptedAssertion] = await libsaml.decryptAssertionSoap(self, samlContent);
|
|
239
|
-
console.log(decryptedAssertion);
|
|
240
|
-
console.log("解密数据-----------------------------");
|
|
241
229
|
// 2. 检查解密后的断言是否包含签名
|
|
242
230
|
const assertionDoc = new DOMParser().parseFromString(decryptedAssertion, 'text/xml');
|
|
243
231
|
const assertionSignatureNodes = select("./*[local-name()='Signature']", assertionDoc.documentElement);
|
|
@@ -250,9 +238,6 @@ async function postFlow(options) {
|
|
|
250
238
|
};
|
|
251
239
|
// 3.2 验证断言签名
|
|
252
240
|
const [assertionVerified, result] = libsaml.verifySignatureSoap(decryptedAssertion, assertionVerificationOptions);
|
|
253
|
-
console.log(assertionVerified);
|
|
254
|
-
console.log(result);
|
|
255
|
-
console.log("验证机结果--------------");
|
|
256
241
|
if (!assertionVerified) {
|
|
257
242
|
console.error("解密后的断言签名验证失败");
|
|
258
243
|
return Promise.reject('ERR_FAIL_TO_VERIFY_ASSERTION_SIGNATURE');
|
|
@@ -307,10 +292,6 @@ async function postFlow(options) {
|
|
|
307
292
|
const targetEntityMetadata = from.entityMeta;
|
|
308
293
|
const issuer = targetEntityMetadata.getEntityID();
|
|
309
294
|
const extractedProperties = parseResult.extract;
|
|
310
|
-
console.log(extractedProperties);
|
|
311
|
-
console.log(parseResult);
|
|
312
|
-
console.log("解析结果----------------------------------");
|
|
313
|
-
console.log("签发这-----------");
|
|
314
295
|
// unmatched issuer
|
|
315
296
|
if ((parserType === 'LogoutResponse' || parserType === 'SAMLResponse')
|
|
316
297
|
&& extractedProperties
|
|
@@ -565,14 +546,19 @@ async function postSimpleSignFlow(options) {
|
|
|
565
546
|
}
|
|
566
547
|
return Promise.resolve(parseResult);
|
|
567
548
|
}
|
|
568
|
-
function checkStatus(content, parserType) {
|
|
549
|
+
function checkStatus(content, parserType, soap) {
|
|
569
550
|
// only check response parser
|
|
570
551
|
if (parserType !== urlParams.samlResponse && parserType !== urlParams.logoutResponse) {
|
|
571
552
|
return Promise.resolve('SKIPPED');
|
|
572
553
|
}
|
|
573
|
-
|
|
554
|
+
let fields = parserType === urlParams.samlResponse
|
|
574
555
|
? loginResponseStatusFields
|
|
575
556
|
: logoutResponseStatusFields;
|
|
557
|
+
if (soap === true) {
|
|
558
|
+
fields = parserType === urlParams.samlResponse
|
|
559
|
+
? loginArtifactResponseStatusFields
|
|
560
|
+
: logoutResponseStatusFields;
|
|
561
|
+
}
|
|
576
562
|
const { top, second } = extract(content, fields);
|
|
577
563
|
// only resolve when top-tier status code is success
|
|
578
564
|
if (top === StatusCode.Success) {
|
package/build/src/libsaml.js
CHANGED
|
@@ -70,19 +70,22 @@ const libSaml = () => {
|
|
|
70
70
|
context: '<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="{AssertionConsumerServiceURL}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:NameIDPolicy Format="{NameIDFormat}" AllowCreate="{AllowCreate}"/></samlp:AuthnRequest>',
|
|
71
71
|
};
|
|
72
72
|
/**
|
|
73
|
-
* @desc Default
|
|
73
|
+
* @desc Default logout request template
|
|
74
74
|
* @type {LogoutRequestTemplate}
|
|
75
75
|
*/
|
|
76
76
|
const defaultLogoutRequestTemplate = {
|
|
77
77
|
context: '<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}"><saml:Issuer>{Issuer}</saml:Issuer><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID></samlp:LogoutRequest>',
|
|
78
78
|
};
|
|
79
79
|
/**
|
|
80
|
-
* @desc Default
|
|
80
|
+
* @desc Default art request template
|
|
81
81
|
* @type {LogoutRequestTemplate}
|
|
82
82
|
*/
|
|
83
83
|
const defaultArtifactResolveTemplate = {
|
|
84
84
|
context: `<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><saml2p:ArtifactResolve xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}"><saml2:Issuer>{Issuer}</saml2:Issuer><saml2p:Artifact>{Art}</saml2p:Artifact></saml2p:ArtifactResolve></SOAP-ENV:Body></SOAP-ENV:Envelope>`,
|
|
85
85
|
};
|
|
86
|
+
const defaultArtAuthnRequestTemplate = {
|
|
87
|
+
context: `<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header></SOAP-ENV:Header><samlp:ArtifactResponse xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="{ID}" InResponseTo="{InResponseTo}" Version="2.0" IssueInstant="{IssueInstant}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status>{AuthnRequest}</samlp:ArtifactResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>`,
|
|
88
|
+
};
|
|
86
89
|
/**
|
|
87
90
|
* @desc Default AttributeStatement template
|
|
88
91
|
* @type {AttributeStatementTemplate}
|
|
@@ -209,6 +212,7 @@ const libSaml = () => {
|
|
|
209
212
|
createXPath,
|
|
210
213
|
getQueryParamByType,
|
|
211
214
|
defaultLoginRequestTemplate,
|
|
215
|
+
defaultArtAuthnRequestTemplate,
|
|
212
216
|
defaultArtifactResolveTemplate,
|
|
213
217
|
defaultLoginResponseTemplate,
|
|
214
218
|
defaultAttributeStatementTemplate,
|
|
@@ -453,7 +457,7 @@ const libSaml = () => {
|
|
|
453
457
|
assertionNode = node[0].toString();
|
|
454
458
|
}
|
|
455
459
|
}
|
|
456
|
-
|
|
460
|
+
|
|
457
461
|
if (assertionSignatureNode.length === 1) {
|
|
458
462
|
const verifiedAssertionInfo = extract(assertionSignatureNode[0].toString(), [{
|
|
459
463
|
key: 'refURI',
|
|
@@ -485,7 +489,7 @@ const libSaml = () => {
|
|
|
485
489
|
}]);
|
|
486
490
|
assertionNode = verifiedDoc.assertion.toString();
|
|
487
491
|
}
|
|
488
|
-
|
|
492
|
+
|
|
489
493
|
return [verified, assertionNode];*/
|
|
490
494
|
},
|
|
491
495
|
verifySignatureSoap(xml, opts) {
|
package/build/src/metadata-sp.js
CHANGED
|
@@ -71,8 +71,10 @@ export class SpMetadata extends Metadata {
|
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
if (isNonEmptyArray(artifactResolutionService)) {
|
|
74
|
+
let indexCount = 0;
|
|
74
75
|
artifactResolutionService.forEach(a => {
|
|
75
76
|
const attr = {
|
|
77
|
+
index: String(indexCount++),
|
|
76
78
|
Binding: a.Binding,
|
|
77
79
|
Location: a.Location,
|
|
78
80
|
};
|
package/build/src/metadata.js
CHANGED
|
@@ -128,8 +128,6 @@ export default class Metadata {
|
|
|
128
128
|
getArtifactResolutionService(binding) {
|
|
129
129
|
if (binding && isString(binding)) {
|
|
130
130
|
const bindType = namespace.binding[binding];
|
|
131
|
-
console.log(this.meta);
|
|
132
|
-
console.log("看一下---------------------");
|
|
133
131
|
let artifactResolutionService = this.meta.artifactResolutionService;
|
|
134
132
|
if (!(artifactResolutionService instanceof Array)) {
|
|
135
133
|
artifactResolutionService = [artifactResolutionService];
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<import namespace="urn:oasis:names:tc:SAML:2.0:assertion"
|
|
16
16
|
schemaLocation="saml-schema-assertion-2.0.xsd"/>
|
|
17
17
|
<import namespace="http://schemas.xmlsoap.org/soap/envelope/"
|
|
18
|
-
schemaLocation="
|
|
18
|
+
schemaLocation="soap-envelope.xsd"/>
|
|
19
19
|
<annotation>
|
|
20
20
|
<documentation>
|
|
21
21
|
Document identifier: saml-schema-ecp-2.0
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
blockDefault="substitution"
|
|
12
12
|
version="2.0">
|
|
13
13
|
<import namespace="http://www.w3.org/2000/09/xmldsig#"
|
|
14
|
-
schemaLocation="
|
|
14
|
+
schemaLocation="xmldsig-core-schema.xsd"/>
|
|
15
15
|
<import namespace="http://www.w3.org/2001/04/xmlenc#"
|
|
16
|
-
schemaLocation="
|
|
16
|
+
schemaLocation="xenc-schema.xsd"/>
|
|
17
17
|
<import namespace="urn:oasis:names:tc:SAML:2.0:assertion"
|
|
18
18
|
schemaLocation="saml-schema-assertion-2.0.xsd"/>
|
|
19
19
|
<import namespace="http://www.w3.org/XML/1998/namespace"
|
|
20
|
-
schemaLocation="
|
|
20
|
+
schemaLocation="xml.xsd"/>
|
|
21
21
|
<annotation>
|
|
22
22
|
<documentation>
|
|
23
23
|
Document identifier: saml-schema-metadata-2.0
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<import namespace="urn:oasis:names:tc:SAML:2.0:assertion"
|
|
13
13
|
schemaLocation="saml-schema-assertion-2.0.xsd"/>
|
|
14
14
|
<import namespace="http://www.w3.org/2000/09/xmldsig#"
|
|
15
|
-
schemaLocation="
|
|
15
|
+
schemaLocation="xmldsig-core-schema.xsd"/>
|
|
16
16
|
<annotation>
|
|
17
17
|
<documentation>
|
|
18
18
|
Document identifier: saml-schema-protocol-2.0
|
|
@@ -1,36 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
<!-- Schema for the SOAP/1.1 envelope
|
|
3
|
-
|
|
4
|
-
Portions © 2001 DevelopMentor.
|
|
5
|
-
© 2001 W3C (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved.
|
|
6
|
-
|
|
7
|
-
This document is governed by the W3C Software License [1] as described in the FAQ [2].
|
|
8
|
-
[1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
|
|
9
|
-
[2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD
|
|
10
|
-
By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions:
|
|
11
|
-
|
|
12
|
-
Permission to use, copy, modify, and distribute this software and its documentation, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make:
|
|
13
|
-
|
|
14
|
-
1. The full text of this NOTICE in a location viewable to users of the redistributed or derivative work.
|
|
15
|
-
|
|
16
|
-
2. Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, a short notice of the following form (hypertext is preferred, text is permitted) should be used within the body of any redistributed or derivative code: "Copyright © 2001 World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/"
|
|
17
|
-
|
|
18
|
-
3. Notice of any changes or modifications to the W3C files, including the date changes were made. (We recommend you provide URIs to the location from which the code is derived.)
|
|
19
|
-
|
|
20
|
-
Original W3C files; http://www.w3.org/2001/06/soap-envelope
|
|
21
|
-
Changes made:
|
|
22
|
-
- reverted namespace to http://schemas.xmlsoap.org/soap/envelope/
|
|
23
|
-
- reverted mustUnderstand to only allow 0 and 1 as lexical values
|
|
24
|
-
- made encodingStyle a global attribute 20020825
|
|
25
|
-
- removed default value from mustUnderstand attribute declaration
|
|
26
|
-
|
|
27
|
-
THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
|
|
28
|
-
|
|
29
|
-
COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
|
|
30
|
-
|
|
31
|
-
The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the software without specific, written prior permission. Title to copyright in this software and any associated documentation will at all times remain with copyright holders.
|
|
32
|
-
|
|
33
|
-
-->
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
34
2
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.xmlsoap.org/soap/envelope/" targetNamespace="http://schemas.xmlsoap.org/soap/envelope/">
|
|
35
3
|
<!-- Envelope, header and body -->
|
|
36
4
|
<xs:element name="Envelope" type="tns:Envelope"/>
|