diff --git a/.editorconfig b/.editorconfig
index ad47af4a..fe823fb3 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -2,18 +2,11 @@ root = true
[*]
indent_style = tab
-end_of_line = lf
+# indent_size = 4
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
-[*.{json,js,ts,jsx,html,css}]
+[*.{yaml,yml}]
indent_style = space
indent_size = 2
-
-[.eslintrc]
-indent_style = space
-indent_size = 2
-
-[*.md]
-trim_trailing_whitespace = false
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 00000000..343de7a7
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,18 @@
+# Hidden but still want to process
+!.*.js
+!.*.json
+!.vscode
+# Do NOT want to process
+node_modules/
+package-lock.json
+*.lock
+*.yaml
+*.log
+*.d.ts
+/.build/
+/lib/
+/docs/
+/.nyc_output/
+/test/key/
+/test/misk/
+.idea/
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 00000000..eda083c5
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,8 @@
+{
+ "root": true,
+ "extends": ["./tools/eslintrc"],
+ "rules": {
+ "security/detect-non-literal-fs-filename": "off",
+ "security/detect-object-injection": "off"
+ }
+}
diff --git a/.gitignore b/.gitignore
index 1a5000ec..5d103a19 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,13 @@
+# OS generated files #
+######################
+.DS_Store
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+
# Logs
logs
*.log
@@ -18,19 +28,18 @@ coverage
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
node_modules
-build
-types/
+/.build/
+/lib/
+/types/
.yarnclean
.nyc_output
+/test.txt
#jetbrains IDEs
.idea
-#vscode
-.vscode
-
*.tgz
-package-lock.json
\ No newline at end of file
+package-lock.json
diff --git a/.npmignore b/.npmignore
deleted file mode 100644
index 8600fc0b..00000000
--- a/.npmignore
+++ /dev/null
@@ -1,7 +0,0 @@
-node_modules
-examples
-test
-@types
-docs
-yarn*
-.nyc_output
\ No newline at end of file
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 00000000..9841c0bd
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+registry=https://registry.yarnpkg.com
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 00000000..a49c27fd
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,16 @@
+# Hidden but still want to process
+!.*.js
+!.*.json
+!.vscode
+# Do NOT want to process
+node_modules/
+package-lock.json
+*.lock
+*.yaml
+*.log
+/.build/
+/lib/
+/docs/
+/.nyc_output/
+/test/misc/attack_response_signed.xml
+/test/misc/multiple_entitydescriptor.xml
diff --git a/.prettierrc.yaml b/.prettierrc.yaml
new file mode 100644
index 00000000..c984bc4c
--- /dev/null
+++ b/.prettierrc.yaml
@@ -0,0 +1,6 @@
+trailingComma: 'es5'
+# tabWidth: 2
+semi: true
+singleQuote: true
+useTabs: true
+printWidth: 120
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 00000000..7355b16d
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,3 @@
+{
+ "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
+}
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..df50854d
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,12 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "node",
+ "request": "attach",
+ "name": "Attach",
+ "restart": true,
+ "processId": "${command:PickProcess}"
+ }
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..8c842c8f
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,37 @@
+{
+ "typescript.tsdk": "./node_modules/typescript/lib",
+ "files.trimTrailingWhitespace": true,
+ "files.insertFinalNewline": true,
+ "files.exclude": {
+ "**/node_modules/": true
+ },
+ "search.exclude": {
+ "**/.build/": true,
+ "server/": true
+ },
+ "editor.tabCompletion": "on",
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "[json]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[markdown]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[html]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[xml]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "editor.formatOnSave": true,
+ "editor.codeActionsOnSave": {
+ "source.organizeImports": true,
+ "source.fixAll": true
+ },
+ "eslint.probe": ["javascript", "typescript"],
+ "eslint.options": {
+ "extensions": [".js", ".ts"],
+ "cache": true,
+ "cacheLocation": ".build/caches/eslint"
+ }
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 00000000..73ec0dfc
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,6 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": []
+}
diff --git a/@types/xml-encryption.d.ts b/@types/xml-encryption.d.ts
deleted file mode 100644
index af8876c0..00000000
--- a/@types/xml-encryption.d.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-declare module "xml-encryption" {
- export interface EncryptOptions {
- rsa_pub: string | Buffer;
- pem: string | Buffer;
- encryptionAlgorithm: string;
- keyEncryptionAlgorithm: string;
- input_encoding?: string;
- }
- export interface DecryptOptions {
- key: string | Buffer;
- }
- export interface Callback {
- (err:Error, result): void;
- }
- export function encrypt(content: string, options: EncryptOptions, callback: Callback): string;
- export function encryptKeyInfo(symmetricKey: string, options: EncryptOptions, callback: Callback): string;
- export function decrypt(xml: string | Document, options: DecryptOptions, callback: Callback): string;
- export function decryptKeyInfo(doc: string | Document, options: DecryptOptions): string;
- const _default: { decrypt, encrypt, decryptKeyInfo, encryptKeyInfo };
- export default _default;
-}
diff --git a/Makefile b/Makefile
index f95753d0..5a55cf87 100644
--- a/Makefile
+++ b/Makefile
@@ -7,12 +7,11 @@ clean: ;
rm -rf node_modules
rebuild: ;
- rm -rf build; \
- tsc; \
+ rm -rf .build lib; tsc -b --verbose; \
pretest: ;
- mkdir -p build/test; \
- cp -a test/key test/misc build/test;
+ mkdir -p .build/test; \
+ cp -a test/key test/misc .build/test;
install_jdk:
sudo add-apt-repository ppa:openjdk-r/ppa -y
diff --git a/README.md b/README.md
index 06003e0d..7063f915 100644
--- a/README.md
+++ b/README.md
@@ -14,8 +14,7 @@ Welcome all PRs for maintaining this project, or provide a link to the repositor
### Sponsor
| |
If you want to quickly implement SAML SSO, feel free to check out Auth0's NodeJS SDK and free plan at [auth0.com/developers](https://auth0.com/developers?utm_source=GHsponsor&utm_medium=GHsponsor&utm_campaign=samlify&utm_content=auth).
|
-| :----------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-
+| :----------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
### Installation
@@ -42,10 +41,8 @@ Now you can create your own schema validator and even suppress it but you have t
```typescript
samlify.setSchemaValidator({
- validate: (response: string) => {
- /* implment your own or always returns a resolved promise to skip */
- return Promise.resolve('skipped');
- }
+ /* implment your own or always returns a resolved promise to skip */
+ validate: async (response: string) => 'skipped';
});
```
diff --git a/docs/encrypted-saml-response.md b/docs/encrypted-saml-response.md
index b12014a7..7b4d38e1 100644
--- a/docs/encrypted-saml-response.md
+++ b/docs/encrypted-saml-response.md
@@ -7,7 +7,7 @@ const idp = IdentityProvider({
isAssertionEncrypted: true,
metadata: fs.readFileSync('./metadata_idp.xml'),
dataEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes128-cbc',
- keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
+ keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
});
```
@@ -46,7 +46,7 @@ Currently, we support the following encrpytion algorithms:
* http://www.w3.org/2009/xmlenc11#aes128-gcm
**Key encryption algorithms**
-* http://www.w3.org/2001/04/xmlenc#rsa-1_5
* http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p
+* http://www.w3.org/2001/04/xmlenc#rsa-1_5
Credits to [auth0/node-xml-encryption](https://github.com/auth0/node-xml-encryption)
diff --git a/docs/idp-configuration.md b/docs/idp-configuration.md
index 5a5891c3..da8b541c 100644
--- a/docs/idp-configuration.md
+++ b/docs/idp-configuration.md
@@ -25,13 +25,13 @@ const idp = new IdentityProvider({
OR
- **entityID: String**
Entity identifier. It is used to identify your entity, and match the equivalence in each saml request/response.
-
+
- **signingCert: String**
_Optional_: Specify the certificate used for signing purpose if you construct the idp without a metadata.
- **encryptCert: String**
_Optional_: Specify the certificate used for encryption purpose if you construct the idp without a metadata.
-
+
- **singleSignOnService: SignOnService[]**
_Optional_: Declare the single sign on service if you construct the idp without a metadata.
@@ -58,32 +58,32 @@ const idp = new IdentityProvider({
Declare the tag of specific xml document node. `TagPrefixKey` currently supports `encryptedAssertion` only. (See more [#220](https://github.com/tngan/samlify/issues/220))
- **loginResponseTemplate: {context: String, attributes: Attributes}**
- Customize the login response template, and user can reuse it in the callback function to do runtime interpolation. (See [more](/template))
+ Customize the login response template, and user can reuse it in the callback function to do runtime interpolation. (See [more](/template))
-- **wantLogoutResponseSigned: Boolean**
+- **wantLogoutResponseSigned: Boolean**
Declare if idp guarantees the logout response from sp is signed.
- **messageSigningOrder: SigningOrder**
Declare the message signing order, either `sign-then-encrypt` (default) or `encrypt-then-sign`.
- **relayState: String**
- Specify the relayState of the request.
+ Specify the relayState of the request.
!> It will be deprecated soon and put into request level instead of entity level.
- **isAssertionEncrypted: Boolean**
Decalre if idp would encrypt the assertion in the response.
-
+
!> It will be deprecated soon, then samlify will automatically detect if the document is encrypted.
-
+
- **requestSignatureAlgorithm: SigningAlgorithm**
The signature algorithm used in request. Default to `http://www.w3.org/2001/04/xmldsig-more#rsa-sha256`. We also support rsa-sha1 (not recommended) `http://www.w3.org/2000/09/xmldsig#rsa-sha1` and rsa-sha2 `http://www.w3.org/2001/04/xmldsig-more#rsa-sha512`.
-
-- **dataEncryptionAlgorithm: EncryptionAlgorithm**
+
+- **dataEncryptionAlgorithm: EncryptionAlgorithm**
The encryption algorithm used in response. Default to `http://www.w3.org/2001/04/xmldsig-more#rsa-sha256`. We also support aes256 `http://www.w3.org/2001/04/xmlenc#aes256-cbc`, tripledes `http://www.w3.org/2001/04/xmlenc#tripledes-cbc` and aes128 `http://www.w3.org/2009/xmlenc11#aes128-gcm`.
- **keyEncryptionAlgorithm: KeyEncryptionAlgorithm**
- The key encryption algorithm. Default to rsa-1_5 `http://www.w3.org/2001/04/xmlenc#rsa-1_5`. We also support rsa-oaep-mgf1p `http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p`.
+ The key encryption algorithm. Default to rsa-oaep-mgf1p `http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p`. We also support rsa-1_5 `http://www.w3.org/2001/04/xmlenc#rsa-1_5`.
- **generateID: (): String**
A function to generate the document identifier in root node. Default to `_${UUID_V4}`.
diff --git a/index.ts b/index.ts
deleted file mode 100644
index edda52b2..00000000
--- a/index.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-// version <= 1.25
-import IdentityProvider, { IdentityProvider as IdentityProviderInstance } from './src/entity-idp';
-import ServiceProvider, { ServiceProvider as ServiceProviderInstance } from './src/entity-sp';
-
-export { default as IdPMetadata } from './src/metadata-idp';
-export { default as SPMetadata } from './src/metadata-sp';
-export { default as Utility } from './src/utility';
-export { default as SamlLib } from './src/libsaml';
-// roadmap
-// new name convention in version >= 3.0
-import * as Constants from './src/urn';
-import * as Extractor from './src/extractor';
-
-// exposed methods for customising samlify
-import { setSchemaValidator } from './src/api';
-
-export {
- Constants,
- Extractor,
- // temp: resolve the conflict after version >= 3.0
- IdentityProvider,
- IdentityProviderInstance,
- ServiceProvider,
- ServiceProviderInstance,
- // set context
- setSchemaValidator
-};
\ No newline at end of file
diff --git a/package.json b/package.json
index e2907c89..f547f2c7 100644
--- a/package.json
+++ b/package.json
@@ -1,71 +1,88 @@
{
- "name": "samlify",
- "version": "2.7.6",
- "description": "High-level API for Single Sign On (SAML 2.0)",
- "main": "build/index.js",
- "keywords": [
- "nodejs",
- "saml2",
- "sso",
- "slo",
- "metadata"
- ],
- "typings": "types/index.d.ts",
- "scripts": {
- "build": "yarn audit;make rebuild",
- "docs": "docsify serve -o docs",
- "lint": "tslint -p .",
- "lint:fix": "tslint -p . --fix",
- "pretest": "make pretest",
- "test": "NODE_ENV=test nyc ava",
- "coverage": "nyc report --reporter=text-lcov | coveralls",
- "hooks:postinstall": "ln -sf $PWD/.pre-commit.sh $PWD/.git/hooks/pre-commit"
- },
- "contributors": [
- "Tony Ngan "
- ],
- "author": "tngan",
- "repository": {
- "url": "https://github.com/tngan/samlify",
- "type": "git"
- },
- "license": "MIT",
- "dependencies": {
- "@authenio/xml-encryption": "^1.2.2",
- "camelcase": "^5.3.1",
- "node-forge": "^0.10.0",
- "node-rsa": "^1.0.5",
- "pako": "^1.0.10",
- "uuid": "^3.3.2",
- "xml": "^1.0.1",
- "xml-crypto": "^2.0.0",
- "xmldom": "^0.1.27",
- "xpath": "^0.0.27"
- },
- "devDependencies": {
- "@ava/typescript": "^1.1.1",
- "@types/node": "^11.11.3",
- "@types/node-forge": "^0.9.5",
- "@types/pako": "^1.0.1",
- "@types/uuid": "3.0.0",
- "@types/xmldom": "^0.1.28",
- "ava": "^3.8.2",
- "coveralls": "^3.1.0",
- "nyc": "^15.0.1",
- "timekeeper": "^2.2.0",
- "ts-node": "^8.3.0",
- "tslint": "^6.1.2",
- "typescript": "^3.8.3"
- },
- "ava": {
- "extensions": [
- "ts"
- ],
- "require": [
- "ts-node/register"
- ],
- "files": [
- "!**/*.d.ts"
- ]
- }
+ "name": "samlify",
+ "version": "2.7.6",
+ "description": "High-level API for Single Sign On (SAML 2.0)",
+ "main": "lib/index.js",
+ "keywords": [
+ "nodejs",
+ "saml2",
+ "sso",
+ "slo",
+ "metadata"
+ ],
+ "files": [
+ "lib"
+ ],
+ "typings": "lib/index.d.ts",
+ "scripts": {
+ "build": "yarn audit;make rebuild",
+ "docs": "docsify serve -o docs",
+ "format": "prettier \"./**/*.{json,js,ts,css,html,xml,md}\" --write",
+ "lint": "eslint .",
+ "lint:fix": "eslint . --fix",
+ "pretest": "make pretest",
+ "test": "NODE_ENV=test nyc ava --cwd test",
+ "coverage": "nyc report --reporter=text-lcov | coveralls",
+ "hooks:postinstall": "ln -sf $PWD/.pre-commit.sh $PWD/.git/hooks/pre-commit"
+ },
+ "contributors": [
+ "Tony Ngan ",
+ "Jim Isaacs "
+ ],
+ "author": "tngan",
+ "repository": {
+ "url": "https://github.com/tngan/samlify",
+ "type": "git"
+ },
+ "license": "MIT",
+ "dependencies": {
+ "camelcase": "^6.2.0",
+ "node-forge": "^0.10.0",
+ "node-rsa": "^1.1.1",
+ "pako": "^1.0.11",
+ "uuid": "^8.3.2",
+ "xml": "^1.0.1",
+ "xml-crypto": "^2.0.0",
+ "xml-encryption": "^1.2.2",
+ "xmldom": "^0.1.27",
+ "xpath": "^0.0.27"
+ },
+ "devDependencies": {
+ "@authenio/samlify-xsd-schema-validator": "^1.0.3",
+ "@ava/typescript": "^1.1.1",
+ "@prettier/plugin-xml": "^0.13.0",
+ "@types/node": "^14.14.31",
+ "@types/node-forge": "^0.9.7",
+ "@types/node-rsa": "^1.1.0",
+ "@types/pako": "^1.0.1",
+ "@types/uuid": "^8.3.0",
+ "@types/xml": "^1.0.5",
+ "@types/xml-crypto": "^1.4.1",
+ "@types/xml-encryption": "^1.2.0",
+ "@types/xmldom": "^0.1.30",
+ "@typescript-eslint/eslint-plugin": "^4.15.2",
+ "@typescript-eslint/parser": "^4.15.2",
+ "ava": "^3.8.2",
+ "coveralls": "^3.1.0",
+ "eslint": "^7.20.0",
+ "eslint-config-prettier": "^8.1.0",
+ "eslint-plugin-prettier": "^3.3.1",
+ "eslint-plugin-security": "^1.4.0",
+ "nyc": "^15.0.1",
+ "prettier": "^2.2.1",
+ "timekeeper": "^2.2.0",
+ "ts-node": "^9.1.1",
+ "typescript": "4.1.x"
+ },
+ "ava": {
+ "extensions": [
+ "ts"
+ ],
+ "require": [
+ "ts-node/register"
+ ],
+ "files": [
+ "!**/*.d.ts"
+ ]
+ }
}
diff --git a/src/api.ts b/src/api.ts
index b74ab6c2..bc4822d2 100644
--- a/src/api.ts
+++ b/src/api.ts
@@ -1,25 +1,24 @@
+import { SamlifyError, SamlifyErrorCode } from './error';
+
// global module configuration
-interface Context extends ValidatorContext {}
+type Context = ValidatorContext;
interface ValidatorContext {
- validate?: (xml: string) => Promise;
+ validate?: (xml: string) => Promise;
}
const context: Context = {
- validate: undefined
+ validate: undefined,
};
export function getContext() {
- return context;
+ return context;
}
export function setSchemaValidator(params: ValidatorContext) {
-
- if (typeof params.validate !== 'function') {
- throw new Error('validate must be a callback function having one arguemnt as xml input');
- }
-
- // assign the validate function to the context
- context.validate = params.validate;
-
-}
\ No newline at end of file
+ if (typeof params.validate !== 'function') {
+ throw new SamlifyError(SamlifyErrorCode.TypeError, 'validate must be a function having one argument as xml input');
+ }
+ // assign the validate function to the context
+ context.validate = params.validate;
+}
diff --git a/src/binding-post.ts b/src/binding-post.ts
index 9777a2af..fdd820eb 100644
--- a/src/binding-post.ts
+++ b/src/binding-post.ts
@@ -1,337 +1,404 @@
/**
-* @file binding-post.ts
-* @author tngan
-* @desc Binding-level API, declare the functions using POST binding
-*/
+ * @file binding-post.ts
+ * @author tngan
+ * @desc Binding-level API, declare the functions using POST binding
+ */
-import { wording, namespace, StatusCode } from './urn';
-import { BindingContext } from './entity';
-import libsaml from './libsaml';
-import utility, { get } from './utility';
-import { LogoutResponseTemplate } from './libsaml';
-
-const binding = wording.binding;
+import type { BindingContext, Entity } from './entity';
+import type { IdentityProvider } from './entity-idp';
+import type { ServiceProvider } from './entity-sp';
+import { SamlifyError, SamlifyErrorCode } from './error';
+import libsaml, { CustomTagReplacement } from './libsaml';
+import { BindingNamespace, StatusCode } from './urn';
+import { base64Decode, base64Encode, get, isNonEmptyArray } from './utility';
/**
-* @desc Generate a base64 encoded login request
-* @param {string} referenceTagXPath reference uri
-* @param {object} entity object includes both idp and sp
-* @param {function} customTagReplacement used when developers have their own login response template
-*/
-function base64LoginRequest(referenceTagXPath: string, entity: any, customTagReplacement?: (template: string) => BindingContext): BindingContext {
- const metadata = { idp: entity.idp.entityMeta, sp: entity.sp.entityMeta };
- const spSetting = entity.sp.entitySetting;
- let id: string = '';
+ * @desc Generate a base64 encoded login request
+ * @param {string} referenceTagXPath reference uri
+ * @param {object} entity object includes both idp and sp
+ * @param {function} customTagReplacement used when developers have their own login response template
+ */
+function base64LoginRequest(
+ referenceTagXPath: string,
+ entity: { idp: IdentityProvider; sp: ServiceProvider },
+ customTagReplacement?: CustomTagReplacement
+): BindingContext {
+ const metadata = { idp: entity.idp.getEntityMeta(), sp: entity.sp.getEntityMeta() };
+ if (!metadata.idp || !metadata.sp) {
+ throw new SamlifyError(SamlifyErrorCode.MissingMetadata);
+ }
+ const spSetting = entity.sp.getEntitySettings();
+ const template = spSetting.loginRequestTemplate ?? libsaml.defaultLoginRequestTemplate;
+
+ const nameIDFormat = spSetting.nameIDFormat;
+ const selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
+
+ let values: Record = {
+ ID: entity.sp.generateID(),
+ Destination: metadata.idp.getSingleSignOnService(BindingNamespace.Post),
+ Issuer: metadata.sp.getEntityID(),
+ IssueInstant: new Date().toISOString(),
+ AssertionConsumerServiceURL: metadata.sp.getAssertionConsumerService(BindingNamespace.Post),
+ EntityID: metadata.sp.getEntityID(),
+ AllowCreate: spSetting.allowCreate,
+ NameIDFormat: selectedNameIDFormat,
+ };
+
+ let rawSaml = template.context ?? '';
+ // perform custom replacement
+ if (customTagReplacement) {
+ [rawSaml = rawSaml, values = values] = customTagReplacement(rawSaml, values);
+ }
+ // pickup any remaining
+ rawSaml = libsaml.replaceTagsByValue(rawSaml, values);
- if (metadata && metadata.idp && metadata.sp) {
- const base = metadata.idp.getSingleSignOnService(binding.post);
- let rawSamlRequest: string;
- if (spSetting.loginRequestTemplate && customTagReplacement) {
- const info = customTagReplacement(spSetting.loginRequestTemplate.context);
- id = get(info, 'id', null);
- rawSamlRequest = get(info, 'context', null);
- } else {
- const nameIDFormat = spSetting.nameIDFormat;
- const selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
- id = spSetting.generateID();
- rawSamlRequest = libsaml.replaceTagsByValue(libsaml.defaultLoginRequestTemplate.context, {
- ID: id,
- Destination: base,
- Issuer: metadata.sp.getEntityID(),
- IssueInstant: new Date().toISOString(),
- AssertionConsumerServiceURL: metadata.sp.getAssertionConsumerService(binding.post),
- EntityID: metadata.sp.getEntityID(),
- AllowCreate: spSetting.allowCreate,
- NameIDFormat: selectedNameIDFormat
- } as any);
- }
- if (metadata.idp.isWantAuthnRequestsSigned()) {
- const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm, transformationAlgorithms } = spSetting;
- return {
- id,
- context: libsaml.constructSAMLSignature({
- referenceTagXPath,
- privateKey,
- privateKeyPass,
- signatureAlgorithm,
- transformationAlgorithms,
- rawSamlMessage: rawSamlRequest,
- signingCert: metadata.sp.getX509Certificate('signing'),
- signatureConfig: spSetting.signatureConfig || {
- prefix: 'ds',
- location: { reference: "/*[local-name(.)='AuthnRequest']/*[local-name(.)='Issuer']", action: 'after' },
- }
- }),
- };
- }
- // No need to embeded XML signature
- return {
- id,
- context: utility.base64Encode(rawSamlRequest),
- };
- }
- throw new Error('ERR_GENERATE_POST_LOGIN_REQUEST_MISSING_METADATA');
+ if (metadata.idp.isWantAuthnRequestsSigned()) {
+ const {
+ privateKey,
+ privateKeyPass,
+ requestSignatureAlgorithm: signatureAlgorithm,
+ transformationAlgorithms,
+ } = spSetting;
+ if (!privateKey) {
+ throw new SamlifyError(
+ SamlifyErrorCode.MissingPrivateKey,
+ "IdentityProvider wants AuthnRequests signed, but ServiceProvider did not provide a 'privateKey'."
+ );
+ }
+ return {
+ id: values.ID,
+ context: libsaml.constructSAMLSignature({
+ referenceTagXPath,
+ privateKey: privateKey.toString(),
+ privateKeyPass,
+ signatureAlgorithm,
+ transformationAlgorithms,
+ rawSamlMessage: rawSaml,
+ signingCert: metadata.sp.getX509Certificate('signing'),
+ signatureConfig: spSetting.signatureConfig || {
+ prefix: 'ds',
+ location: {
+ reference: "/*[local-name(.)='AuthnRequest']/*[local-name(.)='Issuer']",
+ action: 'after',
+ },
+ },
+ }),
+ };
+ }
+ // No need to embeded XML signature
+ return { id: values.ID, context: base64Encode(rawSaml) };
}
/**
-* @desc Generate a base64 encoded login response
-* @param {object} requestInfo corresponding request, used to obtain the id
-* @param {object} entity object includes both idp and sp
-* @param {object} user current logged user (e.g. req.user)
-* @param {function} customTagReplacement used when developers have their own login response template
-* @param {boolean} encryptThenSign whether or not to encrypt then sign first (if signing). Defaults to sign-then-encrypt
-*/
-async function base64LoginResponse(requestInfo: any = {}, entity: any, user: any = {}, customTagReplacement?: (template: string) => BindingContext, encryptThenSign: boolean = false): Promise {
- const idpSetting = entity.idp.entitySetting;
- const spSetting = entity.sp.entitySetting;
- const id = idpSetting.generateID();
- const metadata = {
- idp: entity.idp.entityMeta,
- sp: entity.sp.entityMeta,
- };
- const nameIDFormat = idpSetting.nameIDFormat;
- const selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
- if (metadata && metadata.idp && metadata.sp) {
- const base = metadata.sp.getAssertionConsumerService(binding.post);
- let rawSamlResponse: string;
- const nowTime = new Date();
- const spEntityID = metadata.sp.getEntityID();
- const fiveMinutesLaterTime = new Date(nowTime.getTime());
- fiveMinutesLaterTime.setMinutes(fiveMinutesLaterTime.getMinutes() + 5);
- const fiveMinutesLater = fiveMinutesLaterTime.toISOString();
- const now = nowTime.toISOString();
- const acl = metadata.sp.getAssertionConsumerService(binding.post);
- const tvalue: any = {
- ID: id,
- AssertionID: idpSetting.generateID(),
- Destination: base,
- Audience: spEntityID,
- EntityID: spEntityID,
- SubjectRecipient: acl,
- Issuer: metadata.idp.getEntityID(),
- IssueInstant: now,
- AssertionConsumerServiceURL: acl,
- StatusCode: StatusCode.Success,
- // can be customized
- ConditionsNotBefore: now,
- ConditionsNotOnOrAfter: fiveMinutesLater,
- SubjectConfirmationDataNotOnOrAfter: fiveMinutesLater,
- NameIDFormat: selectedNameIDFormat,
- NameID: user.email || '',
- InResponseTo: get(requestInfo, 'extract.request.id', ''),
- AuthnStatement: '',
- AttributeStatement: '',
- };
- if (idpSetting.loginResponseTemplate && customTagReplacement) {
- const template = customTagReplacement(idpSetting.loginResponseTemplate.context);
- rawSamlResponse = get(template, 'context', null);
- } else {
- if (requestInfo !== null) {
- tvalue.InResponseTo = requestInfo.extract.request.id;
- }
- rawSamlResponse = libsaml.replaceTagsByValue(libsaml.defaultLoginResponseTemplate.context, tvalue);
- }
- const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm } = idpSetting;
- const config = {
- privateKey,
- privateKeyPass,
- signatureAlgorithm,
- signingCert: metadata.idp.getX509Certificate('signing'),
- isBase64Output: false,
- };
- // step: sign assertion ? -> encrypted ? -> sign message ?
- if (metadata.sp.isWantAssertionsSigned()) {
- // console.debug('sp wants assertion signed');
- rawSamlResponse = libsaml.constructSAMLSignature({
- ...config,
- rawSamlMessage: rawSamlResponse,
- transformationAlgorithms: spSetting.transformationAlgorithms,
- referenceTagXPath: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']",
- signatureConfig: {
- prefix: 'ds',
- location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']/*[local-name(.)='Issuer']", action: 'after' },
- },
- });
- }
+ * @desc Generate a base64 encoded login response
+ * @param {object} requestInfo corresponding request, used to obtain the id
+ * @param {object} entity object includes both idp and sp
+ * @param {object} user current logged user (e.g. req.user)
+ * @param {function} customTagReplacement used when developers have their own login response template
+ * @param {boolean} encryptThenSign whether or not to encrypt then sign first (if signing). Defaults to sign-then-encrypt
+ */
+async function base64LoginResponse(
+ requestInfo: Record = {},
+ entity: { idp: IdentityProvider; sp: ServiceProvider },
+ user: Record = {},
+ customTagReplacement?: CustomTagReplacement,
+ encryptThenSign = false
+): Promise {
+ const metadata = { idp: entity.idp.getEntityMeta(), sp: entity.sp.getEntityMeta() };
+ if (!metadata.idp || !metadata.sp) {
+ throw new SamlifyError(SamlifyErrorCode.MissingMetadata);
+ }
+ const idpSetting = entity.idp.getEntitySettings();
+ const template = idpSetting.loginResponseTemplate ?? libsaml.defaultLoginResponseTemplate;
+ const attributes = template.attributes;
+
+ const spSetting = entity.sp.getEntitySettings();
+ const nameIDFormat = idpSetting.nameIDFormat;
+ const selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
+ const base = metadata.sp.getAssertionConsumerService(BindingNamespace.Post);
+ const nowTime = new Date();
+ const spEntityID = metadata.sp.getEntityID();
+ const fiveMinutesLaterTime = new Date(nowTime.getTime());
+ fiveMinutesLaterTime.setMinutes(fiveMinutesLaterTime.getMinutes() + 5);
+ const fiveMinutesLater = fiveMinutesLaterTime.toISOString();
+ const now = nowTime.toISOString();
+ const acl = metadata.sp.getAssertionConsumerService(BindingNamespace.Post);
+
+ let values: Record = {
+ ID: entity.idp.generateID(),
+ AssertionID: entity.idp.generateID(),
+ Destination: base,
+ Audience: spEntityID,
+ EntityID: spEntityID,
+ SubjectRecipient: acl,
+ Issuer: metadata.idp.getEntityID(),
+ IssueInstant: now,
+ AssertionConsumerServiceURL: acl,
+ StatusCode: StatusCode.Success,
+ // can be customized
+ ConditionsNotBefore: now,
+ ConditionsNotOnOrAfter: fiveMinutesLater,
+ SubjectConfirmationDataNotOnOrAfter: fiveMinutesLater,
+ NameIDFormat: selectedNameIDFormat,
+ NameID: user.email || '',
+ InResponseTo: get(requestInfo, 'extract.request.id', ''),
+ AuthnStatement: '',
+ // First fill in attributes
+ AttributeStatement: isNonEmptyArray(attributes)
+ ? libsaml.replaceTagsByValue(
+ libsaml.attributeStatementBuilder(attributes),
+ libsaml.attributeStatementTagBuilder(attributes, user)
+ )
+ : '',
+ };
+
+ let rawSaml = template.context;
+ // perform custom replacement
+ if (customTagReplacement) {
+ [rawSaml = rawSaml, values = values] = customTagReplacement(rawSaml, values);
+ }
+ // pickup any remaining
+ rawSaml = libsaml.replaceTagsByValue(rawSaml, values);
+
+ const getConfig = (wantsSigned: string) => {
+ const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm } = idpSetting;
+ if (!privateKey) {
+ throw new SamlifyError(
+ SamlifyErrorCode.MissingPrivateKey,
+ `ServiceProvider wants ${wantsSigned} signed, but IdentityProvider did not provide a 'privateKey'.`
+ );
+ }
+ return {
+ privateKey: privateKey?.toString(),
+ privateKeyPass,
+ signatureAlgorithm,
+ signingCert: metadata.idp.getX509Certificate('signing'),
+ isBase64Output: false,
+ };
+ };
- // console.debug('after assertion signed', rawSamlResponse);
+ // step: sign assertion ? -> encrypted ? -> sign message ?
+ if (metadata.sp.isWantAssertionsSigned()) {
+ // console.debug('sp wants assertion signed');
+ rawSaml = libsaml.constructSAMLSignature({
+ ...getConfig('Assertions'),
+ rawSamlMessage: rawSaml,
+ transformationAlgorithms: spSetting.transformationAlgorithms,
+ referenceTagXPath: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']",
+ signatureConfig: {
+ prefix: 'ds',
+ location: {
+ reference: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']/*[local-name(.)='Issuer']",
+ action: 'after',
+ },
+ },
+ });
+ }
- // SAML response must be signed sign message first, then encrypt
- if (!encryptThenSign && (spSetting.wantMessageSigned || !metadata.sp.isWantAssertionsSigned())) {
- // console.debug('sign then encrypt and sign entire message');
- rawSamlResponse = libsaml.constructSAMLSignature({
- ...config,
- rawSamlMessage: rawSamlResponse,
- isMessageSigned: true,
- transformationAlgorithms: spSetting.transformationAlgorithms,
- signatureConfig: spSetting.signatureConfig || {
- prefix: 'ds',
- location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Issuer']", action: 'after' },
- },
- });
- }
+ // console.debug('after assertion signed', rawSaml);
- // console.debug('after message signed', rawSamlResponse);
+ // SAML response must be signed sign message first, then encrypt
+ if (!encryptThenSign && (spSetting.wantMessageSigned || !metadata.sp.isWantAssertionsSigned())) {
+ // console.debug('sign then encrypt and sign entire message');
+ rawSaml = libsaml.constructSAMLSignature({
+ ...getConfig('Message'),
+ rawSamlMessage: rawSaml,
+ isMessageSigned: true,
+ transformationAlgorithms: spSetting.transformationAlgorithms,
+ signatureConfig: spSetting.signatureConfig || {
+ prefix: 'ds',
+ location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Issuer']", action: 'after' },
+ },
+ });
+ }
- if (idpSetting.isAssertionEncrypted) {
- // console.debug('idp is configured to do encryption');
- const context = await libsaml.encryptAssertion(entity.idp, entity.sp, rawSamlResponse);
- if (encryptThenSign) {
- //need to decode it
- rawSamlResponse = utility.base64Decode(context) as string;
- } else {
- return Promise.resolve({ id, context });
- }
- }
+ // console.debug('after message signed', rawSaml);
- //sign after encrypting
- if (encryptThenSign && (spSetting.wantMessageSigned || !metadata.sp.isWantAssertionsSigned())) {
- rawSamlResponse = libsaml.constructSAMLSignature({
- ...config,
- rawSamlMessage: rawSamlResponse,
- isMessageSigned: true,
- transformationAlgorithms: spSetting.transformationAlgorithms,
- signatureConfig: spSetting.signatureConfig || {
- prefix: 'ds',
- location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Issuer']", action: 'after' },
- },
- });
- }
+ if (idpSetting.isAssertionEncrypted) {
+ // console.debug('idp is configured to do encryption');
+ const context = await libsaml.encryptAssertion(entity.idp, entity.sp, rawSaml);
+ if (encryptThenSign) {
+ //need to decode it
+ rawSaml = base64Decode(context) as string;
+ } else {
+ return { id: values.ID, context };
+ }
+ }
- return Promise.resolve({
- id,
- context: utility.base64Encode(rawSamlResponse),
- });
+ //sign after encrypting
+ if (encryptThenSign && (spSetting.wantMessageSigned || !metadata.sp.isWantAssertionsSigned())) {
+ rawSaml = libsaml.constructSAMLSignature({
+ ...getConfig('Message'),
+ rawSamlMessage: rawSaml,
+ isMessageSigned: true,
+ transformationAlgorithms: spSetting.transformationAlgorithms,
+ signatureConfig: spSetting.signatureConfig || {
+ prefix: 'ds',
+ location: {
+ reference: "/*[local-name(.)='Response']/*[local-name(.)='Issuer']",
+ action: 'after',
+ },
+ },
+ });
+ }
- }
- throw new Error('ERR_GENERATE_POST_LOGIN_RESPONSE_MISSING_METADATA');
+ return { id: values.ID, context: base64Encode(rawSaml) };
}
/**
-* @desc Generate a base64 encoded logout request
-* @param {object} user current logged user (e.g. req.user)
-* @param {string} referenceTagXPath reference uri
-* @param {object} entity object includes both idp and sp
-* @param {function} customTagReplacement used when developers have their own login response template
-* @return {string} base64 encoded request
-*/
-function base64LogoutRequest(user, referenceTagXPath, entity, customTagReplacement?: (template: string) => BindingContext): BindingContext {
- const metadata = { init: entity.init.entityMeta, target: entity.target.entityMeta };
- const initSetting = entity.init.entitySetting;
- const nameIDFormat = initSetting.nameIDFormat;
- const selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat; let id: string = '';
- if (metadata && metadata.init && metadata.target) {
- let rawSamlRequest: string;
- if (initSetting.logoutRequestTemplate && customTagReplacement) {
- const template = customTagReplacement(initSetting.logoutRequestTemplate.context);
- id = get(template, 'id', null);
- rawSamlRequest = get(template, 'context', null);
- } else {
- id = initSetting.generateID();
- const tvalue: any = {
- ID: id,
- Destination: metadata.target.getSingleLogoutService(binding.redirect),
- Issuer: metadata.init.getEntityID(),
- IssueInstant: new Date().toISOString(),
- EntityID: metadata.init.getEntityID(),
- NameIDFormat: selectedNameIDFormat,
- NameID: user.logoutNameID,
- };
- rawSamlRequest = libsaml.replaceTagsByValue(libsaml.defaultLogoutRequestTemplate.context, tvalue);
- }
- if (entity.target.entitySetting.wantLogoutRequestSigned) {
- // Need to embeded XML signature
- const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm, transformationAlgorithms } = initSetting;
- return {
- id,
- context: libsaml.constructSAMLSignature({
- referenceTagXPath,
- privateKey,
- privateKeyPass,
- signatureAlgorithm,
- transformationAlgorithms,
- rawSamlMessage: rawSamlRequest,
- signingCert: metadata.init.getX509Certificate('signing'),
- signatureConfig: initSetting.signatureConfig || {
- prefix: 'ds',
- location: { reference: "/*[local-name(.)='LogoutRequest']/*[local-name(.)='Issuer']", action: 'after' },
- }
- }),
- };
- }
- return {
- id,
- context: utility.base64Encode(rawSamlRequest),
- };
- }
- throw new Error('ERR_GENERATE_POST_LOGOUT_REQUEST_MISSING_METADATA');
+ * @desc Generate a base64 encoded logout request
+ * @param {object} user current logged user (e.g. req.user)
+ * @param {string} referenceTagXPath reference uri
+ * @param {object} entity object includes both idp and sp
+ * @param {function} customTagReplacement used when developers have their own login response template
+ * @return {string} base64 encoded request
+ */
+function base64LogoutRequest(
+ user: Record,
+ referenceTagXPath: string,
+ entity: { init: Entity; target: Entity },
+ customTagReplacement?: CustomTagReplacement
+): BindingContext {
+ const metadata = { init: entity.init.getEntityMeta(), target: entity.target.getEntityMeta() };
+ if (!metadata.init || !metadata.target) {
+ throw new SamlifyError(SamlifyErrorCode.MissingMetadata);
+ }
+ const initSetting = entity.init.getEntitySettings();
+ const template = initSetting.logoutRequestTemplate ?? libsaml.defaultLogoutRequestTemplate;
+
+ const nameIDFormat = initSetting.nameIDFormat;
+ const selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
+
+ let values: Record = {
+ ID: entity.init.generateID(),
+ Destination: metadata.target.getSingleLogoutService(BindingNamespace.Redirect),
+ Issuer: metadata.init.getEntityID(),
+ IssueInstant: new Date().toISOString(),
+ EntityID: metadata.init.getEntityID(),
+ NameIDFormat: selectedNameIDFormat,
+ NameID: user.logoutNameID,
+ };
+
+ let rawSaml = template.context ?? '';
+ // perform custom replacement
+ if (customTagReplacement) {
+ [rawSaml = rawSaml, values = values] = customTagReplacement(rawSaml, values);
+ }
+ // pickup any remaining
+ rawSaml = libsaml.replaceTagsByValue(rawSaml, values);
+
+ if (entity.target.getEntitySettings().wantLogoutRequestSigned) {
+ // Need to embeded XML signature
+ const {
+ privateKey,
+ privateKeyPass,
+ requestSignatureAlgorithm: signatureAlgorithm,
+ transformationAlgorithms,
+ } = initSetting;
+ if (!privateKey) {
+ throw new SamlifyError(
+ SamlifyErrorCode.MissingPrivateKey,
+ `${entity.target.constructor.name} wants LogoutRequest signed, but ${entity.init.constructor.name} did not provide a 'privateKey'.`
+ );
+ }
+ return {
+ id: values.ID,
+ context: libsaml.constructSAMLSignature({
+ referenceTagXPath,
+ privateKey: privateKey.toString(),
+ privateKeyPass,
+ signatureAlgorithm,
+ transformationAlgorithms,
+ rawSamlMessage: rawSaml,
+ signingCert: metadata.init.getX509Certificate('signing'),
+ signatureConfig: initSetting.signatureConfig || {
+ prefix: 'ds',
+ location: {
+ reference: "/*[local-name(.)='LogoutRequest']/*[local-name(.)='Issuer']",
+ action: 'after',
+ },
+ },
+ }),
+ };
+ }
+ return { id: values.ID, context: base64Encode(rawSaml) };
}
/**
-* @desc Generate a base64 encoded logout response
-* @param {object} requestInfo corresponding request, used to obtain the id
-* @param {string} referenceTagXPath reference uri
-* @param {object} entity object includes both idp and sp
-* @param {function} customTagReplacement used when developers have their own login response template
-*/
-function base64LogoutResponse(requestInfo: any, entity: any, customTagReplacement: (template: string) => BindingContext): BindingContext {
- const metadata = {
- init: entity.init.entityMeta,
- target: entity.target.entityMeta,
- };
- let id: string = '';
- const initSetting = entity.init.entitySetting;
- if (metadata && metadata.init && metadata.target) {
- let rawSamlResponse;
- if (initSetting.logoutResponseTemplate) {
- const template = customTagReplacement(initSetting.logoutResponseTemplate.context);
- id = template.id;
- rawSamlResponse = template.context;
- } else {
- id = initSetting.generateID();
- const tvalue: any = {
- ID: id,
- Destination: metadata.target.getSingleLogoutService(binding.post),
- EntityID: metadata.init.getEntityID(),
- Issuer: metadata.init.getEntityID(),
- IssueInstant: new Date().toISOString(),
- StatusCode: StatusCode.Success,
- InResponseTo: get(requestInfo, 'extract.request.id', null)
- };
- rawSamlResponse = libsaml.replaceTagsByValue(libsaml.defaultLogoutResponseTemplate.context, tvalue);
- }
- if (entity.target.entitySetting.wantLogoutResponseSigned) {
- const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm, transformationAlgorithms } = initSetting;
- return {
- id,
- context: libsaml.constructSAMLSignature({
- isMessageSigned: true,
- transformationAlgorithms: transformationAlgorithms,
- privateKey,
- privateKeyPass,
- signatureAlgorithm,
- rawSamlMessage: rawSamlResponse,
- signingCert: metadata.init.getX509Certificate('signing'),
- signatureConfig: {
- prefix: 'ds',
- location: {
- reference: "/*[local-name(.)='LogoutResponse']/*[local-name(.)='Issuer']",
- action: 'after'
- }
- }
- }),
- };
- }
- return {
- id,
- context: utility.base64Encode(rawSamlResponse),
- };
- }
- throw new Error('ERR_GENERATE_POST_LOGOUT_RESPONSE_MISSING_METADATA');
+ * @desc Generate a base64 encoded logout response
+ * @param {object} requestInfo corresponding request, used to obtain the id
+ * @param {string} referenceTagXPath reference uri
+ * @param {object} entity object includes both idp and sp
+ * @param {function} customTagReplacement used when developers have their own login response template
+ */
+function base64LogoutResponse(
+ requestInfo: Record | null,
+ entity: { init: Entity; target: Entity },
+ customTagReplacement?: CustomTagReplacement
+): BindingContext {
+ const metadata = { init: entity.init.getEntityMeta(), target: entity.target.getEntityMeta() };
+ if (!metadata.init || !metadata.target) {
+ throw new SamlifyError(SamlifyErrorCode.MissingMetadata);
+ }
+ const initSetting = entity.init.getEntitySettings();
+ const template = initSetting.logoutResponseTemplate ?? libsaml.defaultLogoutResponseTemplate;
+
+ let values: Record = {
+ ID: entity.init.generateID(),
+ Destination: metadata.target.getSingleLogoutService(BindingNamespace.Post),
+ EntityID: metadata.init.getEntityID(),
+ Issuer: metadata.init.getEntityID(),
+ IssueInstant: new Date().toISOString(),
+ StatusCode: StatusCode.Success,
+ InResponseTo: get(requestInfo, 'extract.request.id', ''),
+ };
+
+ let rawSaml = template.context;
+ // perform custom replacement
+ if (customTagReplacement) {
+ [rawSaml = rawSaml, values = values] = customTagReplacement(rawSaml, values);
+ }
+ // pickup any remaining
+ rawSaml = libsaml.replaceTagsByValue(rawSaml, values);
+
+ if (entity.target.getEntitySettings().wantLogoutResponseSigned) {
+ const {
+ privateKey,
+ privateKeyPass,
+ requestSignatureAlgorithm: signatureAlgorithm,
+ transformationAlgorithms,
+ } = initSetting;
+ if (!privateKey) {
+ throw new SamlifyError(
+ SamlifyErrorCode.MissingPrivateKey,
+ `${entity.target.constructor.name} wants LogoutResponse signed, but ${entity.init.constructor.name} did not provide a 'privateKey'.`
+ );
+ }
+ return {
+ id: values.ID,
+ context: libsaml.constructSAMLSignature({
+ isMessageSigned: true,
+ transformationAlgorithms: transformationAlgorithms,
+ privateKey: privateKey.toString(),
+ privateKeyPass,
+ signatureAlgorithm,
+ rawSamlMessage: rawSaml,
+ signingCert: metadata.init.getX509Certificate('signing'),
+ signatureConfig: {
+ prefix: 'ds',
+ location: {
+ reference: "/*[local-name(.)='LogoutResponse']/*[local-name(.)='Issuer']",
+ action: 'after',
+ },
+ },
+ }),
+ };
+ }
+ return { id: values.ID, context: base64Encode(rawSaml) };
}
const postBinding = {
- base64LoginRequest,
- base64LoginResponse,
- base64LogoutRequest,
- base64LogoutResponse,
+ base64LoginRequest,
+ base64LoginResponse,
+ base64LogoutRequest,
+ base64LogoutResponse,
};
export default postBinding;
diff --git a/src/binding-redirect.ts b/src/binding-redirect.ts
index 1ac37ac0..9259aaf2 100644
--- a/src/binding-redirect.ts
+++ b/src/binding-redirect.ts
@@ -1,220 +1,257 @@
/**
-* @file binding-redirect.ts
-* @author tngan
-* @desc Binding-level API, declare the functions using Redirect binding
-*/
-import utility, { get } from './utility';
-import libsaml from './libsaml';
-import { BindingContext } from './entity';
-import { IdentityProvider as Idp } from './entity-idp';
-import { ServiceProvider as Sp } from './entity-sp';
-import * as url from 'url';
-import { wording, namespace } from './urn';
-
-const binding = wording.binding;
+ * @file binding-redirect.ts
+ * @author tngan
+ * @desc Binding-level API, declare the functions using Redirect binding
+ */
+import type { BindingContext, Entity } from './entity';
+import type { IdentityProvider } from './entity-idp';
+import type { ServiceProvider } from './entity-sp';
+import { SamlifyError, SamlifyErrorCode } from './error';
+import libsaml, { CustomTagReplacement } from './libsaml';
+import type { RequestSignatureAlgorithm } from './types';
+import { BindingNamespace, StatusCode, wording } from './urn';
+import { base64Encode, deflateString, get } from './utility';
+
const urlParams = wording.urlParams;
-export interface BuildRedirectConfig {
- baseUrl: string;
- type: string;
- isSigned: boolean;
- context: string;
- entitySetting: any;
- relayState?: string;
+interface BuildRedirectConfig {
+ baseUrl: string;
+ context: string;
+ relayState?: string;
+ signed?: {
+ privateKey: string | Buffer;
+ privateKeyPass?: string;
+ requestSignatureAlgorithm?: RequestSignatureAlgorithm;
+ };
+ type: 'SAMLRequest' | 'SAMLResponse' | 'LogoutRequest' | 'LogoutResponse';
}
/**
-* @private
-* @desc Helper of generating URL param/value pair
-* @param {string} param key
-* @param {string} value value of key
-* @param {boolean} first determine whether the param is the starting one in order to add query header '?'
-* @return {string}
-*/
+ * @private
+ * @desc Helper of generating URL param/value pair
+ * @param {string} param key
+ * @param {string} value value of key
+ * @param {boolean} first determine whether the param is the starting one in order to add query header '?'
+ * @return {string}
+ */
function pvPair(param: string, value: string, first?: boolean): string {
- return (first === true ? '?' : '&') + param + '=' + value;
+ return (first === true ? '?' : '&') + param + '=' + value;
}
/**
-* @private
-* @desc Refractored part of URL generation for login/logout request
-* @param {string} type
-* @param {boolean} isSigned
-* @param {string} rawSamlRequest
-* @param {object} entitySetting
-* @return {string}
-*/
+ * @private
+ * @desc Refractored part of URL generation for login/logout request
+ * @param {string} type
+ * @param {boolean} isSigned
+ * @param {string} rawSaml
+ * @param {Entity} entity
+ * @return {string}
+ */
function buildRedirectURL(opts: BuildRedirectConfig) {
- const {
- baseUrl,
- type,
- isSigned,
- context,
- entitySetting,
- } = opts;
- let { relayState = '' } = opts;
- const noParams = (url.parse(baseUrl).query || []).length === 0;
- const queryParam = libsaml.getQueryParamByType(type);
- // In general, this xmlstring is required to do deflate -> base64 -> urlencode
- const samlRequest = encodeURIComponent(utility.base64Encode(utility.deflateString(context)));
- if (relayState !== '') {
- relayState = pvPair(urlParams.relayState, encodeURIComponent(relayState));
- }
- if (isSigned) {
- const sigAlg = pvPair(urlParams.sigAlg, encodeURIComponent(entitySetting.requestSignatureAlgorithm));
- const octetString = samlRequest + relayState + sigAlg;
- return baseUrl + pvPair(queryParam, octetString, noParams) + pvPair(urlParams.signature, encodeURIComponent(libsaml.constructMessageSignature(queryParam + '=' + octetString, entitySetting.privateKey, entitySetting.privateKeyPass, undefined, entitySetting.requestSignatureAlgorithm)));
- }
- return baseUrl + pvPair(queryParam, samlRequest + relayState, noParams);
+ const { baseUrl, type, signed, context } = opts;
+ let { relayState = '' } = opts;
+ const noParams = Array.from(new URL(baseUrl).searchParams).length === 0;
+ const queryParam = libsaml.getQueryParamByType(type);
+ // In general, this xmlstring is required to do deflate -> base64 -> urlencode
+ const samlRequest = encodeURIComponent(base64Encode(deflateString(context)));
+ if (relayState !== '') {
+ relayState = pvPair(urlParams.relayState, encodeURIComponent(relayState));
+ }
+ if (signed) {
+ const { privateKey, privateKeyPass, requestSignatureAlgorithm = libsaml.defaultSignatureAlgorithm } = signed;
+ const sigAlg = pvPair(urlParams.sigAlg, encodeURIComponent(requestSignatureAlgorithm));
+ const octetString = samlRequest + relayState + sigAlg;
+ const signature = libsaml
+ .constructMessageSignature(`${queryParam}=${octetString}`, privateKey, privateKeyPass, requestSignatureAlgorithm)
+ .toString('base64');
+ return (
+ baseUrl + pvPair(queryParam, octetString, noParams) + pvPair(urlParams.signature, encodeURIComponent(signature))
+ );
+ }
+ return baseUrl + pvPair(queryParam, samlRequest + relayState, noParams);
}
/**
-* @desc Redirect URL for login request
-* @param {object} entity object includes both idp and sp
-* @param {function} customTagReplacement used when developers have their own login response template
-* @return {string} redirect URL
-*/
-function loginRequestRedirectURL(entity: { idp: Idp, sp: Sp }, customTagReplacement?: (template: string) => BindingContext): BindingContext {
-
- const metadata: any = { idp: entity.idp.entityMeta, sp: entity.sp.entityMeta };
- const spSetting: any = entity.sp.entitySetting;
- let id: string = '';
-
- if (metadata && metadata.idp && metadata.sp) {
- const base = metadata.idp.getSingleSignOnService(binding.redirect);
- let rawSamlRequest: string;
- if (spSetting.loginRequestTemplate && customTagReplacement) {
- const info = customTagReplacement(spSetting.loginRequestTemplate);
- id = get(info, 'id', null);
- rawSamlRequest = get(info, 'context', null);
- } else {
- const nameIDFormat = spSetting.nameIDFormat;
- const selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
- id = spSetting.generateID();
- rawSamlRequest = libsaml.replaceTagsByValue(libsaml.defaultLoginRequestTemplate.context, {
- ID: id,
- Destination: base,
- Issuer: metadata.sp.getEntityID(),
- IssueInstant: new Date().toISOString(),
- NameIDFormat: selectedNameIDFormat,
- AssertionConsumerServiceURL: metadata.sp.getAssertionConsumerService(binding.post),
- EntityID: metadata.sp.getEntityID(),
- AllowCreate: spSetting.allowCreate,
- } as any);
- }
- return {
- id,
- context: buildRedirectURL({
- context: rawSamlRequest,
- type: urlParams.samlRequest,
- isSigned: metadata.sp.isAuthnRequestSigned(),
- entitySetting: spSetting,
- baseUrl: base,
- relayState: spSetting.relayState,
- }),
- };
- }
- throw new Error('ERR_GENERATE_REDIRECT_LOGIN_REQUEST_MISSING_METADATA');
+ * @desc Redirect URL for login request
+ * @param {object} entity object includes both idp and sp
+ * @param {function} customTagReplacement used when developers have their own login response template
+ * @return {string} redirect URL
+ */
+function loginRequestRedirectURL(
+ entity: { idp: IdentityProvider; sp: ServiceProvider },
+ customTagReplacement?: CustomTagReplacement
+): BindingContext {
+ const metadata = { idp: entity.idp.getEntityMeta(), sp: entity.sp.getEntityMeta() };
+ if (!metadata.idp || !metadata.sp) {
+ throw new SamlifyError(SamlifyErrorCode.MissingMetadata);
+ }
+ const spSetting = entity.sp.getEntitySettings();
+ const template = spSetting.loginRequestTemplate ?? libsaml.defaultLoginRequestTemplate;
+
+ const baseUrl = metadata.idp.getSingleSignOnService(BindingNamespace.Redirect);
+ const nameIDFormat = spSetting.nameIDFormat;
+ const selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
+
+ let values: Record = {
+ ID: entity.sp.generateID(),
+ Destination: baseUrl,
+ Issuer: metadata.sp.getEntityID(),
+ IssueInstant: new Date().toISOString(),
+ NameIDFormat: selectedNameIDFormat,
+ AssertionConsumerServiceURL: metadata.sp.getAssertionConsumerService(BindingNamespace.Post),
+ EntityID: metadata.sp.getEntityID(),
+ AllowCreate: spSetting.allowCreate,
+ };
+
+ let rawSaml = template.context ?? '';
+ // perform custom replacement
+ if (customTagReplacement) {
+ [rawSaml = rawSaml, values = values] = customTagReplacement(rawSaml, values);
+ }
+ // pickup any remaining
+ rawSaml = libsaml.replaceTagsByValue(rawSaml, values);
+
+ const type = urlParams.samlRequest;
+ let signed: BuildRedirectConfig['signed'];
+ if (metadata.sp.isAuthnRequestSigned()) {
+ if (!spSetting.privateKey) {
+ throw new SamlifyError(
+ SamlifyErrorCode.MissingPrivateKey,
+ `${metadata.sp.constructor.name} wants ${type} signed, but it did not provide a 'privateKey'.`
+ );
+ }
+ signed = {
+ privateKey: spSetting.privateKey,
+ privateKeyPass: spSetting.privateKeyPass,
+ requestSignatureAlgorithm: spSetting.requestSignatureAlgorithm,
+ };
+ }
+
+ return {
+ id: values.ID,
+ context: buildRedirectURL({ baseUrl, context: rawSaml, relayState: spSetting.relayState, signed, type }),
+ };
}
/**
-* @desc Redirect URL for logout request
-* @param {object} user current logged user (e.g. req.user)
-* @param {object} entity object includes both idp and sp
-* @param {function} customTagReplacement used when developers have their own login response template
-* @return {string} redirect URL
-*/
-function logoutRequestRedirectURL(user, entity, relayState?: string, customTagReplacement?: (template: string, tags: object) => BindingContext): BindingContext {
- const metadata = { init: entity.init.entityMeta, target: entity.target.entityMeta };
- const initSetting = entity.init.entitySetting;
- let id: string = initSetting.generateID();
- const nameIDFormat = initSetting.nameIDFormat;
- const selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
-
- if (metadata && metadata.init && metadata.target) {
- const base = metadata.target.getSingleLogoutService(binding.redirect);
- let rawSamlRequest: string = '';
- const requiredTags = {
- ID: id,
- Destination: base,
- EntityID: metadata.init.getEntityID(),
- Issuer: metadata.init.getEntityID(),
- IssueInstant: new Date().toISOString(),
- NameIDFormat: selectedNameIDFormat,
- NameID: user.logoutNameID,
- SessionIndex: user.sessionIndex,
- };
- if (initSetting.logoutRequestTemplate && customTagReplacement) {
- const info = customTagReplacement(initSetting.logoutRequestTemplate, requiredTags);
- id = get(info, 'id', null);
- rawSamlRequest = get(info, 'context', null);
- } else {
- rawSamlRequest = libsaml.replaceTagsByValue(libsaml.defaultLogoutRequestTemplate.context, requiredTags as any);
- }
- return {
- id,
- context: buildRedirectURL({
- context: rawSamlRequest,
- relayState,
- type: urlParams.logoutRequest,
- isSigned: entity.target.entitySetting.wantLogoutRequestSigned,
- entitySetting: initSetting,
- baseUrl: base,
- }),
- };
- }
- throw new Error('ERR_GENERATE_REDIRECT_LOGOUT_REQUEST_MISSING_METADATA');
+ * @desc Redirect URL for logout request
+ * @param {object} user current logged user (e.g. req.user)
+ * @param {object} entity object includes both idp and sp
+ * @param {function} customTagReplacement used when developers have their own login response template
+ * @return {string} redirect URL
+ */
+function logoutRequestRedirectURL(
+ user: Record,
+ entity: { init: Entity; target: Entity },
+ relayState?: string,
+ customTagReplacement?: CustomTagReplacement
+): BindingContext {
+ const metadata = { init: entity.init.getEntityMeta(), target: entity.target.getEntityMeta() };
+ if (!metadata.init || !metadata.target) {
+ throw new SamlifyError(SamlifyErrorCode.MissingMetadata);
+ }
+ const initSetting = entity.init.getEntitySettings();
+ const template = initSetting.logoutRequestTemplate ?? libsaml.defaultLogoutRequestTemplate;
+
+ const nameIDFormat = initSetting.nameIDFormat;
+ const baseUrl = metadata.target.getSingleLogoutService(BindingNamespace.Redirect);
+
+ let values: Record = {
+ ID: entity.init.generateID(),
+ Destination: baseUrl,
+ EntityID: metadata.init.getEntityID(),
+ Issuer: metadata.init.getEntityID(),
+ IssueInstant: new Date().toISOString(),
+ NameIDFormat: Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat,
+ NameID: user.logoutNameID,
+ SessionIndex: user.sessionIndex,
+ };
+
+ let rawSaml = template.context ?? '';
+ // perform custom replacement
+ if (customTagReplacement) {
+ [rawSaml = rawSaml, values = values] = customTagReplacement(rawSaml, values);
+ }
+ // pickup any remaining
+ rawSaml = libsaml.replaceTagsByValue(rawSaml, values);
+
+ const type = urlParams.logoutRequest;
+ let signed: BuildRedirectConfig['signed'];
+ if (entity.target.getEntitySettings().wantLogoutRequestSigned) {
+ if (!initSetting.privateKey) {
+ throw new SamlifyError(
+ SamlifyErrorCode.MissingPrivateKey,
+ `${entity.target.constructor.name} wants ${type} signed, but ${entity.init.constructor.name} did not provide a 'privateKey'.`
+ );
+ }
+ signed = {
+ privateKey: initSetting.privateKey,
+ privateKeyPass: initSetting.privateKeyPass,
+ requestSignatureAlgorithm: initSetting.requestSignatureAlgorithm,
+ };
+ }
+
+ return { id: values.ID, context: buildRedirectURL({ baseUrl, context: rawSaml, relayState, signed, type }) };
}
/**
-* @desc Redirect URL for logout response
-* @param {object} requescorresponding request, used to obtain the id
-* @param {object} entity object includes both idp and sp
-* @param {function} customTagReplacement used when developers have their own login response template
-*/
-function logoutResponseRedirectURL(requestInfo: any, entity: any, relayState?: string, customTagReplacement?: (template: string) => BindingContext): BindingContext {
- const metadata = {
- init: entity.init.entityMeta,
- target: entity.target.entityMeta,
- };
- const initSetting = entity.init.entitySetting;
- let id: string = initSetting.generateID();
- if (metadata && metadata.init && metadata.target) {
- const base = metadata.target.getSingleLogoutService(binding.redirect);
- let rawSamlResponse: string;
- if (initSetting.logoutResponseTemplate && customTagReplacement) {
- const template = customTagReplacement(initSetting.logoutResponseTemplate);
- id = get(template, 'id', null);
- rawSamlResponse = get(template, 'context', null);
- } else {
- const tvalue: any = {
- ID: id,
- Destination: base,
- Issuer: metadata.init.getEntityID(),
- EntityID: metadata.init.getEntityID(),
- IssueInstant: new Date().toISOString(),
- StatusCode: namespace.statusCode.success,
- };
- if (requestInfo && requestInfo.extract && requestInfo.extract.logoutRequest) {
- tvalue.InResponseTo = requestInfo.extract.logoutRequest.id;
- }
- rawSamlResponse = libsaml.replaceTagsByValue(libsaml.defaultLogoutResponseTemplate.context, tvalue);
- }
- return {
- id,
- context: buildRedirectURL({
- baseUrl: base,
- type: urlParams.logoutResponse,
- isSigned: entity.target.entitySetting.wantLogoutResponseSigned,
- context: rawSamlResponse,
- entitySetting: initSetting,
- relayState,
- }),
- };
- }
- throw new Error('ERR_GENERATE_REDIRECT_LOGOUT_RESPONSE_MISSING_METADATA');
+ * @desc Redirect URL for logout response
+ * @param {Record|null} requescorresponding request, used to obtain the id
+ * @param {object} entity object includes both idp and sp
+ * @param {function} customTagReplacement used when developers have their own login response template
+ */
+function logoutResponseRedirectURL(
+ requestInfo: Record | null,
+ entity: { init: Entity; target: Entity },
+ relayState?: string,
+ customTagReplacement?: CustomTagReplacement
+): BindingContext {
+ const metadata = { init: entity.init.getEntityMeta(), target: entity.target.getEntityMeta() };
+ if (!metadata.init || !metadata.target) {
+ throw new SamlifyError(SamlifyErrorCode.MissingMetadata);
+ }
+ const initSetting = entity.init.getEntitySettings();
+ const template = initSetting.logoutResponseTemplate ?? libsaml.defaultLogoutResponseTemplate;
+
+ const baseUrl = metadata.target.getSingleLogoutService(BindingNamespace.Redirect);
+
+ let values: Record = {
+ ID: entity.init.generateID(),
+ Destination: baseUrl,
+ Issuer: metadata.init.getEntityID(),
+ EntityID: metadata.init.getEntityID(),
+ IssueInstant: new Date().toISOString(),
+ StatusCode: StatusCode.Success,
+ InResponseTo: get(requestInfo, 'extract.logoutRequest.id', ''),
+ };
+
+ let rawSaml = template.context;
+ // perform custom replacement
+ if (customTagReplacement) {
+ [rawSaml = rawSaml, values = values] = customTagReplacement(rawSaml, values);
+ }
+ // pickup any remaining
+ rawSaml = libsaml.replaceTagsByValue(rawSaml, values);
+
+ const type = urlParams.logoutResponse;
+ let signed: BuildRedirectConfig['signed'];
+ if (entity.target.getEntitySettings().wantLogoutResponseSigned) {
+ if (!initSetting.privateKey) {
+ throw new SamlifyError(
+ SamlifyErrorCode.MissingPrivateKey,
+ `${entity.target.constructor.name} wants ${type} signed, but ${entity.init.constructor.name} did not provide a 'privateKey'.`
+ );
+ }
+ signed = {
+ privateKey: initSetting.privateKey,
+ privateKeyPass: initSetting.privateKeyPass,
+ requestSignatureAlgorithm: initSetting.requestSignatureAlgorithm,
+ };
+ }
+
+ return { id: values.ID, context: buildRedirectURL({ baseUrl, context: rawSaml, relayState, signed, type }) };
}
const redirectBinding = {
- loginRequestRedirectURL,
- logoutRequestRedirectURL,
- logoutResponseRedirectURL,
+ loginRequestRedirectURL,
+ logoutRequestRedirectURL,
+ logoutResponseRedirectURL,
};
export default redirectBinding;
diff --git a/src/entity-idp.ts b/src/entity-idp.ts
index 01634caf..8a4f0769 100644
--- a/src/entity-idp.ts
+++ b/src/entity-idp.ts
@@ -1,110 +1,98 @@
/**
-* @file entity-idp.ts
-* @author tngan
-* @desc Declares the actions taken by identity provider
-*/
-import Entity, { ESamlHttpRequest } from './entity';
-import {
- ServiceProviderConstructor as ServiceProvider,
- ServiceProviderMetadata,
- IdentityProviderMetadata,
- IdentityProviderSettings,
-} from './types';
-import libsaml from './libsaml';
-import { namespace } from './urn';
+ * @file entity-idp.ts
+ * @author tngan
+ * @desc Declares the actions taken by identity provider
+ */
import postBinding from './binding-post';
-import { flow, FlowResult } from './flow';
-import { isString } from './utility';
-import { BindingContext } from './entity';
+import { Entity, ESamlHttpRequest } from './entity';
+import type { ServiceProvider } from './entity-sp';
+import { SamlifyError, SamlifyErrorCode } from './error';
+import { flow } from './flow';
+import type { CustomTagReplacement } from './libsaml';
+import metadataIdp, { MetadataIdp } from './metadata-idp';
+import type { IdentityProviderSettings } from './types';
+import { BindingNamespace, ParserType } from './urn';
/**
* Identity prvider can be configured using either metadata importing or idpSetting
*/
-export default function(props: IdentityProviderSettings) {
- return new IdentityProvider(props);
+export default function (props: IdentityProviderSettings) {
+ return new IdentityProvider(props);
}
/**
- * Identity prvider can be configured using either metadata importing or idpSetting
+ * Identity prvider can be configured using either metadata importing or idpSettings
*/
-export class IdentityProvider extends Entity {
-
- entityMeta: IdentityProviderMetadata;
-
- constructor(idpSetting: IdentityProviderSettings) {
- const defaultIdpEntitySetting = {
- wantAuthnRequestsSigned: false,
- tagPrefix: {
- encryptedAssertion: 'saml',
- },
- };
- const entitySetting = Object.assign(defaultIdpEntitySetting, idpSetting);
- // build attribute part
- if (idpSetting.loginResponseTemplate) {
- if (isString(idpSetting.loginResponseTemplate.context) && Array.isArray(idpSetting.loginResponseTemplate.attributes)) {
- const replacement = {
- AttributeStatement: libsaml.attributeStatementBuilder(idpSetting.loginResponseTemplate.attributes),
- };
- entitySetting.loginResponseTemplate = {
- ...entitySetting.loginResponseTemplate,
- context: libsaml.replaceTagsByValue(entitySetting.loginResponseTemplate!.context, replacement),
- };
- } else {
- console.warn('Invalid login response template');
- }
- }
- super(entitySetting, 'idp');
- }
+export class IdentityProvider extends Entity {
+ constructor(idpSettings: IdentityProviderSettings) {
+ const entitySettings = Object.assign(
+ {
+ wantAuthnRequestsSigned: false,
+ tagPrefix: {
+ encryptedAssertion: 'saml',
+ },
+ },
+ idpSettings
+ );
+ const entityMeta = metadataIdp(entitySettings.metadata || entitySettings);
+ // setting with metadata has higher precedence
+ entitySettings.wantAuthnRequestsSigned = entityMeta.isWantAuthnRequestsSigned();
+ super(entitySettings, entityMeta);
+ }
- /**
- * @desc Generates the login response for developers to design their own method
- * @param sp object of service provider
- * @param requestInfo corresponding request, used to obtain the id
- * @param binding protocol binding
- * @param user current logged user (e.g. req.user)
- * @param customTagReplacement used when developers have their own login response template
- * @param encryptThenSign whether or not to encrypt then sign first (if signing)
- */
- public async createLoginResponse(
- sp: ServiceProvider,
- requestInfo: { [key: string]: any },
- binding: string,
- user: { [key: string]: any },
- customTagReplacement?: (template: string) => BindingContext,
- encryptThenSign?: boolean,
- ) {
- const protocol = namespace.binding[binding];
- // can only support post binding for login response
- if (protocol === namespace.binding.post) {
- const context = await postBinding.base64LoginResponse(requestInfo, {
- idp: this,
- sp,
- }, user, customTagReplacement, encryptThenSign);
- return {
- ...context,
- entityEndpoint: (sp.entityMeta as ServiceProviderMetadata).getAssertionConsumerService(binding),
- type: 'SAMLResponse'
- };
- }
- throw new Error('ERR_CREATE_RESPONSE_UNDEFINED_BINDING');
- }
+ /**
+ * @desc Generates the login response for developers to design their own method
+ * @param sp object of service provider
+ * @param requestInfo corresponding request, used to obtain the id
+ * @param binding protocol binding
+ * @param user current logged user (e.g. req.user)
+ * @param customTagReplacement used when developers have their own login response template
+ * @param encryptThenSign whether or not to encrypt then sign first (if signing)
+ */
+ public async createLoginResponse(
+ sp: ServiceProvider,
+ requestInfo: Record,
+ protocol: BindingNamespace,
+ user: { [key: string]: any },
+ customTagReplacement?: CustomTagReplacement,
+ encryptThenSign?: boolean
+ ) {
+ // can only support post binding for login response
+ if (protocol === BindingNamespace.Post) {
+ const context = await postBinding.base64LoginResponse(
+ requestInfo,
+ {
+ idp: this,
+ sp,
+ },
+ user,
+ customTagReplacement,
+ encryptThenSign
+ );
+ return {
+ ...context,
+ entityEndpoint: sp.getEntityMeta().getAssertionConsumerService(protocol),
+ type: 'SAMLResponse',
+ };
+ }
+ throw new SamlifyError(SamlifyErrorCode.UnsupportedBinding);
+ }
- /**
- * Validation of the parsed URL parameters
- * @param sp ServiceProvider instance
- * @param binding Protocol binding
- * @param req RequesmessageSigningOrderst
- */
- parseLoginRequest(sp: ServiceProvider, binding: string, req: ESamlHttpRequest) {
- const self = this;
- return flow({
- from: sp,
- self: self,
- checkSignature: self.entityMeta.isWantAuthnRequestsSigned(),
- parserType: 'SAMLRequest',
- type: 'login',
- binding: binding,
- request: req
- });
- }
+ /**
+ * Validation of the parsed URL parameters
+ * @param sp ServiceProvider instance
+ * @param binding Protocol binding
+ * @param req RequesmessageSigningOrderst
+ */
+ parseLoginRequest(sp: ServiceProvider, binding: BindingNamespace, req: ESamlHttpRequest) {
+ return flow({
+ from: sp,
+ self: this,
+ checkSignature: this.entityMeta.isWantAuthnRequestsSigned(),
+ parserType: ParserType.SAMLRequest,
+ type: 'login',
+ binding: binding,
+ request: req,
+ });
+ }
}
diff --git a/src/entity-sp.ts b/src/entity-sp.ts
index c89e33bc..4a6137f3 100644
--- a/src/entity-sp.ts
+++ b/src/entity-sp.ts
@@ -1,28 +1,24 @@
/**
-* @file entity-sp.ts
-* @author tngan
-* @desc Declares the actions taken by service provider
-*/
-import Entity, {
- BindingContext,
- PostBindingContext,
- ESamlHttpRequest,
-} from './entity';
-import {
- IdentityProviderConstructor as IdentityProvider,
- ServiceProviderMetadata,
- ServiceProviderSettings,
-} from './types';
-import { namespace } from './urn';
-import redirectBinding from './binding-redirect';
+ * @file entity-sp.ts
+ * @author tngan
+ * @desc Declares the actions taken by service provider
+ */
import postBinding from './binding-post';
-import { flow, FlowResult } from './flow';
+import redirectBinding from './binding-redirect';
+import { BindingContext, Entity, ESamlHttpRequest, PostBindingContext } from './entity';
+import type { IdentityProvider } from './entity-idp';
+import { SamlifyError, SamlifyErrorCode } from './error';
+import { flow } from './flow';
+import type { CustomTagReplacement } from './libsaml';
+import metadataSp, { MetadataSp } from './metadata-sp';
+import type { ServiceProviderSettings } from './types';
+import { BindingNamespace, ParserType } from './urn';
/*
* @desc interface function
*/
-export default function(props: ServiceProviderSettings) {
- return new ServiceProvider(props);
+export default function (props: ServiceProviderSettings) {
+ return new ServiceProvider(props);
}
/**
@@ -30,73 +26,79 @@ export default function(props: ServiceProviderSettings) {
* @param {object} spSettingimport { FlowResult } from '../types/src/flow.d';
*/
-export class ServiceProvider extends Entity {
- entityMeta: ServiceProviderMetadata;
-
- /**
- * @desc Inherited from Entity
- * @param {object} spSetting setting of service provider
- */
- constructor(spSetting: ServiceProviderSettings) {
- const entitySetting = Object.assign({
- authnRequestsSigned: false,
- wantAssertionsSigned: false,
- wantMessageSigned: false,
- }, spSetting);
- super(entitySetting, 'sp');
- }
-
- /**
- * @desc Generates the login request for developers to design their own method
- * @param {IdentityProvider} idp object of identity provider
- * @param {string} binding protocol binding
- * @param {function} customTagReplacement used when developers have their own login response template
- */
- public createLoginRequest(
- idp: IdentityProvider,
- binding = 'redirect',
- customTagReplacement?: (template: string) => BindingContext,
- ): BindingContext | PostBindingContext {
- const nsBinding = namespace.binding;
- const protocol = nsBinding[binding];
- if (this.entityMeta.isAuthnRequestSigned() !== idp.entityMeta.isWantAuthnRequestsSigned()) {
- throw new Error('ERR_METADATA_CONFLICT_REQUEST_SIGNED_FLAG');
- }
+export class ServiceProvider extends Entity {
+ /**
+ * @desc Inherited from Entity
+ * @param {object} spSettings settings of service provider
+ */
+ constructor(spSettings: ServiceProviderSettings) {
+ const entitySettings = Object.assign(
+ {
+ authnRequestsSigned: false,
+ wantAssertionsSigned: false,
+ wantMessageSigned: false,
+ },
+ spSettings
+ );
+ const entityMeta = metadataSp(entitySettings.metadata || entitySettings);
+ // setting with metadata has higher precedence
+ entitySettings.authnRequestsSigned = entityMeta.isAuthnRequestSigned();
+ entitySettings.wantAssertionsSigned = entityMeta.isWantAssertionsSigned();
+ super(entitySettings, entityMeta);
+ }
- if (protocol === nsBinding.redirect) {
- return redirectBinding.loginRequestRedirectURL({ idp, sp: this }, customTagReplacement);
- }
+ /**
+ * @desc Generates the login request for developers to design their own method
+ * @param {IdentityProvider} idp object of identity provider
+ * @param {string} binding protocol binding
+ * @param {function} customTagReplacement used when developers have their own login response template
+ */
+ public createLoginRequest(
+ idp: IdentityProvider,
+ protocol = BindingNamespace.Redirect,
+ customTagReplacement?: CustomTagReplacement
+ ): BindingContext | PostBindingContext {
+ const idpMeta = idp.getEntityMeta();
+ if (this.entityMeta.isAuthnRequestSigned() !== idpMeta.isWantAuthnRequestsSigned()) {
+ throw new SamlifyError(SamlifyErrorCode.MetadataConflictRequestSignedFlag);
+ }
- if (protocol === nsBinding.post) {
- const context = postBinding.base64LoginRequest("/*[local-name(.)='AuthnRequest']", { idp, sp: this }, customTagReplacement);
- return {
- ...context,
- relayState: this.entitySetting.relayState,
- entityEndpoint: idp.entityMeta.getSingleSignOnService(binding) as string,
- type: 'SAMLRequest',
- };
- }
- // Will support artifact in the next release
- throw new Error('ERR_SP_LOGIN_REQUEST_UNDEFINED_BINDING');
- }
+ if (protocol === BindingNamespace.Redirect) {
+ return redirectBinding.loginRequestRedirectURL({ idp, sp: this }, customTagReplacement);
+ }
- /**
- * @desc Validation of the parsed the URL parameters
- * @param {IdentityProvider} idp object of identity provider
- * @param {string} binding protocol binding
- * @param {request} req request
- */
- public parseLoginResponse(idp, binding, request: ESamlHttpRequest) {
- const self = this;
- return flow({
- from: idp,
- self: self,
- checkSignature: true, // saml response must have signature
- parserType: 'SAMLResponse',
- type: 'login',
- binding: binding,
- request: request
- });
- }
+ if (protocol === BindingNamespace.Post) {
+ const context = postBinding.base64LoginRequest(
+ "/*[local-name(.)='AuthnRequest']",
+ { idp, sp: this },
+ customTagReplacement
+ );
+ return {
+ ...context,
+ relayState: this.entitySettings.relayState,
+ entityEndpoint: idpMeta.getSingleSignOnService(protocol),
+ type: 'SAMLRequest',
+ };
+ }
+ // Will support artifact in the next release
+ throw new SamlifyError(SamlifyErrorCode.UnsupportedBinding);
+ }
+ /**
+ * @desc Validation of the parsed the URL parameters
+ * @param {IdentityProvider} idp object of identity provider
+ * @param {BindingNamespace} protocol protocol binding
+ * @param {request} req request
+ */
+ public parseLoginResponse(idp: IdentityProvider, protocol: BindingNamespace, request: ESamlHttpRequest) {
+ return flow({
+ from: idp,
+ self: this,
+ checkSignature: true, // saml response must have signature
+ parserType: ParserType.SAMLResponse,
+ type: 'login',
+ binding: protocol,
+ request: request,
+ });
+ }
}
diff --git a/src/entity.ts b/src/entity.ts
index 28da535f..9ae6f513 100644
--- a/src/entity.ts
+++ b/src/entity.ts
@@ -1,17 +1,18 @@
/**
-* @file entity.ts
-* @author tngan
-* @desc An abstraction for identity provider and service provider.
-*/
-import { isString, isNonEmptyArray } from './utility';
-import { namespace, wording, algorithms, messageConfigurations } from './urn';
-import * as uuid from 'uuid';
-import IdpMetadata, { IdpMetadata as IdpMetadataConstructor } from './metadata-idp';
-import SpMetadata, { SpMetadata as SpMetadataConstructor } from './metadata-sp';
-import redirectBinding from './binding-redirect';
+ * @file entity.ts
+ * @author tngan
+ * @desc An abstraction for identity provider and service provider.
+ */
+import { v4 as uuid } from 'uuid';
import postBinding from './binding-post';
-import { MetadataIdpConstructor, MetadataSpConstructor, EntitySetting } from './types';
+import redirectBinding from './binding-redirect';
+import { SamlifyError, SamlifyErrorCode } from './error';
import { flow, FlowResult } from './flow';
+import type { CustomTagReplacement } from './libsaml';
+import type { Metadata } from './metadata';
+import type { EntitySettings } from './types';
+import { algorithms, BindingNamespace, messageConfigurations, ParserType } from './urn';
+import { isNonEmptyArray, isString } from './utility';
const dataEncryptionAlgorithm = algorithms.encryption.data;
const keyEncryptionAlgorithm = algorithms.encryption.key;
@@ -19,214 +20,219 @@ const signatureAlgorithms = algorithms.signature;
const messageSigningOrders = messageConfigurations.signingOrder;
const defaultEntitySetting = {
- wantLogoutResponseSigned: false,
- messageSigningOrder: messageSigningOrders.SIGN_THEN_ENCRYPT,
- wantLogoutRequestSigned: false,
- allowCreate: false,
- isAssertionEncrypted: false,
- requestSignatureAlgorithm: signatureAlgorithms.RSA_SHA256,
- dataEncryptionAlgorithm: dataEncryptionAlgorithm.AES_256,
- keyEncryptionAlgorithm: keyEncryptionAlgorithm.RSA_1_5,
- generateID: (): string => ('_' + uuid.v4()),
- relayState: '',
-};
+ wantLogoutResponseSigned: false,
+ messageSigningOrder: messageSigningOrders.SIGN_THEN_ENCRYPT,
+ wantLogoutRequestSigned: false,
+ allowCreate: false,
+ isAssertionEncrypted: false,
+ requestSignatureAlgorithm: signatureAlgorithms.RSA_SHA256,
+ dataEncryptionAlgorithm: dataEncryptionAlgorithm.AES_256,
+ keyEncryptionAlgorithm: keyEncryptionAlgorithm.RSA_OAEP_MGF1P,
+ generateID: (): string => `_${uuid()}`,
+ relayState: '',
+} as const;
export interface ESamlHttpRequest {
- query?: any;
- body?: any;
- octetString?: string;
+ query?: any;
+ body?: any;
+ octetString?: string;
}
export interface BindingContext {
- context: string;
- id: string;
+ context: string;
+ id: string;
}
export interface PostBindingContext extends BindingContext {
- relayState?: string;
- entityEndpoint: string;
- type: string;
+ relayState?: string;
+ entityEndpoint: string;
+ type: 'SAMLRequest' | 'SAMLResponse';
}
export interface ParseResult {
- samlContent: string;
- extract: any;
- sigAlg: string;
+ samlContent: string;
+ extract: any;
+ sigAlg: string;
}
-export type EntityConstructor = (MetadataIdpConstructor | MetadataSpConstructor)
- & { metadata?: string | Buffer };
-
-export default class Entity {
- entitySetting: EntitySetting;
- entityType: string;
- entityMeta: IdpMetadataConstructor | SpMetadataConstructor;
-
- /**
- * @param entitySetting
- * @param entityMeta is the entity metadata, deprecated after 2.0
- */
- constructor(entitySetting: EntityConstructor, entityType: 'idp' | 'sp') {
- this.entitySetting = Object.assign({}, defaultEntitySetting, entitySetting);
- const metadata = entitySetting.metadata || entitySetting;
- switch (entityType) {
- case 'idp':
- this.entityMeta = IdpMetadata(metadata);
- // setting with metadata has higher precedence
- this.entitySetting.wantAuthnRequestsSigned = this.entityMeta.isWantAuthnRequestsSigned();
- this.entitySetting.nameIDFormat = this.entityMeta.getNameIDFormat() || this.entitySetting.nameIDFormat;
- break;
- case 'sp':
- this.entityMeta = SpMetadata(metadata);
- // setting with metadata has higher precedence
- this.entitySetting.authnRequestsSigned = this.entityMeta.isAuthnRequestSigned();
- this.entitySetting.wantAssertionsSigned = this.entityMeta.isWantAssertionsSigned();
- this.entitySetting.nameIDFormat = this.entityMeta.getNameIDFormat() || this.entitySetting.nameIDFormat;
- break;
- default:
- throw new Error('ERR_UNDEFINED_ENTITY_TYPE');
- }
- }
-
- /**
- * @desc Returns the setting of entity
- * @return {object}
- */
- getEntitySetting() {
- return this.entitySetting;
- }
- /**
- * @desc Returns the xml string of entity metadata
- * @return {string}
- */
- getMetadata(): string {
- return this.entityMeta.getMetadata();
- }
+export class Entity {
+ protected entitySettings: Settings;
+ /**
+ * @param entitySettings
+ * @param Meta
+ */
+ constructor(entitySettings: Settings, protected entityMeta: Meta) {
+ // setting with metadata has higher precedence
+ entitySettings.nameIDFormat = entityMeta.getNameIDFormat() || entitySettings.nameIDFormat;
+ this.entitySettings = Object.assign({}, defaultEntitySetting, entitySettings);
+ }
+ /**
+ * @desc Returns the setting of entity
+ * @return {string}
+ */
+ generateID(): string {
+ if (this.entitySettings.generateID) return this.entitySettings.generateID();
+ return defaultEntitySetting.generateID();
+ }
+ /**
+ * @desc Returns the setting of entity
+ * @return {EntitySettings}
+ */
+ getEntitySettings(): Settings {
+ return this.entitySettings;
+ }
+ /**
+ * @desc Returns the meta object of entity
+ * @return {Metadata}
+ */
+ getEntityMeta(): Meta {
+ return this.entityMeta;
+ }
+ /**
+ * @desc Returns the xml string of entity metadata
+ * @return {string}
+ */
+ getMetadata(): string {
+ return this.entityMeta.getMetadata();
+ }
- /**
- * @desc Exports the entity metadata into specified folder
- * @param {string} exportFile indicates the file name
- */
- exportMetadata(exportFile: string) {
- return this.entityMeta.exportMetadata(exportFile);
- }
+ /**
+ * @desc Exports the entity metadata into specified folder
+ * @param {string} exportFile indicates the file name
+ */
+ exportMetadata(exportFile: string) {
+ return this.entityMeta.exportMetadata(exportFile);
+ }
- /** * @desc Verify fields with the one specified in metadata
- * @param {string/[string]} field is a string or an array of string indicating the field value in SAML message
- * @param {string} metaField is a string indicating the same field specified in metadata
- * @return {boolean} True/False
- */
- verifyFields(field: string | string[], metaField: string): boolean {
- if (isString(field)) {
- return field === metaField;
- }
- if (isNonEmptyArray(field)) {
- let res = true;
- (field as string[]).forEach(f => {
- if (f !== metaField) {
- res = false;
- return;
- }
- });
- return res;
- }
- return false;
- }
- /** @desc Generates the logout request for developers to design their own method
- * @param {ServiceProvider} sp object of service provider
- * @param {string} binding protocol binding
- * @param {object} user current logged user (e.g. user)
- * @param {string} relayState the URL to which to redirect the user when logout is complete
- * @param {function} customTagReplacement used when developers have their own login response template
- */
- createLogoutRequest(targetEntity, binding, user, relayState = '', customTagReplacement?): BindingContext | PostBindingContext {
- if (binding === wording.binding.redirect) {
- return redirectBinding.logoutRequestRedirectURL(user, {
- init: this,
- target: targetEntity,
- }, relayState, customTagReplacement);
- }
- if (binding === wording.binding.post) {
- const entityEndpoint = targetEntity.entityMeta.getSingleLogoutService(binding);
- const context = postBinding.base64LogoutRequest(user, "/*[local-name(.)='LogoutRequest']", { init: this, target: targetEntity }, customTagReplacement);
- return {
- ...context,
- relayState,
- entityEndpoint,
- type: 'SAMLRequest',
- };
- }
- // Will support artifact in the next release
- throw new Error('ERR_UNDEFINED_BINDING');
- }
+ /** * @desc Verify fields with the one specified in metadata
+ * @param {string/[string]} field is a string or an array of string indicating the field value in SAML message
+ * @param {string} metaField is a string indicating the same field specified in metadata
+ * @return {boolean} True/False
+ */
+ verifyFields(field: string | string[], metaField: string): boolean {
+ if (isString(field)) {
+ return field === metaField;
+ }
+ if (isNonEmptyArray(field)) {
+ let res = true;
+ field.forEach((f) => {
+ if (f !== metaField) {
+ res = false;
+ return;
+ }
+ });
+ return res;
+ }
+ return false;
+ }
+ /** @desc Generates the logout request for developers to design their own method
+ * @param {ServiceProvider} sp object of service provider
+ * @param {string} binding protocol binding
+ * @param {object} user current logged user (e.g. user)
+ * @param {string} relayState the URL to which to redirect the user when logout is complete
+ * @param {function} customTagReplacement used when developers have their own login response template
+ */
+ createLogoutRequest(
+ target: Entity,
+ protocol: BindingNamespace,
+ user: Record,
+ relayState = '',
+ customTagReplacement?: CustomTagReplacement
+ ): BindingContext | PostBindingContext {
+ if (protocol === BindingNamespace.Redirect) {
+ return redirectBinding.logoutRequestRedirectURL(user, { init: this, target }, relayState, customTagReplacement);
+ }
+ if (protocol === BindingNamespace.Post) {
+ const entityEndpoint = target.entityMeta.getSingleLogoutService(protocol);
+ const context = postBinding.base64LogoutRequest(
+ user,
+ "/*[local-name(.)='LogoutRequest']",
+ { init: this, target },
+ customTagReplacement
+ );
+ return {
+ ...context,
+ relayState,
+ entityEndpoint,
+ type: 'SAMLRequest',
+ };
+ }
+ // Will support artifact in the next release
+ throw new SamlifyError(SamlifyErrorCode.UnsupportedBinding);
+ }
- /**
- * @desc Generates the logout response for developers to design their own method
- * @param {IdentityProvider} idp object of identity provider
- * @param {object} requestInfo corresponding request, used to obtain the id
- * @param {string} relayState the URL to which to redirect the user when logout is complete.
- * @param {string} binding protocol binding
- * @param {function} customTagReplacement used when developers have their own login response template
- */
- createLogoutResponse(target, requestInfo, binding, relayState = '', customTagReplacement?): BindingContext | PostBindingContext {
- const protocol = namespace.binding[binding];
- if (protocol === namespace.binding.redirect) {
- return redirectBinding.logoutResponseRedirectURL(requestInfo, {
- init: this,
- target,
- }, relayState, customTagReplacement);
- }
- if (protocol === namespace.binding.post) {
- const context = postBinding.base64LogoutResponse(requestInfo, {
- init: this,
- target,
- }, customTagReplacement);
- return {
- ...context,
- relayState,
- entityEndpoint: target.entityMeta.getSingleLogoutService(binding),
- type: 'SAMLResponse',
- };
- }
- throw new Error('ERR_CREATE_LOGOUT_RESPONSE_UNDEFINED_BINDING');
- }
+ /**
+ * @desc Generates the logout response for developers to design their own method
+ * @param {IdentityProvider} idp object of identity provider
+ * @param {object|null} requestInfo corresponding request, used to obtain the id
+ * @param {string} relayState the URL to which to redirect the user when logout is complete.
+ * @param {string} binding protocol binding
+ * @param {function} customTagReplacement used when developers have their own login response template
+ */
+ createLogoutResponse(
+ target: Entity,
+ requestInfo: Record | null,
+ protocol: BindingNamespace,
+ relayState = '',
+ customTagReplacement?: CustomTagReplacement
+ ): BindingContext | PostBindingContext {
+ if (protocol === BindingNamespace.Redirect) {
+ return redirectBinding.logoutResponseRedirectURL(
+ requestInfo,
+ {
+ init: this,
+ target,
+ },
+ relayState,
+ customTagReplacement
+ );
+ }
+ if (protocol === BindingNamespace.Post) {
+ const context = postBinding.base64LogoutResponse(requestInfo, { init: this, target }, customTagReplacement);
+ return {
+ ...context,
+ relayState,
+ entityEndpoint: target.entityMeta.getSingleLogoutService(protocol),
+ type: 'SAMLResponse',
+ };
+ }
+ throw new SamlifyError(SamlifyErrorCode.UnsupportedBinding);
+ }
- /**
- * @desc Validation of the parsed the URL parameters
- * @param {IdentityProvider} idp object of identity provider
- * @param {string} binding protocol binding
- * @param {request} req request
- * @return {Promise}
- */
- parseLogoutRequest(from, binding, request: ESamlHttpRequest) {
- const self = this;
- return flow({
- from: from,
- self: self,
- type: 'logout',
- parserType: 'LogoutRequest',
- checkSignature: this.entitySetting.wantLogoutRequestSigned,
- binding: binding,
- request: request,
- });
- }
- /**
- * @desc Validation of the parsed the URL parameters
- * @param {object} config config for the parser
- * @param {string} binding protocol binding
- * @param {request} req request
- * @return {Promise}
- */
- parseLogoutResponse(from, binding, request: ESamlHttpRequest) {
- const self = this;
- return flow({
- from: from,
- self: self,
- type: 'logout',
- parserType: 'LogoutResponse',
- checkSignature: self.entitySetting.wantLogoutResponseSigned,
- binding: binding,
- request: request
- });
- }
+ /**
+ * @desc Validation of the parsed the URL parameters
+ * @param {IdentityProvider} idp object of identity provider
+ * @param {BindingNamespace} protocol protocol binding
+ * @param {request} req request
+ * @return {Promise}
+ */
+ parseLogoutRequest(from: Entity, protocol: BindingNamespace, request: ESamlHttpRequest): Promise {
+ return flow({
+ from: from,
+ self: this,
+ type: 'logout',
+ parserType: ParserType.LogoutRequest,
+ checkSignature: this.entitySettings.wantLogoutRequestSigned,
+ binding: protocol,
+ request: request,
+ });
+ }
+ /**
+ * @desc Validation of the parsed the URL parameters
+ * @param {object} config config for the parser
+ * @param {BindingNamespace} protocol protocol binding
+ * @param {ESamlHttpRequest} req request
+ * @return {Promise}
+ */
+ parseLogoutResponse(from: Entity, protocol: BindingNamespace, request: ESamlHttpRequest): Promise {
+ return flow({
+ from: from,
+ self: this,
+ type: 'logout',
+ parserType: ParserType.LogoutResponse,
+ checkSignature: this.entitySettings.wantLogoutResponseSigned,
+ binding: protocol,
+ request: request,
+ });
+ }
}
diff --git a/src/error.ts b/src/error.ts
new file mode 100644
index 00000000..335510c0
--- /dev/null
+++ b/src/error.ts
@@ -0,0 +1,59 @@
+export enum SamlifyErrorCode {
+ /**
+ * catch-all for all unknown codes, including missing codes.
+ */
+ Unknown = 'Unknown',
+ CertificateNotFound = 'CertificateNotFound',
+ EmptyAssertion = 'EmptyAssertion',
+ ExceptionOfAssertionDecryption = 'ExceptionOfAssertionDecryption',
+ ExceptionOfAssertionEncryption = 'ExceptionOfAssertionEncryption',
+ ExpiredSession = 'ExpiredSession',
+ FailedMessageSignatureVerification = 'FailedMessageSignatureVerification',
+ FailedStatus = 'FailedStatus',
+ FailedToVerifySignature = 'FailedToVerifySignature',
+ FailedToVerifySignatureETS = 'FailedToVerifySignatureETS',
+ FailedToVerifySignatureSTE = 'FailedToVerifySignatureSTE',
+ InvalidXML = 'InvalidXML',
+ MetadataConflictRequestSignedFlag = 'MetadataConflictRequestSignedFlag',
+ MetadataIdpMissingSingleSignOnService = 'MetadataIdpMissingSingleSignOnService',
+ MismatchedCertificateDeclarationInMetadata = 'MismatchedCertificateDeclarationInMetadata',
+ MismatchedIssuer = 'MismatchedIssuer',
+ MissingDataEncryptionAlgorithm = 'MissingDataEncryptionAlgorithm',
+ MissingEncPrivateKey = 'MissingEncPrivateKey',
+ MissingKeyEncryptionAlgorithm = 'MissingKeyEncryptionAlgorithm',
+ MissingMetadata = 'MissingMetadata',
+ MissingOptionsForSignatureVerification = 'MissingOptionsForSignatureVerification',
+ MissingPrivateKey = 'MissingPrivateKey',
+ MissingQueryOctetString = 'MissingQueryOctetString',
+ MissingSigAlg = 'MissingSigAlg',
+ MissingStatus = 'MissingStatus',
+ MissingValidation = 'MissingValidation',
+ MultipleAssertion = 'MultipleAssertion',
+ MultipleMetadataEntityDescriptor = 'MultipleMetadataEntityDescriptor',
+ PotentialWrappingAttack = 'PotentialWrappingAttack',
+ RedirectFlowBadArgs = 'RedirectFlowBadArgs',
+ SingleLogoutLocationNotFound = 'SingleLogoutLocationNotFound',
+ SingleSignOnLocationNotFound = 'SingleSignOnLocationNotFound',
+ SubjectUnconfirmed = 'SubjectUnconfirmed',
+ TypeError = 'TypeError',
+ UndefinedAssertion = 'UndefinedAssertion',
+ UndefinedQueryParams = 'UndefinedQueryParams',
+ UnexpectedFlow = 'UnexpectedFlow',
+ UnsupportedBinding = 'UnsupportedBinding',
+ UnsupportedParserType = 'UnsupportedParserType',
+ ZeroSignature = 'ZeroSignature',
+}
+
+export class SamlifyError extends Error {
+ public code: SamlifyErrorCode;
+ constructor(code: SamlifyErrorCode = SamlifyErrorCode.Unknown, message?: string) {
+ super(message);
+ this.code = code;
+ this.name = `SamlifyError(${code})`;
+ }
+}
+
+// shorthand
+export function isSamlifyError(error: unknown): error is SamlifyError {
+ return error instanceof SamlifyError;
+}
diff --git a/src/extractor.ts b/src/extractor.ts
index 5d4449f3..51e8f241 100644
--- a/src/extractor.ts
+++ b/src/extractor.ts
@@ -1,223 +1,235 @@
+import camelCase from 'camelcase';
import { DOMParser } from 'xmldom';
import { select, SelectedValue } from 'xpath';
-import { uniq, last, zipObject, notEmpty } from './utility';
-import camelCase from 'camelcase';
+import { isArrayOfArrays, last, notEmpty, uniq, zipObject } from './utility';
const dom = DOMParser;
interface ExtractorField {
- key: string;
- localPath: string[] | string[][];
- attributes: string[];
- index?: string[];
- attributePath?: string[];
- context?: boolean;
+ key: string;
+ localPath: string[] | string[][];
+ attributes: string[];
+ index?: string[];
+ attributePath?: string[];
+ context?: boolean;
}
export type ExtractorFields = ExtractorField[];
-function buildAbsoluteXPath(paths) {
- return paths.reduce((currentPath, name) => {
- let appendedPath = currentPath;
- const isWildcard = name.startsWith('~');
- if (isWildcard) {
- const pathName = name.replace('~', '');
- appendedPath = currentPath + `/*[contains(local-name(), '${pathName}')]`;
- }
- if (!isWildcard) {
- appendedPath = currentPath + `/*[local-name(.)='${name}']`;
- }
- return appendedPath;
- }, '');
+export function isNode(n?: SelectedValue): n is Node {
+ return typeof n === 'object' && 'nodeType' in n;
+}
+
+export function isAttr(n?: SelectedValue): n is Attr {
+ return isNode(n) && 'value' in n;
+}
+
+export function isElement(n?: SelectedValue): n is Element {
+ return isNode(n) && 'attributes' in n;
+}
+
+function buildAbsoluteXPath(paths: string[]) {
+ return paths.reduce((currentPath, name) => {
+ let appendedPath = currentPath;
+ const isWildcard = name.startsWith('~');
+ if (isWildcard) {
+ const pathName = name.replace('~', '');
+ appendedPath = currentPath + `/*[contains(local-name(), '${pathName}')]`;
+ }
+ if (!isWildcard) {
+ appendedPath = currentPath + `/*[local-name(.)='${name}']`;
+ }
+ return appendedPath;
+ }, '');
}
-function buildAttributeXPath(attributes) {
- if (attributes.length === 0) {
- return '/text()';
- }
- if (attributes.length === 1) {
- return `/@${attributes[0]}`;
- }
- const filters = attributes.map(attribute => `name()='${attribute}'`).join(' or ');
- return `/@*[${filters}]`;
+function buildAttributeXPath(attributes: string[]) {
+ if (attributes.length === 0) {
+ return '/text()';
+ }
+ if (attributes.length === 1) {
+ return `/@${attributes[0]}`;
+ }
+ const filters = attributes.map((attribute) => `name()='${attribute}'`).join(' or ');
+ return `/@*[${filters}]`;
}
export const loginRequestFields: ExtractorFields = [
- {
- key: 'request',
- localPath: ['AuthnRequest'],
- attributes: ['ID', 'IssueInstant', 'Destination', 'AssertionConsumerServiceURL']
- },
- {
- key: 'issuer',
- localPath: ['AuthnRequest', 'Issuer'],
- attributes: []
- },
- {
- key: 'nameIDPolicy',
- localPath: ['AuthnRequest', 'NameIDPolicy'],
- attributes: ['Format', 'AllowCreate']
- },
- {
- key: 'authnContextClassRef',
- localPath: ['AuthnRequest', 'AuthnContextClassRef'],
- attributes: []
- },
- {
- key: 'signature',
- localPath: ['AuthnRequest', 'Signature'],
- attributes: [],
- context: true
- }
+ {
+ key: 'request',
+ localPath: ['AuthnRequest'],
+ attributes: ['ID', 'IssueInstant', 'Destination', 'AssertionConsumerServiceURL'],
+ },
+ {
+ key: 'issuer',
+ localPath: ['AuthnRequest', 'Issuer'],
+ attributes: [],
+ },
+ {
+ key: 'nameIDPolicy',
+ localPath: ['AuthnRequest', 'NameIDPolicy'],
+ attributes: ['Format', 'AllowCreate'],
+ },
+ {
+ key: 'authnContextClassRef',
+ localPath: ['AuthnRequest', 'AuthnContextClassRef'],
+ attributes: [],
+ },
+ {
+ key: 'signature',
+ localPath: ['AuthnRequest', 'Signature'],
+ attributes: [],
+ context: true,
+ },
];
// support two-tiers status code
export const loginResponseStatusFields = [
- {
- key: 'top',
- localPath: ['Response', 'Status', 'StatusCode'],
- attributes: ['Value'],
- },
- {
- key: 'second',
- localPath: ['Response', 'Status', 'StatusCode', 'StatusCode'],
- attributes: ['Value'],
- }
+ {
+ key: 'top',
+ localPath: ['Response', 'Status', 'StatusCode'],
+ attributes: ['Value'],
+ },
+ {
+ key: 'second',
+ localPath: ['Response', 'Status', 'StatusCode', 'StatusCode'],
+ attributes: ['Value'],
+ },
];
// support two-tiers status code
export const logoutResponseStatusFields = [
- {
- key: 'top',
- localPath: ['LogoutResponse', 'Status', 'StatusCode'],
- attributes: ['Value']
- },
- {
- key: 'second',
- localPath: ['LogoutResponse', 'Status', 'StatusCode', 'StatusCode'],
- attributes: ['Value'],
- }
+ {
+ key: 'top',
+ localPath: ['LogoutResponse', 'Status', 'StatusCode'],
+ attributes: ['Value'],
+ },
+ {
+ key: 'second',
+ localPath: ['LogoutResponse', 'Status', 'StatusCode', 'StatusCode'],
+ attributes: ['Value'],
+ },
];
-export const loginResponseFields: ((assertion: any) => ExtractorFields) = assertion => [
- {
- key: 'conditions',
- localPath: ['Assertion', 'Conditions'],
- attributes: ['NotBefore', 'NotOnOrAfter'],
- shortcut: assertion
- },
- {
- key: 'response',
- localPath: ['Response'],
- attributes: ['ID', 'IssueInstant', 'Destination', 'InResponseTo'],
- },
- {
- key: 'audience',
- localPath: ['Assertion', 'Conditions', 'AudienceRestriction', 'Audience'],
- attributes: [],
- shortcut: assertion
- },
- // {
- // key: 'issuer',
- // localPath: ['Response', 'Issuer'],
- // attributes: []
- // },
- {
- key: 'issuer',
- localPath: ['Assertion', 'Issuer'],
- attributes: [],
- shortcut: assertion
- },
- {
- key: 'nameID',
- localPath: ['Assertion', 'Subject', 'NameID'],
- attributes: [],
- shortcut: assertion
- },
- {
- key: 'sessionIndex',
- localPath: ['Assertion', 'AuthnStatement'],
- attributes: ['AuthnInstant', 'SessionNotOnOrAfter', 'SessionIndex'],
- shortcut: assertion
- },
- {
- key: 'attributes',
- localPath: ['Assertion', 'AttributeStatement', 'Attribute'],
- index: ['Name'],
- attributePath: ['AttributeValue'],
- attributes: [],
- shortcut: assertion
- }
+export const loginResponseFields: (assertion: any) => ExtractorFields = (assertion) => [
+ {
+ key: 'conditions',
+ localPath: ['Assertion', 'Conditions'],
+ attributes: ['NotBefore', 'NotOnOrAfter'],
+ shortcut: assertion,
+ },
+ {
+ key: 'response',
+ localPath: ['Response'],
+ attributes: ['ID', 'IssueInstant', 'Destination', 'InResponseTo'],
+ },
+ {
+ key: 'audience',
+ localPath: ['Assertion', 'Conditions', 'AudienceRestriction', 'Audience'],
+ attributes: [],
+ shortcut: assertion,
+ },
+ // {
+ // key: 'issuer',
+ // localPath: ['Response', 'Issuer'],
+ // attributes: []
+ // },
+ {
+ key: 'issuer',
+ localPath: ['Assertion', 'Issuer'],
+ attributes: [],
+ shortcut: assertion,
+ },
+ {
+ key: 'nameID',
+ localPath: ['Assertion', 'Subject', 'NameID'],
+ attributes: [],
+ shortcut: assertion,
+ },
+ {
+ key: 'sessionIndex',
+ localPath: ['Assertion', 'AuthnStatement'],
+ attributes: ['AuthnInstant', 'SessionNotOnOrAfter', 'SessionIndex'],
+ shortcut: assertion,
+ },
+ {
+ key: 'attributes',
+ localPath: ['Assertion', 'AttributeStatement', 'Attribute'],
+ index: ['Name'],
+ attributePath: ['AttributeValue'],
+ attributes: [],
+ shortcut: assertion,
+ },
];
export const logoutRequestFields: ExtractorFields = [
- {
- key: 'request',
- localPath: ['LogoutRequest'],
- attributes: ['ID', 'IssueInstant', 'Destination']
- },
- {
- key: 'issuer',
- localPath: ['LogoutRequest', 'Issuer'],
- attributes: []
- },
- {
- key: 'nameID',
- localPath: ['LogoutRequest', 'NameID'],
- attributes: []
- },
- {
- key: 'signature',
- localPath: ['LogoutRequest', 'Signature'],
- attributes: [],
- context: true
- }
+ {
+ key: 'request',
+ localPath: ['LogoutRequest'],
+ attributes: ['ID', 'IssueInstant', 'Destination'],
+ },
+ {
+ key: 'issuer',
+ localPath: ['LogoutRequest', 'Issuer'],
+ attributes: [],
+ },
+ {
+ key: 'nameID',
+ localPath: ['LogoutRequest', 'NameID'],
+ attributes: [],
+ },
+ {
+ key: 'signature',
+ localPath: ['LogoutRequest', 'Signature'],
+ attributes: [],
+ context: true,
+ },
];
export const logoutResponseFields: ExtractorFields = [
- {
- key: 'response',
- localPath: ['LogoutResponse'],
- attributes: ['ID', 'Destination', 'InResponseTo']
- },
- {
- key: 'issuer',
- localPath: ['LogoutResponse', 'Issuer'],
- attributes: []
- },
- {
- key: 'signature',
- localPath: ['LogoutResponse', 'Signature'],
- attributes: [],
- context: true
- }
+ {
+ key: 'response',
+ localPath: ['LogoutResponse'],
+ attributes: ['ID', 'Destination', 'InResponseTo'],
+ },
+ {
+ key: 'issuer',
+ localPath: ['LogoutResponse', 'Issuer'],
+ attributes: [],
+ },
+ {
+ key: 'signature',
+ localPath: ['LogoutResponse', 'Signature'],
+ attributes: [],
+ context: true,
+ },
];
-export function extract(context: string, fields) {
+export function extract(context: string, fields: ExtractorField[]) {
+ const rootDoc = new dom().parseFromString(context);
- const rootDoc = new dom().parseFromString(context);
+ return fields.reduce((result, field) => {
+ // get essential fields
+ const key = field.key;
+ const localPath = field.localPath;
+ const attributes = field.attributes;
+ const isEntire = field.context;
+ // @ts-expect-error todo
+ const shortcut = field.shortcut;
+ // get optional fields
+ const index = field.index;
+ const attributePath = field.attributePath;
- return fields.reduce((result: any, field) => {
- // get essential fields
- const key = field.key;
- const localPath = field.localPath;
- const attributes = field.attributes;
- const isEntire = field.context;
- const shortcut = field.shortcut;
- // get optional fields
- const index = field.index;
- const attributePath = field.attributePath;
+ // set allowing overriding if there is a shortcut injected
+ let targetDoc = rootDoc;
- // set allowing overriding if there is a shortcut injected
- let targetDoc = rootDoc;
+ // if shortcut is used, then replace the doc
+ // it's a design for overriding the doc used during runtime
+ if (shortcut) {
+ targetDoc = new dom().parseFromString(shortcut);
+ }
- // if shortcut is used, then replace the doc
- // it's a design for overriding the doc used during runtime
- if (shortcut) {
- targetDoc = new dom().parseFromString(shortcut);
- }
-
- // special case: multiple path
- /*
+ // special case: multiple path
+ /*
{
key: 'issuer',
localPath: [
@@ -227,26 +239,31 @@ export function extract(context: string, fields) {
attributes: []
}
*/
- if (localPath.every(path => Array.isArray(path))) {
- const multiXPaths = localPath
- .map(path => {
- // not support attribute yet, so ignore it
- return `${buildAbsoluteXPath(path)}/text()`;
- })
- .join(' | ');
+ if (isArrayOfArrays(localPath)) {
+ const multiXPaths = localPath
+ .map((path) => {
+ // not support attribute yet, so ignore it
+ return `${buildAbsoluteXPath(path)}/text()`;
+ })
+ .join(' | ');
- return {
- ...result,
- [key]: uniq(select(multiXPaths, targetDoc).map((n: Node) => n.nodeValue).filter(notEmpty))
- };
- }
- // eo special case: multiple path
+ return {
+ ...result,
+ [key]: uniq(
+ select(multiXPaths, targetDoc)
+ .filter(isNode)
+ .map((n: Node) => n.nodeValue)
+ .filter(notEmpty)
+ ),
+ };
+ }
+ // eo special case: multiple path
- const baseXPath = buildAbsoluteXPath(localPath);
- const attributeXPath = buildAttributeXPath(attributes);
+ const baseXPath = buildAbsoluteXPath(localPath);
+ const attributeXPath = buildAttributeXPath(attributes);
- // special case: get attributes where some are in child, some are in parent
- /*
+ // special case: get attributes where some are in child, some are in parent
+ /*
{
key: 'attributes',
localPath: ['Response', 'Assertion', 'AttributeStatement', 'Attribute'],
@@ -255,46 +272,51 @@ export function extract(context: string, fields) {
attributes: []
}
*/
- if (index && attributePath) {
- // find the index in localpath
- const indexPath = buildAttributeXPath(index);
- const fullLocalXPath = `${baseXPath}${indexPath}`;
- const parentNodes = select(baseXPath, targetDoc);
- // [uid, mail, edupersonaffiliation], ready for aggregate
- const parentAttributes = select(fullLocalXPath, targetDoc).map((n: Attr) => n.value);
- // [attribute, attributevalue]
- const childXPath = buildAbsoluteXPath([last(localPath)].concat(attributePath));
- const childAttributeXPath = buildAttributeXPath(attributes);
- const fullChildXPath = `${childXPath}${childAttributeXPath}`;
- // [ 'test', 'test@example.com', [ 'users', 'examplerole1' ] ]
- const childAttributes = parentNodes.map(node => {
- const nodeDoc = new dom().parseFromString(node.toString());
- if (attributes.length === 0) {
- const childValues = select(fullChildXPath, nodeDoc).map((n: Node) => n.nodeValue);
- if (childValues.length === 1) {
- return childValues[0];
- }
- return childValues;
- }
- if (attributes.length > 0) {
- const childValues = select(fullChildXPath, nodeDoc).map((n: Attr) => n.value);
- if (childValues.length === 1) {
- return childValues[0];
- }
- return childValues;
- }
- return null;
- });
- // aggregation
- const obj = zipObject(parentAttributes, childAttributes, false);
- return {
- ...result,
- [key]: obj
- };
-
- }
- // case: fetch entire content, only allow one existence
- /*
+ if (index && attributePath) {
+ // find the index in localpath
+ const indexPath = buildAttributeXPath(index);
+ const fullLocalXPath = `${baseXPath}${indexPath}`;
+ const parentNodes = select(baseXPath, targetDoc);
+ // [uid, mail, edupersonaffiliation], ready for aggregate
+ const parentAttributes = select(fullLocalXPath, targetDoc)
+ .filter(isAttr)
+ .map((n: Attr) => n.value);
+ // [attribute, attributevalue]
+ const childXPath = buildAbsoluteXPath([last(localPath)].concat(attributePath).filter(notEmpty));
+ const childAttributeXPath = buildAttributeXPath(attributes);
+ const fullChildXPath = `${childXPath}${childAttributeXPath}`;
+ // [ 'test', 'test@example.com', [ 'users', 'examplerole1' ] ]
+ const childAttributes = parentNodes.map((node) => {
+ const nodeDoc = new dom().parseFromString(node.toString());
+ if (attributes.length === 0) {
+ const childValues = select(fullChildXPath, nodeDoc)
+ .filter(isNode)
+ .map((n: Node) => n.nodeValue);
+ if (childValues.length === 1) {
+ return childValues[0];
+ }
+ return childValues;
+ }
+ if (attributes.length > 0) {
+ const childValues = select(fullChildXPath, nodeDoc)
+ .filter(isAttr)
+ .map((n: Attr) => n.value);
+ if (childValues.length === 1) {
+ return childValues[0];
+ }
+ return childValues;
+ }
+ return null;
+ });
+ // aggregation
+ const obj = zipObject(parentAttributes, childAttributes, false);
+ return {
+ ...result,
+ [key]: obj,
+ };
+ }
+ // case: fetch entire content, only allow one existence
+ /*
{
key: 'signature',
localPath: ['AuthnRequest', 'Signature'],
@@ -302,87 +324,92 @@ export function extract(context: string, fields) {
context: true
}
*/
- if (isEntire) {
- const node = select(baseXPath, targetDoc);
- let value: string | string[] | null = null;
- if (node.length === 1) {
- value = node[0].toString();
- }
- if (node.length > 1) {
- value = node.map(n => n.toString());
- }
- return {
- ...result,
- [key]: value
- };
- }
+ if (isEntire) {
+ const node = select(baseXPath, targetDoc);
+ let value: string | string[] | null = null;
+ if (node.length === 1 && node[0] != null) {
+ value = node[0].toString();
+ }
+ if (node.length > 1) {
+ value = node.map((n) => n.toString());
+ }
+ return {
+ ...result,
+ [key]: value,
+ };
+ }
- // case: multiple attribute
- /*
+ // case: multiple attribute
+ /*
{
key: 'nameIDPolicy',
localPath: ['AuthnRequest', 'NameIDPolicy'],
attributes: ['Format', 'AllowCreate']
}
*/
- if (attributes.length > 1) {
- const baseNode = select(baseXPath, targetDoc).map(n => n.toString());
- const childXPath = `${buildAbsoluteXPath([last(localPath)])}${attributeXPath}`;
- const attributeValues = baseNode.map((node: string) => {
- const nodeDoc = new dom().parseFromString(node);
- const values = select(childXPath, nodeDoc).reduce((r: any, n: Attr) => {
- r[camelCase(n.name)] = n.value;
- return r;
- }, {});
- return values;
- });
- return {
- ...result,
- [key]: attributeValues.length === 1 ? attributeValues[0] : attributeValues
- };
- }
- // case: single attribute
- /*
+ if (attributes.length > 1) {
+ const baseNode = select(baseXPath, targetDoc).map((n) => n.toString());
+ const childXPath = `${buildAbsoluteXPath([last(localPath)].filter(notEmpty))}${attributeXPath}`;
+ const attributeValues = baseNode.map((node: string) => {
+ const nodeDoc = new dom().parseFromString(node);
+ const values = select(childXPath, nodeDoc)
+ .filter(isAttr)
+ .reduce((r, n) => {
+ r[camelCase(n.name)] = n.value;
+ return r;
+ }, {} as Record);
+ return values;
+ });
+ return {
+ ...result,
+ [key]: attributeValues.length === 1 ? attributeValues[0] : attributeValues,
+ };
+ }
+ // case: single attribute
+ /*
{
key: 'statusCode',
localPath: ['Response', 'Status', 'StatusCode'],
attributes: ['Value'],
}
*/
- if (attributes.length === 1) {
- const fullPath = `${baseXPath}${attributeXPath}`;
- const attributeValues = select(fullPath, targetDoc).map((n: Attr) => n.value);
- return {
- ...result,
- [key]: attributeValues[0]
- };
- }
- // case: zero attribute
- /*
+ if (attributes.length === 1) {
+ const fullPath = `${baseXPath}${attributeXPath}`;
+ const attributeValues = select(fullPath, targetDoc)
+ .filter(isAttr)
+ .map((n: Attr) => n.value);
+ return {
+ ...result,
+ [key]: attributeValues[0],
+ };
+ }
+ // case: zero attribute
+ /*
{
key: 'issuer',
localPath: ['AuthnRequest', 'Issuer'],
attributes: []
}
*/
- if (attributes.length === 0) {
- let attributeValue: SelectedValue[] | (string | null)[] | null = null;
- const node = select(baseXPath, targetDoc);
- if (node.length === 1) {
- const fullPath = `string(${baseXPath}${attributeXPath})`;
- attributeValue = select(fullPath, targetDoc);
- }
- if (node.length > 1) {
- attributeValue = node.filter((n: Node) => n.firstChild)
- .map((n: Node) => n.firstChild!.nodeValue);
- }
- return {
- ...result,
- [key]: attributeValue
- };
- }
-
- return result;
- }, {});
+ if (attributes.length === 0) {
+ let attributeValue: SelectedValue[] | (string | null)[] | null = null;
+ const node = select(baseXPath, targetDoc);
+ if (node.length === 1) {
+ const fullPath = `string(${baseXPath}${attributeXPath})`;
+ attributeValue = select(fullPath, targetDoc);
+ }
+ if (node.length > 1) {
+ attributeValue = node
+ .filter(isNode)
+ .filter((n: Node) => n.firstChild)
+ .map((n: Node) => n.firstChild?.nodeValue ?? null);
+ }
+ return {
+ ...result,
+ [key]: attributeValue,
+ };
+ }
+ return result;
+ }, {} as Record);
}
diff --git a/src/flow.ts b/src/flow.ts
index 7750563b..749a9ebb 100644
--- a/src/flow.ts
+++ b/src/flow.ts
@@ -1,288 +1,275 @@
-import { inflateString, base64Decode } from './utility';
-import { verifyTime } from './validator';
-import libsaml from './libsaml';
+import type { Entity, ESamlHttpRequest } from './entity';
+import { SamlifyError, SamlifyErrorCode } from './error';
import {
- extract,
- loginRequestFields,
- loginResponseFields,
- logoutRequestFields,
- logoutResponseFields,
- ExtractorFields,
- logoutResponseStatusFields,
- loginResponseStatusFields
+ extract,
+ ExtractorFields,
+ loginRequestFields,
+ loginResponseFields,
+ loginResponseStatusFields,
+ logoutRequestFields,
+ logoutResponseFields,
+ logoutResponseStatusFields,
} from './extractor';
+import libsaml from './libsaml';
+import { BindingNamespace, MessageSignatureOrder, ParserType, StatusCode, wording } from './urn';
+import { base64Decode, inflateString } from './utility';
+import { verifyTime } from './validator';
-import {
- BindingNamespace,
- ParserType,
- wording,
- MessageSignatureOrder,
- StatusCode
-} from './urn';
-
-const bindDict = wording.binding;
const urlParams = wording.urlParams;
+export interface FlowOptions {
+ from: From;
+ self: Self;
+ checkSignature?: boolean;
+ parserType: ParserType;
+ type: 'login' | 'logout';
+ binding: BindingNamespace;
+ request: ESamlHttpRequest;
+ supportBindings?: BindingNamespace[];
+}
+
export interface FlowResult {
- samlContent: string;
- extract: any;
+ samlContent: string;
+ extract: any;
}
// get the default extractor fields based on the parserType
function getDefaultExtractorFields(parserType: ParserType, assertion?: any): ExtractorFields {
- switch (parserType) {
- case ParserType.SAMLRequest:
- return loginRequestFields;
- case ParserType.SAMLResponse:
- if (!assertion) {
- // unexpected hit
- throw new Error('ERR_EMPTY_ASSERTION');
- }
- return loginResponseFields(assertion);
- case ParserType.LogoutRequest:
- return logoutRequestFields;
- case ParserType.LogoutResponse:
- return logoutResponseFields;
- default:
- throw new Error('ERR_UNDEFINED_PARSERTYPE');
- }
+ switch (parserType) {
+ case ParserType.SAMLRequest:
+ return loginRequestFields;
+ case ParserType.SAMLResponse:
+ if (!assertion) {
+ // unexpected hit
+ throw new SamlifyError(SamlifyErrorCode.EmptyAssertion);
+ }
+ return loginResponseFields(assertion);
+ case ParserType.LogoutRequest:
+ return logoutRequestFields;
+ case ParserType.LogoutResponse:
+ return logoutResponseFields;
+ default:
+ throw new SamlifyError(SamlifyErrorCode.UnsupportedParserType);
+ }
}
// proceed the redirect binding flow
-async function redirectFlow(options) {
-
- const { request, parserType, checkSignature = true, from } = options;
- const { query, octetString } = request;
- const { SigAlg: sigAlg, Signature: signature } = query;
-
- const targetEntityMetadata = from.entityMeta;
-
- // ?SAMLRequest= or ?SAMLResponse=
- const direction = libsaml.getQueryParamByType(parserType);
- const content = query[direction];
-
- // query must contain the saml content
- if (content === undefined) {
- return Promise.reject('ERR_REDIRECT_FLOW_BAD_ARGS');
- }
-
- const xmlString = inflateString(decodeURIComponent(content));
-
- // validate the xml (remarks: login response must be gone through post flow)
- if (
- parserType === urlParams.samlRequest ||
- parserType === urlParams.logoutRequest ||
- parserType === urlParams.logoutResponse
- ) {
- try {
- await libsaml.isValidXml(xmlString);
- } catch (e) {
- return Promise.reject('ERR_INVALID_XML');
- }
- }
-
- const extractorFields = getDefaultExtractorFields(parserType);
-
- const parseResult: { samlContent: string, extract: any, sigAlg: (string | null) } = {
- samlContent: xmlString,
- sigAlg: null,
- extract: extract(xmlString, extractorFields),
- };
-
- // check status based on different scenarios
- await checkStatus(xmlString, parserType);
-
- // see if signature check is required
- // only verify message signature is enough
- if (checkSignature) {
- if (!signature || !sigAlg) {
- return Promise.reject('ERR_MISSING_SIG_ALG');
- }
-
- // put the below two assignemnts into verifyMessageSignature function
- const base64Signature = Buffer.from(decodeURIComponent(signature), 'base64');
- const decodeSigAlg = decodeURIComponent(sigAlg);
-
- const verified = libsaml.verifyMessageSignature(targetEntityMetadata, octetString, base64Signature, sigAlg);
-
- if (!verified) {
- // Fail to verify message signature
- return Promise.reject('ERR_FAILED_MESSAGE_SIGNATURE_VERIFICATION');
- }
-
- parseResult.sigAlg = decodeSigAlg;
- }
-
- return Promise.resolve(parseResult);
+async function redirectFlow(options: FlowOptions): Promise {
+ const { request, parserType, checkSignature = true, from } = options;
+ const { query, octetString } = request;
+ const { SigAlg: sigAlg, Signature: signature } = query;
+
+ const targetEntityMetadata = from.getEntityMeta();
+
+ // ?SAMLRequest= or ?SAMLResponse=
+ const direction = libsaml.getQueryParamByType(parserType);
+ const content = query[direction];
+
+ // query must contain the saml content
+ if (content === undefined) {
+ throw new SamlifyError(SamlifyErrorCode.RedirectFlowBadArgs);
+ }
+
+ const xmlString = inflateString(decodeURIComponent(content));
+
+ // validate the xml (remarks: login response must be gone through post flow)
+ if (
+ parserType === urlParams.samlRequest ||
+ parserType === urlParams.logoutRequest ||
+ parserType === urlParams.logoutResponse
+ ) {
+ try {
+ await libsaml.isValidXml(xmlString);
+ } catch (e) {
+ throw new SamlifyError(SamlifyErrorCode.InvalidXML);
+ }
+ }
+
+ const extractorFields = getDefaultExtractorFields(parserType);
+
+ const parseResult: { samlContent: string; extract: any; sigAlg: string | null } = {
+ samlContent: xmlString,
+ sigAlg: null,
+ extract: extract(xmlString, extractorFields),
+ };
+
+ // check status based on different scenarios
+ await checkStatus(xmlString, parserType);
+
+ // see if signature check is required
+ // only verify message signature is enough
+ if (checkSignature) {
+ if (!octetString) {
+ throw new SamlifyError(SamlifyErrorCode.MissingQueryOctetString);
+ }
+ if (!signature || !sigAlg) {
+ throw new SamlifyError(SamlifyErrorCode.MissingSigAlg);
+ }
+
+ // put the below two assignemnts into verifyMessageSignature function
+ const base64Signature = Buffer.from(decodeURIComponent(signature), 'base64');
+ const decodeSigAlg = decodeURIComponent(sigAlg);
+
+ const verified = libsaml.verifyMessageSignature(targetEntityMetadata, octetString, base64Signature, sigAlg);
+
+ if (!verified) {
+ // Fail to verify message signature
+ throw new SamlifyError(SamlifyErrorCode.FailedMessageSignatureVerification);
+ }
+
+ parseResult.sigAlg = decodeSigAlg;
+ }
+
+ return parseResult;
}
// proceed the post flow
-async function postFlow(options): Promise {
-
- const {
- request,
- from,
- self,
- parserType,
- checkSignature = true
- } = options;
-
- const { body } = request;
-
- const direction = libsaml.getQueryParamByType(parserType);
- const encodedRequest = body[direction];
-
- let samlContent = String(base64Decode(encodedRequest));
-
- const verificationOptions = {
- metadata: from.entityMeta,
- signatureAlgorithm: from.entitySetting.requestSignatureAlgorithm,
- };
-
- const decryptRequired = from.entitySetting.isAssertionEncrypted;
-
- let extractorFields: ExtractorFields = [];
-
- // validate the xml first
- await libsaml.isValidXml(samlContent);
-
- if (parserType !== urlParams.samlResponse) {
- extractorFields = getDefaultExtractorFields(parserType, null);
- }
-
- // check status based on different scenarios
- await checkStatus(samlContent, parserType);
-
- // verify the signatures (the repsonse is encrypted then signed, then verify first then decrypt)
- if (
- checkSignature &&
- from.entitySetting.messageSigningOrder === MessageSignatureOrder.ETS
- ) {
- const [verified, verifiedAssertionNode] = libsaml.verifySignature(samlContent, verificationOptions);
- if (!verified) {
- return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
- }
- if (!decryptRequired) {
- extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
- }
- }
-
- if (parserType === 'SAMLResponse' && decryptRequired) {
- const result = await libsaml.decryptAssertion(self, samlContent);
- samlContent = result[0];
- extractorFields = getDefaultExtractorFields(parserType, result[1]);
- }
-
- // verify the signatures (the repsonse is signed then encrypted, then decrypt first then verify)
- if (
- checkSignature &&
- from.entitySetting.messageSigningOrder === MessageSignatureOrder.STE
- ) {
- const [verified, verifiedAssertionNode] = libsaml.verifySignature(samlContent, verificationOptions);
- if (verified) {
- extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
- } else {
- return Promise.reject('ERR_FAIL_TO_VERIFY_STE_SIGNATURE');
- }
- }
-
- const parseResult = {
- samlContent: samlContent,
- extract: extract(samlContent, extractorFields),
- };
-
- /**
- * Validation part: validate the context of response after signature is verified and decrpyted (optional)
- */
- const targetEntityMetadata = from.entityMeta;
- const issuer = targetEntityMetadata.getEntityID();
- const extractedProperties = parseResult.extract;
-
- // unmatched issuer
- if (
- (parserType === 'LogoutResponse' || parserType === 'SAMLResponse')
- && extractedProperties
- && extractedProperties.issuer !== issuer
- ) {
- return Promise.reject('ERR_UNMATCH_ISSUER');
- }
-
- // invalid session time
- // only run the verifyTime when `SessionNotOnOrAfter` exists
- if (
- parserType === 'SAMLResponse'
- && extractedProperties.sessionIndex.sessionNotOnOrAfter
- && !verifyTime(
- undefined,
- extractedProperties.sessionIndex.sessionNotOnOrAfter,
- self.entitySetting.clockDrifts
- )
- ) {
- return Promise.reject('ERR_EXPIRED_SESSION');
- }
-
- // invalid time
- // 2.4.1.2 https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
- if (
- parserType === 'SAMLResponse'
- && extractedProperties.conditions
- && !verifyTime(
- extractedProperties.conditions.notBefore,
- extractedProperties.conditions.notOnOrAfter,
- self.entitySetting.clockDrifts
- )
- ) {
- return Promise.reject('ERR_SUBJECT_UNCONFIRMED');
- }
-
- return Promise.resolve(parseResult);
+async function postFlow(options: FlowOptions): Promise {
+ const { request, from, self, parserType, checkSignature = true } = options;
+
+ const { body } = request;
+
+ const fromEntitySetting = from.getEntitySettings();
+ const direction = libsaml.getQueryParamByType(parserType);
+ const encodedRequest = body[direction];
+
+ let samlContent = String(base64Decode(encodedRequest));
+
+ const verificationOptions = {
+ metadata: from.getEntityMeta(),
+ signatureAlgorithm: fromEntitySetting.requestSignatureAlgorithm,
+ };
+
+ const decryptRequired = fromEntitySetting.isAssertionEncrypted;
+
+ let extractorFields: ExtractorFields = [];
+
+ // validate the xml first
+ await libsaml.isValidXml(samlContent);
+
+ if (parserType !== urlParams.samlResponse) {
+ extractorFields = getDefaultExtractorFields(parserType, null);
+ }
+
+ // check status based on different scenarios
+ await checkStatus(samlContent, parserType);
+
+ // verify the signatures (the repsonse is encrypted then signed, then verify first then decrypt)
+ if (checkSignature && fromEntitySetting.messageSigningOrder === MessageSignatureOrder.ETS) {
+ const [verified, verifiedAssertionNode] = libsaml.verifySignature(samlContent, verificationOptions);
+ if (!verified) {
+ throw new SamlifyError(SamlifyErrorCode.FailedToVerifySignatureETS);
+ }
+ if (!decryptRequired) {
+ extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
+ }
+ }
+
+ if (parserType === ParserType.SAMLResponse && decryptRequired) {
+ const result = await libsaml.decryptAssertion(self, samlContent);
+ samlContent = result[0];
+ extractorFields = getDefaultExtractorFields(parserType, result[1]);
+ }
+
+ // verify the signatures (the repsonse is signed then encrypted, then decrypt first then verify)
+ if (checkSignature && fromEntitySetting.messageSigningOrder === MessageSignatureOrder.STE) {
+ const [verified, verifiedAssertionNode] = libsaml.verifySignature(samlContent, verificationOptions);
+ if (verified) {
+ extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
+ } else {
+ throw new SamlifyError(SamlifyErrorCode.FailedToVerifySignatureSTE);
+ }
+ }
+
+ const parseResult = {
+ samlContent: samlContent,
+ extract: extract(samlContent, extractorFields),
+ };
+
+ /**
+ * Validation part: validate the context of response after signature is verified and decrpyted (optional)
+ */
+ const initEntitySetting = self.getEntitySettings();
+ const targetEntityMetadata = from.getEntityMeta();
+ const issuer = targetEntityMetadata.getEntityID();
+ const extractedProperties = parseResult.extract;
+
+ // unmatched issuer
+ if (
+ (parserType === ParserType.LogoutResponse || parserType === ParserType.SAMLResponse) &&
+ extractedProperties &&
+ extractedProperties.issuer !== issuer
+ ) {
+ throw new SamlifyError(SamlifyErrorCode.MismatchedIssuer);
+ }
+
+ // invalid session time
+ // only run the verifyTime when `SessionNotOnOrAfter` exists
+ if (
+ parserType === ParserType.SAMLResponse &&
+ extractedProperties.sessionIndex.sessionNotOnOrAfter &&
+ !verifyTime(undefined, extractedProperties.sessionIndex.sessionNotOnOrAfter, initEntitySetting.clockDrifts)
+ ) {
+ throw new SamlifyError(SamlifyErrorCode.ExpiredSession);
+ }
+
+ // invalid time
+ // 2.4.1.2 https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
+ if (
+ parserType === ParserType.SAMLResponse &&
+ extractedProperties.conditions &&
+ !verifyTime(
+ extractedProperties.conditions.notBefore,
+ extractedProperties.conditions.notOnOrAfter,
+ initEntitySetting.clockDrifts
+ )
+ ) {
+ throw new SamlifyError(SamlifyErrorCode.SubjectUnconfirmed);
+ }
+
+ return parseResult;
}
-function checkStatus(content: string, parserType: string): Promise {
-
- // only check response parser
- if (parserType !== urlParams.samlResponse && parserType !== urlParams.logoutResponse) {
- return Promise.resolve('SKIPPED');
- }
+async function checkStatus(content: string, parserType: string): Promise {
+ // only check response parser
+ if (parserType !== urlParams.samlResponse && parserType !== urlParams.logoutResponse) {
+ return 'SKIPPED';
+ }
- const fields = parserType === urlParams.samlResponse
- ? loginResponseStatusFields
- : logoutResponseStatusFields;
+ const fields = parserType === urlParams.samlResponse ? loginResponseStatusFields : logoutResponseStatusFields;
- const {top, second} = extract(content, fields);
+ const { top, second } = extract(content, fields);
- // only resolve when top-tier status code is success
- if (top === StatusCode.Success) {
- return Promise.resolve('OK');
- }
+ // only resolve when top-tier status code is success
+ if (top === StatusCode.Success) {
+ return 'OK';
+ }
- if (!top) {
- throw new Error('ERR_UNDEFINED_STATUS');
- }
+ if (!top) {
+ throw new SamlifyError(SamlifyErrorCode.MissingStatus);
+ }
- // returns a detailed error for two-tier error code
- throw new Error(`ERR_FAILED_STATUS with top tier code: ${top}, second tier code: ${second}`);
+ // returns a detailed error for two-tier error code
+ throw new SamlifyError(SamlifyErrorCode.FailedStatus, `with top tier code: ${top}, second tier code: ${second}`);
}
-export function flow(options): Promise {
-
- const binding = options.binding;
- const parserType = options.parserType;
-
- options.supportBindings = [BindingNamespace.Redirect, BindingNamespace.Post];
- // saml response only allows POST
- if (parserType === ParserType.SAMLResponse) {
- options.supportBindings = [BindingNamespace.Post];
- }
+export function flow(options: FlowOptions): Promise {
+ const binding = options.binding;
+ const parserType = options.parserType;
- if (binding === bindDict.post) {
- return postFlow(options);
- }
+ options.supportBindings = [BindingNamespace.Redirect, BindingNamespace.Post];
+ // saml response only allows POST
+ if (parserType === ParserType.SAMLResponse) {
+ options.supportBindings = [BindingNamespace.Post];
+ }
- if (binding === bindDict.redirect) {
- return redirectFlow(options);
- }
+ if (binding === BindingNamespace.Post) {
+ return postFlow(options);
+ }
- return Promise.reject('ERR_UNEXPECTED_FLOW');
+ if (binding === BindingNamespace.Redirect) {
+ return redirectFlow(options);
+ }
+ throw new SamlifyError(SamlifyErrorCode.UnexpectedFlow);
}
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 00000000..b8a79472
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,9 @@
+export { setSchemaValidator } from './api';
+export { default as identityProvider } from './entity-idp';
+export { default as serviceProvider } from './entity-sp';
+export * as extractor from './extractor';
+export { default as libsaml } from './libsaml';
+export { default as metadataIdp } from './metadata-idp';
+export { default as metadataSp } from './metadata-sp';
+export * as constants from './urn';
+export * as utility from './utility';
diff --git a/src/libsaml.ts b/src/libsaml.ts
index 4159cbd2..104b7eaf 100644
--- a/src/libsaml.ts
+++ b/src/libsaml.ts
@@ -1,20 +1,32 @@
/**
-* @file SamlLib.js
-* @author tngan
-* @desc A simple library including some common functions
-*/
+ * @file SamlLib.js
+ * @author tngan
+ * @desc A simple library including some common functions
+ */
-import { DOMParser } from 'xmldom';
-import utility, { flattenDeep, isString } from './utility';
-import { algorithms, wording, namespace } from './urn';
-import { select, SelectedValue } from 'xpath';
-import { MetadataInterface } from './metadata';
-import * as nrsa from 'node-rsa';
-import { SignedXml, FileKeyInfo } from 'xml-crypto';
-import * as xmlenc from '@authenio/xml-encryption';
-import { extract } from './extractor';
import camelCase from 'camelcase';
+import type NodeRSA from 'node-rsa';
+import nrsa from 'node-rsa';
+import { FileKeyInfo, SignedXml } from 'xml-crypto';
+import xmlenc from 'xml-encryption';
+import { DOMParser } from 'xmldom';
+import { select } from 'xpath';
import { getContext } from './api';
+import type { Entity } from './entity';
+import { SamlifyError, SamlifyErrorCode } from './error';
+import { extract, isNode } from './extractor';
+import type { Metadata } from './metadata';
+import type { RequestSignatureAlgorithm, SAMLDocumentTemplate } from './types';
+import { algorithms, names, wording } from './urn';
+import {
+ base64Encode,
+ flattenDeep,
+ getPublicKeyPemFromCertificate,
+ isNonEmptyArray,
+ isString,
+ normalizeCerString,
+ readPrivateKey,
+} from './utility';
const signatureAlgorithms = algorithms.signature;
const digestAlgorithms = algorithms.digest;
@@ -22,624 +34,677 @@ const certUse = wording.certUse;
const urlParams = wording.urlParams;
const dom = DOMParser;
+// class MyFileKeyInfo extends FileKeyInfo {
+// constructor(private _getKey: () => Buffer) {
+// super();
+// }
+// getKey(): Buffer {
+// return this._getKey();
+// }
+// getKeyInfo(key = '', prefix = '') {
+// return `<${prefix}X509Data><${prefix}X509Certificate>${key}${prefix}X509Certificate>${prefix}X509Data>`;
+// }
+// }
+
export interface SignatureConstructor {
- rawSamlMessage: string;
- referenceTagXPath?: string;
- privateKey: string;
- privateKeyPass?: string;
- signatureAlgorithm: string;
- signingCert: string | Buffer;
- isBase64Output?: boolean;
- signatureConfig?: any;
- isMessageSigned?: boolean;
- transformationAlgorithms?: string[];
+ rawSamlMessage: string;
+ referenceTagXPath?: string;
+ privateKey: string;
+ privateKeyPass?: string;
+ signatureAlgorithm?: RequestSignatureAlgorithm;
+ signingCert: string | Buffer;
+ isBase64Output?: boolean;
+ signatureConfig?: any;
+ isMessageSigned?: boolean;
+ transformationAlgorithms?: string[];
}
export interface SignatureVerifierOptions {
- metadata?: MetadataInterface;
- keyFile?: string;
- signatureAlgorithm?: string;
+ metadata?: Metadata;
+ keyFile?: string;
+ signatureAlgorithm?: RequestSignatureAlgorithm;
}
export interface ExtractorResult {
- [key: string]: any;
- signature?: string | string[];
- issuer?: string | string[];
- nameid?: string;
- notexist?: boolean;
+ [key: string]: any;
+ signature?: string | string[];
+ issuer?: string | string[];
+ nameid?: string;
+ notexist?: boolean;
}
export interface LoginResponseAttribute {
- name: string;
- nameFormat: string; //
- valueXsiType: string; //
- valueTag: string;
- valueXmlnsXs?: string;
- valueXmlnsXsi?: string;
+ name: string;
+ nameFormat: string; //
+ valueXsiType: string; //
+ valueTag: string;
+ valueXmlnsXs?: string;
+ valueXmlnsXsi?: string;
}
-
-export interface BaseSamlTemplate {
- context: string;
+export interface LoginResponseTemplate extends Required {
+ attributes?: LoginResponseAttribute[];
}
+export type LoginRequestTemplate = Required;
-export interface LoginResponseTemplate extends BaseSamlTemplate {
- attributes?: LoginResponseAttribute[];
-}
-export interface LoginRequestTemplate extends BaseSamlTemplate { }
-
-export interface LogoutRequestTemplate extends BaseSamlTemplate { }
+export type LogoutRequestTemplate = Required;
-export interface LogoutResponseTemplate extends BaseSamlTemplate { }
+export type LogoutResponseTemplate = Required;
export type KeyUse = 'signing' | 'encryption';
export interface KeyComponent {
- [key: string]: any;
+ [key: string]: any;
}
-export interface LibSamlInterface {
- getQueryParamByType: (type: string) => string;
- createXPath: (local, isExtractAll?: boolean) => string;
- replaceTagsByValue: (rawXML: string, tagValues: any) => string;
- attributeStatementBuilder: (attributes: LoginResponseAttribute[]) => string;
- constructSAMLSignature: (opts: SignatureConstructor) => string;
- verifySignature: (xml: string, opts) => [boolean, any];
- createKeySection: (use: KeyUse, cert: string | Buffer) => {};
- constructMessageSignature: (octetString: string, key: string, passphrase?: string, isBase64?: boolean, signingAlgorithm?: string) => string;
- verifyMessageSignature: (metadata, octetString: string, signature: string | Buffer, verifyAlgorithm?: string) => boolean;
- getKeyInfo: (x509Certificate: string, signatureConfig?: any) => void;
- encryptAssertion: (sourceEntity, targetEntity, entireXML: string) => Promise;
- decryptAssertion: (here, entireXML: string) => Promise<[string, any]>;
-
- getSigningScheme: (sigAlg: string) => string | null;
- getDigestMethod: (sigAlg: string) => string | null;
-
- nrsaAliasMapping: any;
- defaultLoginRequestTemplate: LoginRequestTemplate;
- defaultLoginResponseTemplate: LoginResponseTemplate;
- defaultLogoutRequestTemplate: LogoutRequestTemplate;
- defaultLogoutResponseTemplate: LogoutResponseTemplate;
+export interface CustomTagReplacement<
+ Values extends Record = Record
+> {
+ (template: string, values: Values): readonly [template?: string, values?: Values];
}
const libSaml = () => {
-
- /**
- * @desc helper function to get back the query param for redirect binding for SLO/SSO
- * @type {string}
- */
- function getQueryParamByType(type: string) {
- if ([urlParams.logoutRequest, urlParams.samlRequest].indexOf(type) !== -1) {
- return 'SAMLRequest';
- }
- if ([urlParams.logoutResponse, urlParams.samlResponse].indexOf(type) !== -1) {
- return 'SAMLResponse';
- }
- throw new Error('ERR_UNDEFINED_QUERY_PARAMS');
- }
- /**
- *
- */
- const nrsaAliasMapping = {
- 'http://www.w3.org/2000/09/xmldsig#rsa-sha1': 'sha1',
- 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256': 'sha256',
- 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512': 'sha512',
- };
- /**
- * @desc Default login request template
- * @type {LoginRequestTemplate}
- */
- const defaultLoginRequestTemplate = {
- context: '{Issuer}',
- };
- /**
- * @desc Default logout request template
- * @type {LogoutRequestTemplate}
- */
- const defaultLogoutRequestTemplate = {
- context: '{Issuer}{NameID}',
- };
- /**
- * @desc Default login response template
- * @type {LoginResponseTemplate}
- */
- const defaultLoginResponseTemplate = {
- context: '{Issuer}{Issuer}{NameID}{Audience}{AuthnStatement}{AttributeStatement}',
- attributes: [],
- };
- /**
- * @desc Default logout response template
- * @type {LogoutResponseTemplate}
- */
- const defaultLogoutResponseTemplate = {
- context: '{Issuer}',
- };
- /**
- * @private
- * @desc Get the signing scheme alias by signature algorithms, used by the node-rsa module
- * @param {string} sigAlg signature algorithm
- * @return {string/null} signing algorithm short-hand for the module node-rsa
- */
- function getSigningScheme(sigAlg?: string): string | null {
- if (sigAlg) {
- const algAlias = nrsaAliasMapping[sigAlg];
- if (!(algAlias === undefined)) {
- return algAlias;
- }
- }
- return nrsaAliasMapping[signatureAlgorithms.RSA_SHA1]; // default value
- }
- /**
- * @private
- * @desc Get the digest algorithms by signature algorithms
- * @param {string} sigAlg signature algorithm
- * @return {string/null} digest algorithm
- */
- function getDigestMethod(sigAlg: string): string | null {
- const digestAlg = digestAlgorithms[sigAlg];
- if (!(digestAlg === undefined)) {
- return digestAlg;
- }
- return null; // default value
- }
- /**
- * @public
- * @desc Create XPath
- * @param {string/object} local parameters to create XPath
- * @param {boolean} isExtractAll define whether returns whole content according to the XPath
- * @return {string} xpath
- */
- function createXPath(local, isExtractAll?: boolean): string {
- if (isString(local)) {
- return isExtractAll === true ? "//*[local-name(.)='" + local + "']/text()" : "//*[local-name(.)='" + local + "']";
- }
- return "//*[local-name(.)='" + local.name + "']/@" + local.attr;
- }
-
- /**
- * @private
- * @desc Tag normalization
- * @param {string} prefix prefix of the tag
- * @param {content} content normalize it to capitalized camel case
- * @return {string}
- */
- function tagging(prefix: string, content: string): string {
- const camelContent = camelCase(content);
- return prefix + camelContent.charAt(0).toUpperCase() + camelContent.slice(1);
- }
-
- return {
-
- createXPath,
- getQueryParamByType,
- defaultLoginRequestTemplate,
- defaultLoginResponseTemplate,
- defaultLogoutRequestTemplate,
- defaultLogoutResponseTemplate,
-
- /**
- * @desc Repalce the tag (e.g. {tag}) inside the raw XML
- * @param {string} rawXML raw XML string used to do keyword replacement
- * @param {array} tagValues tag values
- * @return {string}
- */
- replaceTagsByValue(rawXML: string, tagValues: any): string {
- Object.keys(tagValues).forEach(t => {
- rawXML = rawXML.replace(new RegExp(`{${t}}`, 'g'), tagValues[t]);
- });
- return rawXML;
- },
- /**
- * @desc Helper function to build the AttributeStatement tag
- * @param {LoginResponseAttribute} attributes an array of attribute configuration
- * @return {string}
- */
- attributeStatementBuilder(attributes: LoginResponseAttribute[]): string {
- const attr = attributes.map(({ name, nameFormat, valueTag, valueXsiType, valueXmlnsXs, valueXmlnsXsi }) => {
- const defaultValueXmlnsXs = 'http://www.w3.org/2001/XMLSchema';
- const defaultValueXmlnsXsi = 'http://www.w3.org/2001/XMLSchema-instance';
- return `{${tagging('attr', valueTag)}}`;
- }).join('');
- return `${attr}`;
- },
- /**
- * @desc Construct the XML signature for POST binding
- * @param {string} rawSamlMessage request/response xml string
- * @param {string} referenceTagXPath reference uri
- * @param {string} privateKey declares the private key
- * @param {string} passphrase passphrase of the private key [optional]
- * @param {string|buffer} signingCert signing certificate
- * @param {string} signatureAlgorithm signature algorithm
- * @param {string[]} transformationAlgorithms canonicalization and transformation Algorithms
- * @return {string} base64 encoded string
- */
- constructSAMLSignature(opts: SignatureConstructor) {
- const {
- rawSamlMessage,
- referenceTagXPath,
- privateKey,
- privateKeyPass,
- signatureAlgorithm = signatureAlgorithms.RSA_SHA256,
- transformationAlgorithms = [
- 'http://www.w3.org/2000/09/xmldsig#enveloped-signature',
- 'http://www.w3.org/2001/10/xml-exc-c14n#',
- ],
- signingCert,
- signatureConfig,
- isBase64Output = true,
- isMessageSigned = false,
- } = opts;
- const sig = new SignedXml();
- // Add assertion sections as reference
- if (referenceTagXPath) {
- sig.addReference(
- referenceTagXPath,
- opts.transformationAlgorithms,
- getDigestMethod(signatureAlgorithm)
- );
- }
- if (isMessageSigned) {
- sig.addReference(
- // reference to the root node
- '/*',
- transformationAlgorithms,
- getDigestMethod(signatureAlgorithm),
- '',
- '',
- '',
- false,
- );
- }
- sig.signatureAlgorithm = signatureAlgorithm;
- sig.keyInfoProvider = new this.getKeyInfo(signingCert, signatureConfig);
- sig.signingKey = utility.readPrivateKey(privateKey, privateKeyPass, true);
- if (signatureConfig) {
- sig.computeSignature(rawSamlMessage, signatureConfig);
- } else {
- sig.computeSignature(rawSamlMessage);
- }
- return isBase64Output !== false ? utility.base64Encode(sig.getSignedXml()) : sig.getSignedXml();
- },
- /**
- * @desc Verify the XML signature
- * @param {string} xml xml
- * @param {signature} signature context of XML signature
- * @param {SignatureVerifierOptions} opts cert declares the X509 certificate
- * @return {boolean} verification result
- */
- verifySignature(xml: string, opts: SignatureVerifierOptions) {
-
- const doc = new dom().parseFromString(xml);
- // In order to avoid the wrapping attack, we have changed to use absolute xpath instead of naively fetching the signature element
- // message signature (logout response / saml response)
- const messageSignatureXpath = "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Signature']";
- // assertion signature (logout response / saml response)
- const assertionSignatureXpath = "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']/*[local-name(.)='Signature']";
- // check if there is a potential malicious wrapping signature
- const wrappingElementsXPath = "/*[contains(local-name(), 'Response')]/*[local-name(.)='Assertion']/*[local-name(.)='Subject']/*[local-name(.)='SubjectConfirmation']/*[local-name(.)='SubjectConfirmationData']//*[local-name(.)='Assertion' or local-name(.)='Signature']";
-
- // select the signature node
- let selection: any = [];
- let assertionNode: string | null = null;
- const messageSignatureNode = select(messageSignatureXpath, doc);
- const assertionSignatureNode = select(assertionSignatureXpath, doc);
- const wrappingElementNode = select(wrappingElementsXPath, doc);
-
- selection = selection.concat(messageSignatureNode);
- selection = selection.concat(assertionSignatureNode);
-
- // try to catch potential wrapping attack
- if (wrappingElementNode.length !== 0) {
- throw new Error('ERR_POTENTIAL_WRAPPING_ATTACK');
- }
-
- // guarantee to have a signature in saml response
- if (selection.length === 0) {
- throw new Error('ERR_ZERO_SIGNATURE');
- }
-
- const sig = new SignedXml();
- let verified = true;
- // need to refactor later on
- selection.forEach(signatureNode => {
-
- sig.signatureAlgorithm = opts.signatureAlgorithm;
-
- if (!opts.keyFile && !opts.metadata) {
- throw new Error('ERR_UNDEFINED_SIGNATURE_VERIFIER_OPTIONS');
- }
-
- if (opts.keyFile) {
- sig.keyInfoProvider = new FileKeyInfo(opts.keyFile);
- }
-
- if (opts.metadata) {
-
- const certificateNode = select(".//*[local-name(.)='X509Certificate']", signatureNode) as any;
- // certificate in metadata
- let metadataCert: any = opts.metadata.getX509Certificate(certUse.signing);
- // flattens the nested array of Certificates from each KeyDescriptor
- if (Array.isArray(metadataCert)) {
- metadataCert = flattenDeep(metadataCert);
- } else if (typeof metadataCert === 'string') {
- metadataCert = [metadataCert];
- }
- // normalise the certificate string
- metadataCert = metadataCert.map(utility.normalizeCerString);
-
- if (certificateNode.length === 0) {
- throw new Error('NO_SELECTED_CERTIFICATE');
- }
-
- // no certificate node in response
- if (certificateNode.length !== 0) {
- const x509CertificateData = certificateNode[0].firstChild.data;
- const x509Certificate = utility.normalizeCerString(x509CertificateData);
-
- if (
- metadataCert.length >= 1 &&
- !metadataCert.find(cert => cert.trim() === x509Certificate.trim())
- ) {
- // keep this restriction for rolling certificate usage
- // to make sure the response certificate is one of those specified in metadata
- throw new Error('ERROR_UNMATCH_CERTIFICATE_DECLARATION_IN_METADATA');
- }
-
- sig.keyInfoProvider = new this.getKeyInfo(x509Certificate);
-
- }
-
- }
-
- sig.loadSignature(signatureNode);
-
- doc.removeChild(signatureNode);
-
- verified = verified && sig.checkSignature(doc.toString());
-
- // immediately throw error when any one of the signature is failed to get verified
- if (!verified) {
- throw new Error('ERR_FAILED_TO_VERIFY_SIGNATURE');
- }
-
- });
-
- // response must be signed, either entire document or assertion
- // default we will take the assertion section under root
- if (messageSignatureNode.length === 1) {
- const node = select("/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']", doc);
- if (node.length === 1) {
- assertionNode = node[0].toString();
- }
- }
-
- if (assertionSignatureNode.length === 1) {
- const verifiedAssertionInfo = extract(assertionSignatureNode[0].toString(), [{
- key: 'refURI',
- localPath: ['Signature', 'SignedInfo', 'Reference'],
- attributes: ['URI']
- }]);
- // get the assertion supposed to be the one should be verified
- const desiredAssertionInfo = extract(doc.toString(), [{
- key: 'id',
- localPath: ['~Response', 'Assertion'],
- attributes: ['ID']
- }]);
- // 5.4.2 References
- // SAML assertions and protocol messages MUST supply a value for the ID attribute on the root element of
- // the assertion or protocol message being signed. The assertion’s or protocol message's root element may
- // or may not be the root element of the actual XML document containing the signed assertion or protocol
- // message (e.g., it might be contained within a SOAP envelope).
- // Signatures MUST contain a single containing a same-document reference to the ID
- // attribute value of the root element of the assertion or protocol message being signed. For example, if the
- // ID attribute value is "foo", then the URI attribute in the element MUST be "#foo".
- if (verifiedAssertionInfo.refURI !== `#${desiredAssertionInfo.id}`) {
- throw new Error('ERR_POTENTIAL_WRAPPING_ATTACK');
- }
- const verifiedDoc = extract(doc.toString(), [{
- key: 'assertion',
- localPath: ['~Response', 'Assertion'],
- attributes: [],
- context: true
- }]);
- assertionNode = verifiedDoc.assertion.toString();
- }
-
- return [verified, assertionNode];
- },
- /**
- * @desc Helper function to create the key section in metadata (abstraction for signing and encrypt use)
- * @param {string} use type of certificate (e.g. signing, encrypt)
- * @param {string} certString declares the certificate String
- * @return {object} object used in xml module
- */
- createKeySection(use: KeyUse, certString: string | Buffer): KeyComponent {
- return {
- ['KeyDescriptor']: [
- {
- _attr: { use },
- },
- {
- ['ds:KeyInfo']: [
- {
- _attr: {
- 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
- },
- },
- {
- ['ds:X509Data']: [{
- 'ds:X509Certificate': utility.normalizeCerString(certString),
- }],
- },
- ],
- }],
- };
- },
- /**
- * @desc Constructs SAML message
- * @param {string} octetString see "Bindings for the OASIS Security Assertion Markup Language (SAML V2.0)" P.17/46
- * @param {string} key declares the pem-formatted private key
- * @param {string} passphrase passphrase of private key [optional]
- * @param {string} signingAlgorithm signing algorithm
- * @return {string} message signature
- */
- constructMessageSignature(octetString: string, key: string, passphrase?: string, isBase64?: boolean, signingAlgorithm?: string) {
- // Default returning base64 encoded signature
- // Embed with node-rsa module
- const decryptedKey = new nrsa(utility.readPrivateKey(key, passphrase), {
- signingScheme: getSigningScheme(signingAlgorithm),
- });
- const signature = decryptedKey.sign(octetString);
- // Use private key to sign data
- return isBase64 !== false ? signature.toString('base64') : signature;
- },
- /**
- * @desc Verifies message signature
- * @param {Metadata} metadata metadata object of identity provider or service provider
- * @param {string} octetString see "Bindings for the OASIS Security Assertion Markup Language (SAML V2.0)" P.17/46
- * @param {string} signature context of XML signature
- * @param {string} verifyAlgorithm algorithm used to verify
- * @return {boolean} verification result
- */
- verifyMessageSignature(metadata, octetString: string, signature: string | Buffer, verifyAlgorithm?: string) {
- const signCert = metadata.getX509Certificate(certUse.signing);
- const signingScheme = getSigningScheme(verifyAlgorithm);
- const key = new nrsa(utility.getPublicKeyPemFromCertificate(signCert), { signingScheme });
- return key.verify(new Buffer(octetString), signature);
- },
- /**
- * @desc Get the public key in string format
- * @param {string} x509Certificate certificate
- * @return {string} public key
- */
- getKeyInfo(x509Certificate: string, signatureConfig: any = {}) {
- this.getKeyInfo = key => {
- const prefix = signatureConfig.prefix ? `${signatureConfig.prefix}:` : '';
- return `<${prefix}X509Data><${prefix}X509Certificate>${x509Certificate}${prefix}X509Certificate>${prefix}X509Data>`;
- };
- this.getKey = keyInfo => {
- return utility.getPublicKeyPemFromCertificate(x509Certificate).toString();
- };
- },
- /**
- * @desc Encrypt the assertion section in Response
- * @param {Entity} sourceEntity source entity
- * @param {Entity} targetEntity target entity
- * @param {string} xml response in xml string format
- * @return {Promise} a promise to resolve the finalized xml
- */
- encryptAssertion(sourceEntity, targetEntity, xml?: string) {
- // Implement encryption after signature if it has
- return new Promise((resolve, reject) => {
-
- if (!xml) {
- return reject(new Error('ERR_UNDEFINED_ASSERTION'));
- }
-
- const sourceEntitySetting = sourceEntity.entitySetting;
- const targetEntityMetadata = targetEntity.entityMeta;
- const doc = new dom().parseFromString(xml);
- const assertions = select("//*[local-name(.)='Assertion']", doc) as Node[];
- if (!Array.isArray(assertions)) {
- throw new Error('ERR_NO_ASSERTION');
- }
- if (assertions.length !== 1) {
- throw new Error('ERR_MULTIPLE_ASSERTION');
- }
- // Perform encryption depends on the setting, default is false
- if (sourceEntitySetting.isAssertionEncrypted) {
- xmlenc.encrypt(assertions[0].toString(), {
- // use xml-encryption module
- rsa_pub: Buffer.from(utility.getPublicKeyPemFromCertificate(targetEntityMetadata.getX509Certificate(certUse.encrypt)).replace(/\r?\n|\r/g, '')), // public key from certificate
- pem: Buffer.from('-----BEGIN CERTIFICATE-----' + targetEntityMetadata.getX509Certificate(certUse.encrypt) + '-----END CERTIFICATE-----'),
- encryptionAlgorithm: sourceEntitySetting.dataEncryptionAlgorithm,
- keyEncryptionAlgorithm: sourceEntitySetting.keyEncryptionAlgorithm,
- }, (err, res) => {
- if (err) {
- console.error(err);
- return reject(new Error('ERR_EXCEPTION_OF_ASSERTION_ENCRYPTION'));
- }
- if (!res) {
- return reject(new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION'));
- }
- const { encryptedAssertion: encAssertionPrefix } = sourceEntitySetting.tagPrefix;
- const encryptAssertionNode = new dom().parseFromString(`<${encAssertionPrefix}:EncryptedAssertion xmlns:${encAssertionPrefix}="${namespace.names.assertion}">${res}${encAssertionPrefix}:EncryptedAssertion>`);
- doc.replaceChild(encryptAssertionNode, assertions[0]);
- return resolve(utility.base64Encode(doc.toString()));
- });
- } else {
- return resolve(utility.base64Encode(xml)); // No need to do encrpytion
- }
- });
- },
- /**
- * @desc Decrypt the assertion section in Response
- * @param {string} type only accept SAMLResponse to proceed decryption
- * @param {Entity} here this entity
- * @param {Entity} from from the entity where the message is sent
- * @param {string} entireXML response in xml string format
- * @return {function} a promise to get back the entire xml with decrypted assertion
- */
- decryptAssertion(here, entireXML: string) {
- return new Promise<[string, any]>((resolve, reject) => {
- // Implement decryption first then check the signature
- if (!entireXML) {
- return reject(new Error('ERR_UNDEFINED_ASSERTION'));
- }
- // Perform encryption depends on the setting of where the message is sent, default is false
- const hereSetting = here.entitySetting;
- const xml = new dom().parseFromString(entireXML);
- const encryptedAssertions = select("/*[contains(local-name(), 'Response')]/*[local-name(.)='EncryptedAssertion']", xml) as Node[];
- if (!Array.isArray(encryptedAssertions)) {
- throw new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION');
- }
- if (encryptedAssertions.length !== 1) {
- throw new Error('ERR_MULTIPLE_ASSERTION');
- }
- return xmlenc.decrypt(encryptedAssertions[0].toString(), {
- key: utility.readPrivateKey(hereSetting.encPrivateKey, hereSetting.encPrivateKeyPass),
- }, (err, res) => {
- if (err) {
- console.error(err);
- return reject(new Error('ERR_EXCEPTION_OF_ASSERTION_DECRYPTION'));
- }
- if (!res) {
- return reject(new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION'));
- }
- const assertionNode = new dom().parseFromString(res);
- xml.replaceChild(assertionNode, encryptedAssertions[0]);
- return resolve([xml.toString(), res]);
- });
- });
- },
- /**
- * @desc Check if the xml string is valid and bounded
- */
- async isValidXml(input: string) {
-
- // check if global api contains the validate function
- const { validate } = getContext();
-
- /**
- * user can write a validate function that always returns
- * a resolved promise and skip the validator even in
- * production, user will take the responsibility if
- * they intend to skip the validation
- */
- if (!validate) {
-
- // otherwise, an error will be thrown
- return Promise.reject('Your application is potentially vulnerable because no validation function found. Please read the documentation on how to setup the validator. (https://github.com/tngan/samlify#installation)');
-
- }
-
- try {
- return await validate(input);
- } catch (e) {
- throw e;
- }
-
- },
- };
+ /**
+ * @desc helper function to get back the query param for redirect binding for SLO/SSO
+ * @type {string}
+ */
+ function getQueryParamByType(type: string) {
+ if (([urlParams.logoutRequest, urlParams.samlRequest] as string[]).includes(type)) {
+ return 'SAMLRequest';
+ }
+ if (([urlParams.logoutResponse, urlParams.samlResponse] as string[]).includes(type)) {
+ return 'SAMLResponse';
+ }
+ throw new SamlifyError(SamlifyErrorCode.UndefinedQueryParams);
+ }
+ /**
+ *
+ */
+ const nrsaAliasMapping = {
+ [signatureAlgorithms.RSA_SHA1]: 'pkcs1-sha1',
+ [signatureAlgorithms.RSA_SHA256]: 'pkcs1-sha256',
+ [signatureAlgorithms.RSA_SHA512]: 'pkcs1-sha512',
+ } as const;
+ /**
+ *
+ */
+ const defaultSignatureAlgorithm = signatureAlgorithms.RSA_SHA1;
+ /**
+ * @desc Default login request template
+ * @type {LoginRequestTemplate}
+ */
+ const defaultLoginRequestTemplate: LoginRequestTemplate = {
+ context:
+ '{Issuer}',
+ } as const;
+ /**
+ * @desc Default logout request template
+ * @type {LogoutRequestTemplate}
+ */
+ const defaultLogoutRequestTemplate: LogoutRequestTemplate = {
+ context:
+ '{Issuer}{NameID}',
+ };
+ /**
+ * @desc Default login response template
+ * @type {LoginResponseTemplate}
+ */
+ const defaultLoginResponseTemplate: LoginResponseTemplate = {
+ context:
+ '{Issuer}{Issuer}{NameID}{Audience}{AuthnStatement}{AttributeStatement}',
+ };
+ /**
+ * @desc Default logout response template
+ * @type {LogoutResponseTemplate}
+ */
+ const defaultLogoutResponseTemplate: LogoutResponseTemplate = {
+ context:
+ '{Issuer}',
+ };
+ /**
+ * @private
+ * @desc Get the signing scheme alias by signature algorithms, used by the node-rsa module
+ * @param {string} sigAlg signature algorithm
+ * @return {string} signing algorithm short-hand for the module node-rsa
+ */
+ function getSigningScheme(sigAlg?: RequestSignatureAlgorithm): NodeRSA.Options['signingScheme'] {
+ if (sigAlg) {
+ const algAlias = nrsaAliasMapping[sigAlg];
+ if (algAlias != null) return algAlias;
+ }
+ return nrsaAliasMapping[defaultSignatureAlgorithm]; // default value
+ }
+ /**
+ * @private
+ * @desc Get the digest algorithms by signature algorithms
+ * @param {string} sigAlg signature algorithm
+ * @return {string/null} digest algorithm
+ */
+ function getDigestMethod(sigAlg: keyof typeof digestAlgorithms) {
+ return digestAlgorithms[sigAlg];
+ }
+ /**
+ * @public
+ * @desc Create XPath
+ * @param {string/object} local parameters to create XPath
+ * @param {boolean} isExtractAll define whether returns whole content according to the XPath
+ * @return {string} xpath
+ */
+ function createXPath(local: string | { name: string; attr: string }, isExtractAll?: boolean): string {
+ if (isString(local)) {
+ return isExtractAll === true ? "//*[local-name(.)='" + local + "']/text()" : "//*[local-name(.)='" + local + "']";
+ }
+ return "//*[local-name(.)='" + local.name + "']/@" + local.attr;
+ }
+
+ /**
+ * @private
+ * @desc Tag normalization
+ * @param {string} prefix prefix of the tag
+ * @param {content} content normalize it to capitalized camel case
+ * @return {string}
+ */
+ function tagging(prefix: string, content: string): string {
+ const camelContent = camelCase(content);
+ return prefix + camelContent.charAt(0).toUpperCase() + camelContent.slice(1);
+ }
+
+ return {
+ createXPath,
+ getQueryParamByType,
+ defaultSignatureAlgorithm,
+ defaultLoginRequestTemplate,
+ defaultLoginResponseTemplate,
+ defaultLogoutRequestTemplate,
+ defaultLogoutResponseTemplate,
+
+ /**
+ * @desc Repalce the tag (e.g. {tag}) inside the raw XML
+ * @param {string} rawXML raw XML string used to do keyword replacement
+ * @param {Record} tagValues tag values
+ * @return {string}
+ */
+ replaceTagsByValue(rawXML: string, tagValues: Record): string {
+ Object.keys(tagValues).forEach((t) => {
+ rawXML = rawXML.replace(new RegExp(`{${t}}`, 'g'), tagValues[t]); // eslint-disable-line
+ });
+ return rawXML;
+ },
+ /**
+ * @desc Helper function to build the AttributeStatement tag
+ * @param {LoginResponseAttribute} attributes an array of attribute configuration
+ * @return {string}
+ */
+ attributeStatementBuilder(attributes: LoginResponseAttribute[]): string {
+ const attr = attributes
+ .map(({ name, nameFormat, valueTag, valueXsiType, valueXmlnsXs, valueXmlnsXsi }) => {
+ const defaultValueXmlnsXs = 'http://www.w3.org/2001/XMLSchema';
+ const defaultValueXmlnsXsi = 'http://www.w3.org/2001/XMLSchema-instance';
+ return `{${tagging(
+ 'attr',
+ valueTag
+ )}}`;
+ })
+ .join('');
+ return `${attr}`;
+ },
+ /* @desc Helper function to build the AttributeStatement tag values
+ * @param {LoginResponseAttribute} attributes an array of attribute configuration
+ * @param {any} user The user
+ * @return {any}
+ */
+ attributeStatementTagBuilder(
+ attributes: LoginResponseAttribute[],
+ user: Record
+ ): Record {
+ return attributes.reduce((r, { valueTag }) => {
+ const key = tagging('attr', valueTag);
+ const value = user[valueTag.replace('user.', '')];
+ if (key != null && value != null) r[key] = value;
+ return r;
+ }, {} as Record);
+ },
+ /**
+ * @desc Construct the XML signature for POST binding
+ * @param {string} rawSamlMessage request/response xml string
+ * @param {string} referenceTagXPath reference uri
+ * @param {string} privateKey declares the private key
+ * @param {string} passphrase passphrase of the private key [optional]
+ * @param {string|buffer} signingCert signing certificate
+ * @param {string} signatureAlgorithm signature algorithm
+ * @param {string[]} transformationAlgorithms canonicalization and transformation Algorithms
+ * @return {string} base64 encoded string
+ */
+ constructSAMLSignature(opts: SignatureConstructor) {
+ const {
+ rawSamlMessage,
+ referenceTagXPath,
+ privateKey,
+ privateKeyPass,
+ signatureAlgorithm = signatureAlgorithms.RSA_SHA256,
+ transformationAlgorithms = [
+ 'http://www.w3.org/2000/09/xmldsig#enveloped-signature',
+ 'http://www.w3.org/2001/10/xml-exc-c14n#',
+ ],
+ signingCert,
+ signatureConfig,
+ isBase64Output = true,
+ isMessageSigned = false,
+ } = opts;
+ const sig = new SignedXml();
+ // Add assertion sections as reference
+ if (referenceTagXPath) {
+ sig.addReference(referenceTagXPath, opts.transformationAlgorithms, getDigestMethod(signatureAlgorithm));
+ }
+ if (isMessageSigned) {
+ sig.addReference(
+ // reference to the root node
+ '/*',
+ transformationAlgorithms,
+ getDigestMethod(signatureAlgorithm),
+ '',
+ '',
+ '',
+ false
+ );
+ }
+ sig.signatureAlgorithm = signatureAlgorithm;
+ // @ts-expect-error todo
+ sig.keyInfoProvider = this.getKeyInfoProvider(signingCert, signatureConfig);
+ sig.signingKey = readPrivateKey(privateKey, privateKeyPass, true);
+ if (signatureConfig) {
+ sig.computeSignature(rawSamlMessage, signatureConfig);
+ } else {
+ sig.computeSignature(rawSamlMessage);
+ }
+ return isBase64Output !== false ? base64Encode(sig.getSignedXml()) : sig.getSignedXml();
+ },
+ /**
+ * @desc Verify the XML signature
+ * @param {string} xml xml
+ * @param {signature} signature context of XML signature
+ * @param {SignatureVerifierOptions} opts cert declares the X509 certificate
+ * @return {boolean} verification result
+ */
+ verifySignature(xml: string, opts: SignatureVerifierOptions) {
+ const doc = new dom().parseFromString(xml);
+ // In order to avoid the wrapping attack, we have changed to use absolute xpath instead of naively fetching the signature element
+ // message signature (logout response / saml response)
+ const messageSignatureXpath =
+ "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Signature']";
+ // assertion signature (logout response / saml response)
+ const assertionSignatureXpath =
+ "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']/*[local-name(.)='Signature']";
+ // check if there is a potential malicious wrapping signature
+ const wrappingElementsXPath =
+ "/*[contains(local-name(), 'Response')]/*[local-name(.)='Assertion']/*[local-name(.)='Subject']/*[local-name(.)='SubjectConfirmation']/*[local-name(.)='SubjectConfirmationData']//*[local-name(.)='Assertion' or local-name(.)='Signature']";
+
+ // select the signature node
+ let assertionNode: string | null = null;
+ const messageSignatureNode = select(messageSignatureXpath, doc);
+ const assertionSignatureNode = select(assertionSignatureXpath, doc);
+ const wrappingElementNode = select(wrappingElementsXPath, doc);
+
+ // try to catch potential wrapping attack
+ if (wrappingElementNode.length !== 0) {
+ throw new SamlifyError(SamlifyErrorCode.PotentialWrappingAttack);
+ }
+
+ const selection = [...messageSignatureNode, ...assertionSignatureNode].filter(isNode);
+
+ // guarantee to have a signature in saml response
+ if (selection.length === 0) {
+ throw new SamlifyError(SamlifyErrorCode.ZeroSignature);
+ }
+
+ const sig = new SignedXml();
+ let verified = true;
+ // need to refactor later on
+ selection.forEach((signatureNode) => {
+ sig.signatureAlgorithm = opts.signatureAlgorithm as string;
+
+ if (!opts.keyFile && !opts.metadata) {
+ throw new SamlifyError(SamlifyErrorCode.MissingOptionsForSignatureVerification);
+ }
+
+ if (opts.keyFile) {
+ sig.keyInfoProvider = new FileKeyInfo(opts.keyFile);
+ }
+
+ if (opts.metadata) {
+ const certificateNode = select(".//*[local-name(.)='X509Certificate']", signatureNode) as any;
+ // no certificate node in response
+ if (certificateNode.length === 0) {
+ throw new SamlifyError(SamlifyErrorCode.CertificateNotFound);
+ }
+
+ // certificate in metadata
+ let certs: string[];
+ const metadataCert = opts.metadata.getX509Certificate(certUse.signing);
+ // flattens the nested array of Certificates from each KeyDescriptor
+ if (Array.isArray(metadataCert)) {
+ certs = flattenDeep(metadataCert).map(normalizeCerString);
+ } else {
+ certs = [normalizeCerString(metadataCert)];
+ }
+
+ const x509CertificateData = certificateNode[0].firstChild.data;
+ const x509Certificate = normalizeCerString(x509CertificateData);
+
+ if (certs.length >= 1 && !certs.find((cert) => cert === x509Certificate)) {
+ // keep this restriction for rolling certificate usage
+ // to make sure the response certificate is one of those specified in metadata
+ throw new SamlifyError(SamlifyErrorCode.MismatchedCertificateDeclarationInMetadata);
+ }
+ // @ts-expect-error todo
+ sig.keyInfoProvider = this.getKeyInfoProvider(x509Certificate);
+ }
+
+ sig.loadSignature(signatureNode);
+
+ doc.removeChild(signatureNode);
+
+ verified = verified && sig.checkSignature(doc.toString());
+
+ // immediately throw error when any one of the signature is failed to get verified
+ if (!verified) {
+ throw new SamlifyError(SamlifyErrorCode.FailedToVerifySignature);
+ }
+ });
+
+ // response must be signed, either entire document or assertion
+ // default we will take the assertion section under root
+ if (messageSignatureNode.length === 1) {
+ const node = select(
+ "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']",
+ doc
+ );
+ if (node.length === 1 && node[0] != null) {
+ assertionNode = node[0].toString();
+ }
+ }
+
+ if (assertionSignatureNode.length === 1 && assertionSignatureNode[0] != null) {
+ const verifiedAssertionInfo = extract(assertionSignatureNode[0].toString(), [
+ {
+ key: 'refURI',
+ localPath: ['Signature', 'SignedInfo', 'Reference'],
+ attributes: ['URI'],
+ },
+ ]);
+ // get the assertion supposed to be the one should be verified
+ const desiredAssertionInfo = extract(doc.toString(), [
+ {
+ key: 'id',
+ localPath: ['~Response', 'Assertion'],
+ attributes: ['ID'],
+ },
+ ]);
+ // 5.4.2 References
+ // SAML assertions and protocol messages MUST supply a value for the ID attribute on the root element of
+ // the assertion or protocol message being signed. The assertion’s or protocol message's root element may
+ // or may not be the root element of the actual XML document containing the signed assertion or protocol
+ // message (e.g., it might be contained within a SOAP envelope).
+ // Signatures MUST contain a single containing a same-document reference to the ID
+ // attribute value of the root element of the assertion or protocol message being signed. For example, if the
+ // ID attribute value is "foo", then the URI attribute in the element MUST be "#foo".
+ if (verifiedAssertionInfo.refURI !== `#${desiredAssertionInfo.id}`) {
+ throw new SamlifyError(SamlifyErrorCode.PotentialWrappingAttack);
+ }
+ const verifiedDoc = extract(doc.toString(), [
+ {
+ key: 'assertion',
+ localPath: ['~Response', 'Assertion'],
+ attributes: [],
+ context: true,
+ },
+ ]);
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+ assertionNode = verifiedDoc.assertion?.toString();
+ }
+
+ return [verified, assertionNode];
+ },
+ /**
+ * @desc Helper function to create the key section in metadata (abstraction for signing and encrypt use)
+ * @param {string} use type of certificate (e.g. signing, encrypt)
+ * @param {string} certString declares the certificate String
+ * @return {object} object used in xml module
+ */
+ createKeySection(use: KeyUse, certString: string | Buffer): KeyComponent {
+ return {
+ ['KeyDescriptor']: [
+ {
+ _attr: { use },
+ },
+ {
+ ['ds:KeyInfo']: [
+ {
+ _attr: {
+ 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
+ },
+ },
+ {
+ ['ds:X509Data']: [
+ {
+ 'ds:X509Certificate': normalizeCerString(certString),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ };
+ },
+ /**
+ * @desc Constructs SAML message
+ * @param {string} octetString see "Bindings for the OASIS Security Assertion Markup Language (SAML V2.0)" P.17/46
+ * @param {string|Buffer} key declares the pem-formatted private key
+ * @param {string} passphrase passphrase of private key [optional]
+ * @param {string} signingAlgorithm signing algorithm
+ * @return {string|Buffer} message signature
+ */
+ constructMessageSignature(
+ octetString: string,
+ key: string | Buffer,
+ passphrase?: string,
+ signingAlgorithm?: RequestSignatureAlgorithm
+ ): Buffer {
+ // Default returning base64 encoded signature
+ // Embed with node-rsa module
+ const decryptedKey = new nrsa(readPrivateKey(key, passphrase), undefined, {
+ signingScheme: getSigningScheme(signingAlgorithm),
+ });
+ // Use private key to sign data
+ return decryptedKey.sign(octetString);
+ },
+ /**
+ * @desc Verifies message signature
+ * @param {Metadata} metadata metadata object of identity provider or service provider
+ * @param {string} octetString see "Bindings for the OASIS Security Assertion Markup Language (SAML V2.0)" P.17/46
+ * @param {string} signature context of XML signature
+ * @param {string} verifyAlgorithm algorithm used to verify
+ * @return {boolean} verification result
+ */
+ verifyMessageSignature(
+ metadata: Metadata,
+ octetString: NodeRSA.Data,
+ signature: Buffer,
+ verifyAlgorithm?: RequestSignatureAlgorithm
+ ) {
+ const signCert = metadata.getX509Certificate(certUse.signing);
+ const signingScheme = getSigningScheme(verifyAlgorithm);
+ const key = new nrsa(getPublicKeyPemFromCertificate(signCert), undefined, {
+ signingScheme,
+ });
+ return key.verify(octetString, signature);
+ },
+ /**
+ * @desc Get the public key in string format
+ * @param {string | Buffer} x509Certificate certificate
+ * @return {string} public key
+ */
+ getKeyInfoProvider(x509Certificate: string | Buffer, signatureConfig: any) {
+ return {
+ getKeyInfo: () => {
+ const prefix = signatureConfig?.prefix ? `${signatureConfig.prefix}:` : '';
+ return `<${prefix}X509Data><${prefix}X509Certificate>${x509Certificate}${prefix}X509Certificate>${prefix}X509Data>`;
+ },
+ getKey: () => {
+ return getPublicKeyPemFromCertificate(x509Certificate).toString();
+ },
+ };
+ },
+ /**
+ * @desc Encrypt the assertion section in Response
+ * @param {Entity} sourceEntity source entity
+ * @param {Entity} targetEntity target entity
+ * @param {string} xml response in xml string format
+ * @return {Promise} a promise to resolve the finalized xml
+ */
+ encryptAssertion(sourceEntity: Entity, targetEntity: Entity, xml?: string): Promise {
+ // Implement encryption after signature if it has
+ return new Promise((resolve, reject) => {
+ if (!xml) {
+ return reject(new SamlifyError(SamlifyErrorCode.UndefinedAssertion));
+ }
+
+ const sourceEntitySetting = sourceEntity.getEntitySettings();
+ const targetEntityMetadata = targetEntity.getEntityMeta();
+ const doc = new dom().parseFromString(xml);
+ const assertions = select("//*[local-name(.)='Assertion']", doc) as Node[];
+ if (!isNonEmptyArray(assertions) || assertions[0] == null) {
+ throw new SamlifyError(SamlifyErrorCode.UndefinedAssertion);
+ }
+ if (assertions.length > 1) {
+ throw new SamlifyError(SamlifyErrorCode.MultipleAssertion);
+ }
+ const assertion = assertions[0];
+ // Perform encryption depends on the setting, default is false
+ if (sourceEntitySetting.isAssertionEncrypted) {
+ if (!sourceEntitySetting.dataEncryptionAlgorithm) {
+ throw new SamlifyError(SamlifyErrorCode.MissingDataEncryptionAlgorithm);
+ }
+ if (!sourceEntitySetting.keyEncryptionAlgorithm) {
+ throw new SamlifyError(SamlifyErrorCode.MissingKeyEncryptionAlgorithm);
+ }
+ xmlenc.encrypt(
+ assertion.toString(),
+ {
+ // use xml-encryption module
+ rsa_pub: Buffer.from(
+ getPublicKeyPemFromCertificate(targetEntityMetadata.getX509Certificate(certUse.encrypt)).replace(
+ /\r?\n|\r/g,
+ ''
+ )
+ ), // public key from certificate
+ pem: Buffer.from(`
+-----BEGIN CERTIFICATE-----
+${targetEntityMetadata.getX509Certificate(certUse.encrypt)}
+-----END CERTIFICATE-----
+`),
+ encryptionAlgorithm: sourceEntitySetting.dataEncryptionAlgorithm,
+ keyEncryptionAlgorithm: sourceEntitySetting.keyEncryptionAlgorithm,
+ },
+ (err, res) => {
+ if (err) {
+ console.error(err);
+ return reject(new SamlifyError(SamlifyErrorCode.ExceptionOfAssertionEncryption));
+ }
+ if (!res) {
+ return reject(new SamlifyError(SamlifyErrorCode.UndefinedAssertion));
+ }
+ const encAssertionPrefix = sourceEntitySetting.tagPrefix?.encryptedAssertion;
+ const encryptAssertionNode = new dom().parseFromString(
+ `<${encAssertionPrefix}:EncryptedAssertion xmlns:${encAssertionPrefix}="${names.assertion}">${res}${encAssertionPrefix}:EncryptedAssertion>`
+ );
+ doc.replaceChild(encryptAssertionNode, assertion);
+ return resolve(base64Encode(doc.toString()));
+ }
+ );
+ } else {
+ return resolve(base64Encode(xml)); // No need to do encrpytion
+ }
+ });
+ },
+ /**
+ * @desc Decrypt the assertion section in Response
+ * @param {string} type only accept SAMLResponse to proceed decryption
+ * @param {Entity} here this entity
+ * @param {Entity} from from the entity where the message is sent
+ * @param {string} entireXML response in xml string format
+ * @return {function} a promise to get back the entire xml with decrypted assertion
+ */
+ decryptAssertion(here: Entity, entireXML: string) {
+ return new Promise<[string, any]>((resolve, reject) => {
+ // Implement decryption first then check the signature
+ if (!entireXML) {
+ return reject(new SamlifyError(SamlifyErrorCode.UndefinedAssertion));
+ }
+ // Perform encryption depends on the setting of where the message is sent, default is false
+ const hereSetting = here.getEntitySettings();
+ const xml = new dom().parseFromString(entireXML);
+ const encryptedAssertions = select(
+ "/*[contains(local-name(), 'Response')]/*[local-name(.)='EncryptedAssertion']",
+ xml
+ ) as Node[];
+ if (!isNonEmptyArray(encryptedAssertions) || encryptedAssertions[0] == null) {
+ throw new SamlifyError(SamlifyErrorCode.UndefinedAssertion);
+ }
+ if (encryptedAssertions.length !== 1) {
+ throw new SamlifyError(SamlifyErrorCode.MultipleAssertion);
+ }
+ const encryptedAssertion = encryptedAssertions[0];
+ if (!hereSetting.encPrivateKey) {
+ throw new SamlifyError(
+ SamlifyErrorCode.MissingEncPrivateKey,
+ `${here.constructor.name} is trying to decrypt assertion, but encPrivateKey was not provided.`
+ );
+ }
+ return xmlenc.decrypt(
+ encryptedAssertion.toString(),
+ {
+ key: readPrivateKey(hereSetting.encPrivateKey, hereSetting.encPrivateKeyPass),
+ },
+ (err, res) => {
+ if (err) {
+ console.error(err);
+ return reject(new SamlifyError(SamlifyErrorCode.ExceptionOfAssertionDecryption));
+ }
+ if (!res) {
+ return reject(new SamlifyError(SamlifyErrorCode.UndefinedAssertion));
+ }
+ const assertionNode = new dom().parseFromString(res);
+ xml.replaceChild(assertionNode, encryptedAssertion);
+ return resolve([xml.toString(), res]);
+ }
+ );
+ });
+ },
+ /**
+ * @desc Check if the xml string is valid and bounded
+ */
+ async isValidXml(input: string) {
+ // check if global api contains the validate function
+ const { validate } = getContext();
+
+ /**
+ * user can write a validate function that always returns
+ * a resolved promise and skip the validator even in
+ * production, user will take the responsibility if
+ * they intend to skip the validation
+ */
+ if (!validate) {
+ // otherwise, an error will be thrown
+ throw new SamlifyError(
+ SamlifyErrorCode.MissingValidation,
+ 'Your application is potentially vulnerable because no validation function found. Please read the documentation on how to setup the validator. (https://github.com/tngan/samlify#installation)'
+ );
+ }
+ return validate(input);
+ },
+ };
};
export default libSaml();
diff --git a/src/metadata-idp.ts b/src/metadata-idp.ts
index 1918d5a1..c14c33e8 100644
--- a/src/metadata-idp.ts
+++ b/src/metadata-idp.ts
@@ -1,150 +1,146 @@
/**
-* @file metadata-idp.ts
-* @author tngan
-* @desc Metadata of identity provider
-*/
-import Metadata, { MetadataInterface } from './metadata';
-import { MetadataIdpOptions, MetadataIdpConstructor } from './types';
-import { namespace } from './urn';
+ * @file metadata-idp.ts
+ * @author tngan
+ * @desc Metadata of identity provider
+ */
+import xml from 'xml';
+import { SamlifyError, SamlifyErrorCode } from './error';
import libsaml from './libsaml';
+import { Metadata } from './metadata';
+import type { MetadataIdpConstructorOptions } from './types';
+import { BindingNamespace, names } from './urn';
import { isNonEmptyArray, isString } from './utility';
-import * as xml from 'xml';
-
-export interface IdpMetadataInterface extends MetadataInterface {
-
-}
/*
* @desc interface function
*/
-export default function(meta: MetadataIdpConstructor) {
- return new IdpMetadata(meta);
+export default function (meta: MetadataIdpConstructorOptions) {
+ return new MetadataIdp(meta);
}
-export class IdpMetadata extends Metadata {
-
- constructor(meta: MetadataIdpConstructor) {
-
- const isFile = isString(meta) || meta instanceof Buffer;
-
- if (!isFile) {
-
- const {
- entityID,
- signingCert,
- encryptCert,
- wantAuthnRequestsSigned = false,
- nameIDFormat = [],
- singleSignOnService = [],
- singleLogoutService = [],
- } = meta as MetadataIdpOptions;
-
- const IDPSSODescriptor: any[] = [{
- _attr: {
- WantAuthnRequestsSigned: String(wantAuthnRequestsSigned),
- protocolSupportEnumeration: namespace.names.protocol,
- },
- }];
-
- if (signingCert) {
- IDPSSODescriptor.push(libsaml.createKeySection('signing', signingCert));
- } else {
- //console.warn('Construct identity provider - missing signing certificate');
- }
-
- if (encryptCert) {
- IDPSSODescriptor.push(libsaml.createKeySection('encryption', encryptCert));
- } else {
- //console.warn('Construct identity provider - missing encrypt certificate');
- }
-
- if (isNonEmptyArray(nameIDFormat)) {
- nameIDFormat.forEach(f => IDPSSODescriptor.push({ NameIDFormat: f }));
- }
-
- if (isNonEmptyArray(singleSignOnService)) {
- singleSignOnService.forEach((a, indexCount) => {
- const attr: any = {
- Binding: a.Binding,
- Location: a.Location,
- };
- if (a.isDefault) {
- attr.isDefault = true;
- }
- IDPSSODescriptor.push({ SingleSignOnService: [{ _attr: attr }] });
- });
- } else {
- throw new Error('ERR_IDP_METADATA_MISSING_SINGLE_SIGN_ON_SERVICE');
- }
-
- if (isNonEmptyArray(singleLogoutService)) {
- singleLogoutService.forEach((a, indexCount) => {
- const attr: any = {};
- if (a.isDefault) {
- attr.isDefault = true;
- }
- attr.Binding = a.Binding;
- attr.Location = a.Location;
- IDPSSODescriptor.push({ SingleLogoutService: [{ _attr: attr }] });
- });
- } else {
- console.warn('Construct identity provider - missing endpoint of SingleLogoutService');
- }
- // Create a new metadata by setting
- meta = xml([{
- EntityDescriptor: [{
- _attr: {
- 'xmlns': namespace.names.metadata,
- 'xmlns:assertion': namespace.names.assertion,
- 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
- entityID,
- },
- }, { IDPSSODescriptor }],
- }]);
- }
-
- super(meta as string | Buffer, [
- {
- key: 'wantAuthnRequestsSigned',
- localPath: ['EntityDescriptor', 'IDPSSODescriptor'],
- attributes: ['WantAuthnRequestsSigned'],
- },
- {
- key: 'singleSignOnService',
- localPath: ['EntityDescriptor', 'IDPSSODescriptor', 'SingleSignOnService'],
- index: ['Binding'],
- attributePath: [],
- attributes: ['Location']
- },
- ]);
-
- }
-
- /**
- * @desc Get the preference whether it wants a signed request
- * @return {boolean} WantAuthnRequestsSigned
- */
- isWantAuthnRequestsSigned(): boolean {
- const was = this.meta.wantAuthnRequestsSigned;
- if (was === undefined) {
- return false;
- }
- return String(was) === 'true';
- }
-
- /**
- * @desc Get the entity endpoint for single sign on service
- * @param {string} binding protocol binding (e.g. redirect, post)
- * @return {string/object} location
- */
- getSingleSignOnService(binding: string): string | object {
- if (isString(binding)) {
- const bindName = namespace.binding[binding];
- const service = this.meta.singleSignOnService[bindName];
- if (service) {
- return service;
- }
- }
- return this.meta.singleSignOnService;
- }
+export class MetadataIdp extends Metadata {
+ constructor(meta: MetadataIdpConstructorOptions) {
+ // use object configuation instead of importing metadata file directly
+ if (!(isString(meta) || meta instanceof Buffer)) {
+ const {
+ entityID,
+ signingCert,
+ encryptCert,
+ wantAuthnRequestsSigned = false,
+ nameIDFormat = [],
+ singleSignOnService = [],
+ singleLogoutService = [],
+ } = meta;
+
+ const IDPSSODescriptor: any[] = [
+ {
+ _attr: {
+ WantAuthnRequestsSigned: String(wantAuthnRequestsSigned),
+ protocolSupportEnumeration: names.protocol,
+ },
+ },
+ ];
+
+ if (signingCert) {
+ IDPSSODescriptor.push(libsaml.createKeySection('signing', signingCert));
+ } else {
+ //console.warn('Construct identity provider - missing signing certificate');
+ }
+
+ if (encryptCert) {
+ IDPSSODescriptor.push(libsaml.createKeySection('encryption', encryptCert));
+ } else {
+ //console.warn('Construct identity provider - missing encrypt certificate');
+ }
+
+ if (isNonEmptyArray(nameIDFormat)) {
+ nameIDFormat.forEach((f) => IDPSSODescriptor.push({ NameIDFormat: f }));
+ }
+
+ if (isNonEmptyArray(singleSignOnService)) {
+ singleSignOnService.forEach((a) => {
+ const attr: any = {
+ Binding: a.Binding,
+ Location: a.Location,
+ };
+ if (a.isDefault) {
+ attr.isDefault = true;
+ }
+ IDPSSODescriptor.push({ SingleSignOnService: [{ _attr: attr }] });
+ });
+ } else {
+ throw new SamlifyError(SamlifyErrorCode.MetadataIdpMissingSingleSignOnService);
+ }
+
+ if (isNonEmptyArray(singleLogoutService)) {
+ singleLogoutService.forEach((a) => {
+ const attr: any = {};
+ if (a.isDefault) {
+ attr.isDefault = true;
+ }
+ attr.Binding = a.Binding;
+ attr.Location = a.Location;
+ IDPSSODescriptor.push({ SingleLogoutService: [{ _attr: attr }] });
+ });
+ } else {
+ console.warn('Construct identity provider - missing endpoint of SingleLogoutService');
+ }
+ // Create a new metadata by setting
+ meta = xml([
+ {
+ EntityDescriptor: [
+ {
+ _attr: {
+ xmlns: names.metadata,
+ 'xmlns:assertion': names.assertion,
+ 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
+ entityID,
+ },
+ },
+ { IDPSSODescriptor },
+ ],
+ },
+ ]);
+ }
+
+ super(meta, [
+ {
+ key: 'wantAuthnRequestsSigned',
+ localPath: ['EntityDescriptor', 'IDPSSODescriptor'],
+ attributes: ['WantAuthnRequestsSigned'],
+ },
+ {
+ key: 'singleSignOnService',
+ localPath: ['EntityDescriptor', 'IDPSSODescriptor', 'SingleSignOnService'],
+ index: ['Binding'],
+ attributePath: [],
+ attributes: ['Location'],
+ },
+ ]);
+ }
+
+ /**
+ * @desc Get the preference whether it wants a signed request
+ * @return {boolean} WantAuthnRequestsSigned
+ */
+ isWantAuthnRequestsSigned(): boolean {
+ const was = this.meta.wantAuthnRequestsSigned;
+ if (was === undefined) {
+ return false;
+ }
+ return String(was) === 'true';
+ }
+
+ /**
+ * @desc Get the entity endpoint for single sign on service
+ * @param {string} binding protocol binding (e.g. redirect, post)
+ * @return {string/object} location
+ */
+ getSingleSignOnService(protocol: BindingNamespace): string {
+ const service = this.meta.singleSignOnService[protocol];
+ if (isString(service)) {
+ return service;
+ }
+ throw new SamlifyError(SamlifyErrorCode.SingleSignOnLocationNotFound);
+ }
}
diff --git a/src/metadata-sp.ts b/src/metadata-sp.ts
index a33ecc76..687d3ba2 100644
--- a/src/metadata-sp.ts
+++ b/src/metadata-sp.ts
@@ -1,207 +1,196 @@
/**
-* @file metadata-sp.ts
-* @author tngan
-* @desc Metadata of service provider
-*/
-import Metadata, { MetadataInterface } from './metadata';
-import { MetadataSpConstructor, MetadataSpOptions } from './types';
-import { namespace, elementsOrder as order } from './urn';
+ * @file metadata-sp.ts
+ * @author tngan
+ * @desc Metadata of service provider
+ */
+import xml from 'xml';
import libsaml from './libsaml';
+import { Metadata } from './metadata';
+import type { MetadataSpConstructorOptions, MetaElement } from './types';
+import { BindingNamespace, elementsOrder as order, names } from './urn';
import { isNonEmptyArray, isString } from './utility';
-import * as xml from 'xml';
-
-export interface SpMetadataInterface extends MetadataInterface {
-
-}
-
-// https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf (P.16, 18)
-interface MetaElement {
- KeyDescriptor?: any[];
- NameIDFormat?: any[];
- SingleLogoutService?: any[];
- AssertionConsumerService?: any[];
- AttributeConsumingService?: any[];
-}
/*
* @desc interface function
*/
-export default function(meta: MetadataSpConstructor) {
- return new SpMetadata(meta);
+export default function (meta: MetadataSpConstructorOptions) {
+ return new MetadataSp(meta);
}
/**
-* @desc SP Metadata is for creating Service Provider, provides a set of API to manage the actions in SP.
-*/
-export class SpMetadata extends Metadata {
-
- /**
- * @param {object/string} meta (either xml string or configuation in object)
- * @return {object} prototypes including public functions
- */
- constructor(meta: MetadataSpConstructor) {
-
- const isFile = isString(meta) || meta instanceof Buffer;
-
- // use object configuation instead of importing metadata file directly
- if (!isFile) {
-
- const {
- elementsOrder = order.default,
- entityID,
- signingCert,
- encryptCert,
- authnRequestsSigned = false,
- wantAssertionsSigned = false,
- wantMessageSigned = false,
- signatureConfig,
- nameIDFormat = [],
- singleLogoutService = [],
- assertionConsumerService = [],
- } = meta as MetadataSpOptions;
-
- const descriptors: MetaElement = {
- KeyDescriptor: [],
- NameIDFormat: [],
- SingleLogoutService: [],
- AssertionConsumerService: [],
- AttributeConsumingService: [],
- };
-
- const SPSSODescriptor: any[] = [{
- _attr: {
- AuthnRequestsSigned: String(authnRequestsSigned),
- WantAssertionsSigned: String(wantAssertionsSigned),
- protocolSupportEnumeration: namespace.names.protocol,
- },
- }];
-
- if (wantMessageSigned && signatureConfig === undefined) {
- console.warn('Construct service provider - missing signatureConfig');
- }
-
- if (signingCert) {
- descriptors.KeyDescriptor!.push(libsaml.createKeySection('signing', signingCert).KeyDescriptor);
- } else {
- //console.warn('Construct service provider - missing signing certificate');
- }
-
- if (encryptCert) {
- descriptors.KeyDescriptor!.push(libsaml.createKeySection('encryption', encryptCert).KeyDescriptor);
- } else {
- //console.warn('Construct service provider - missing encrypt certificate');
- }
-
- if (isNonEmptyArray(nameIDFormat)) {
- nameIDFormat.forEach(f => descriptors.NameIDFormat!.push(f));
- } else {
- // default value
- descriptors.NameIDFormat!.push(namespace.format.emailAddress);
- }
-
- if (isNonEmptyArray(singleLogoutService)) {
- singleLogoutService.forEach(a => {
- const attr: any = {
- Binding: a.Binding,
- Location: a.Location,
- };
- if (a.isDefault) {
- attr.isDefault = true;
- }
- descriptors.SingleLogoutService!.push([{ _attr: attr }]);
- });
- }
-
- if (isNonEmptyArray(assertionConsumerService)) {
- let indexCount = 0;
- assertionConsumerService.forEach(a => {
- const attr: any = {
- index: String(indexCount++),
- Binding: a.Binding,
- Location: a.Location,
- };
- if (a.isDefault) {
- attr.isDefault = true;
- }
- descriptors.AssertionConsumerService!.push([{ _attr: attr }]);
- });
- } else {
- // console.warn('Missing endpoint of AssertionConsumerService');
- }
-
- // handle element order
- const existedElements = elementsOrder.filter(name => isNonEmptyArray(descriptors[name]));
- existedElements.forEach(name => {
- descriptors[name].forEach(e => SPSSODescriptor.push({ [name]: e }));
- });
-
- // Re-assign the meta reference as a XML string|Buffer for use with the parent constructor
- meta = xml([{
- EntityDescriptor: [{
- _attr: {
- entityID,
- 'xmlns': namespace.names.metadata,
- 'xmlns:assertion': namespace.names.assertion,
- 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
- },
- }, { SPSSODescriptor }],
- }]);
-
- }
-
- // Use the re-assigned meta object reference here
- super(meta as string | Buffer, [
- {
- key: 'spSSODescriptor',
- localPath: ['EntityDescriptor', 'SPSSODescriptor'],
- attributes: ['WantAssertionsSigned', 'AuthnRequestsSigned'],
- },
- {
- key: 'assertionConsumerService',
- localPath: ['EntityDescriptor', 'SPSSODescriptor', 'AssertionConsumerService'],
- attributes: ['Binding', 'Location', 'isDefault', 'index'],
- }
- ]);
-
- }
-
- /**
- * @desc Get the preference whether it wants a signed assertion response
- * @return {boolean} Wantassertionssigned
- */
- public isWantAssertionsSigned(): boolean {
- return this.meta.spSSODescriptor.wantAssertionsSigned === 'true';
- }
- /**
- * @desc Get the preference whether it signs request
- * @return {boolean} Authnrequestssigned
- */
- public isAuthnRequestSigned(): boolean {
- return this.meta.spSSODescriptor.authnRequestsSigned === 'true';
- }
- /**
- * @desc Get the entity endpoint for assertion consumer service
- * @param {string} binding protocol binding (e.g. redirect, post)
- * @return {string/[string]} URL of endpoint(s)
- */
- public getAssertionConsumerService(binding: string): string | string[] {
- if (isString(binding)) {
- let location;
- const bindName = namespace.binding[binding];
- if (isNonEmptyArray(this.meta.assertionConsumerService)) {
- this.meta.assertionConsumerService.forEach(obj => {
- if (obj.binding === bindName) {
- location = obj.location;
- return;
- }
- });
- } else {
- if (this.meta.assertionConsumerService.binding === bindName) {
- location = this.meta.assertionConsumerService.location;
- }
- }
- return location;
- }
- return this.meta.assertionConsumerService;
- }
+ * @desc SP Metadata is for creating Service Provider, provides a set of API to manage the actions in SP.
+ */
+export class MetadataSp extends Metadata {
+ /**
+ * @param {object/string} meta (either xml string or configuation in object)
+ * @return {object} prototypes including public functions
+ */
+ constructor(meta: MetadataSpConstructorOptions) {
+ // use object configuation instead of importing metadata file directly
+ if (!(isString(meta) || meta instanceof Buffer)) {
+ const {
+ elementsOrder = order.default,
+ entityID,
+ signingCert,
+ encryptCert,
+ authnRequestsSigned = false,
+ wantAssertionsSigned = false,
+ wantMessageSigned = false,
+ signatureConfig,
+ nameIDFormat = [],
+ singleLogoutService = [],
+ assertionConsumerService = [],
+ } = meta;
+
+ const descriptors: MetaElement = {
+ KeyDescriptor: [],
+ NameIDFormat: [],
+ SingleLogoutService: [],
+ AssertionConsumerService: [],
+ AttributeConsumingService: [],
+ };
+
+ const SPSSODescriptor: any[] = [
+ {
+ _attr: {
+ AuthnRequestsSigned: String(authnRequestsSigned),
+ WantAssertionsSigned: String(wantAssertionsSigned),
+ protocolSupportEnumeration: names.protocol,
+ },
+ },
+ ];
+
+ if (wantMessageSigned && signatureConfig === undefined) {
+ console.warn('Construct service provider - missing signatureConfig');
+ }
+
+ if (signingCert) {
+ descriptors.KeyDescriptor?.push(libsaml.createKeySection('signing', signingCert).KeyDescriptor);
+ } else {
+ //console.warn('Construct service provider - missing signing certificate');
+ }
+
+ if (encryptCert) {
+ descriptors.KeyDescriptor?.push(libsaml.createKeySection('encryption', encryptCert).KeyDescriptor);
+ } else {
+ //console.warn('Construct service provider - missing encrypt certificate');
+ }
+
+ if (isNonEmptyArray(nameIDFormat)) {
+ nameIDFormat.forEach((f) => descriptors.NameIDFormat?.push(f));
+ } else {
+ // default value
+ descriptors.NameIDFormat?.push(names.nameidFormat.emailAddress);
+ }
+
+ if (isNonEmptyArray(singleLogoutService)) {
+ singleLogoutService.forEach((a) => {
+ const attr: any = {
+ Binding: a.Binding,
+ Location: a.Location,
+ };
+ if (a.isDefault) {
+ attr.isDefault = true;
+ }
+ descriptors.SingleLogoutService?.push([{ _attr: attr }]);
+ });
+ }
+
+ if (isNonEmptyArray(assertionConsumerService)) {
+ let indexCount = 0;
+ assertionConsumerService.forEach((a) => {
+ const attr: any = {
+ index: String(indexCount++),
+ Binding: a.Binding,
+ Location: a.Location,
+ };
+ if (a.isDefault) {
+ attr.isDefault = true;
+ }
+ descriptors.AssertionConsumerService?.push([{ _attr: attr }]);
+ });
+ } else {
+ // console.warn('Missing endpoint of AssertionConsumerService');
+ }
+
+ // handle element order
+ const existedElements = elementsOrder.filter((name) => isNonEmptyArray(descriptors[name]));
+ existedElements.forEach((name) => {
+ descriptors[name]?.forEach((e) => SPSSODescriptor.push({ [name]: e }));
+ });
+
+ // Re-assign the meta reference as a XML string|Buffer for use with the parent constructor
+ meta = xml([
+ {
+ EntityDescriptor: [
+ {
+ _attr: {
+ entityID,
+ xmlns: names.metadata,
+ 'xmlns:assertion': names.assertion,
+ 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
+ },
+ },
+ { SPSSODescriptor },
+ ],
+ },
+ ]);
+ }
+
+ // Use the re-assigned meta object reference here
+ super(meta, [
+ {
+ key: 'spSSODescriptor',
+ localPath: ['EntityDescriptor', 'SPSSODescriptor'],
+ attributes: ['WantAssertionsSigned', 'AuthnRequestsSigned'],
+ },
+ {
+ key: 'assertionConsumerService',
+ localPath: ['EntityDescriptor', 'SPSSODescriptor', 'AssertionConsumerService'],
+ attributes: ['Binding', 'Location', 'isDefault', 'index'],
+ },
+ ]);
+ }
+
+ /**
+ * @desc Get the preference whether it wants a signed assertion response
+ * @return {boolean} Wantassertionssigned
+ */
+ public isWantAssertionsSigned(): boolean {
+ return this.meta.spSSODescriptor.wantAssertionsSigned === 'true';
+ }
+ /**
+ * @desc Get the preference whether it signs request
+ * @return {boolean} Authnrequestssigned
+ */
+ public isAuthnRequestSigned(): boolean {
+ return this.meta.spSSODescriptor.authnRequestsSigned === 'true';
+ }
+ /**
+ * @desc Get the entity endpoint for assertion consumer service
+ * @param {string} protocol protocol binding (e.g. redirect, post)
+ * @return {string/[string]} URL of endpoint(s)
+ */
+ public getAssertionConsumerService(protocol: BindingNamespace): string | string[] {
+ if (isString(protocol)) {
+ let location;
+ if (isNonEmptyArray(this.meta.assertionConsumerService)) {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+ this.meta.assertionConsumerService.forEach((obj: any) => {
+ if (obj.binding === protocol) {
+ location = obj.location;
+ return;
+ }
+ });
+ } else {
+ if (this.meta.assertionConsumerService.binding === protocol) {
+ location = this.meta.assertionConsumerService.location;
+ }
+ }
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return location;
+ }
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.meta.assertionConsumerService;
+ }
}
diff --git a/src/metadata.ts b/src/metadata.ts
index c3db6ebc..5ba0df49 100644
--- a/src/metadata.ts
+++ b/src/metadata.ts
@@ -1,166 +1,159 @@
/**
-* @file metadata.ts
-* @author tngan
-* @desc An abstraction for metadata of identity provider and service provider
-*/
-import * as fs from 'fs';
-import { namespace } from './urn';
+ * @file metadata.ts
+ * @author tngan
+ * @desc An abstraction for metadata of identity provider and service provider
+ */
+import fs from 'fs';
+import { SamlifyError, SamlifyErrorCode } from './error';
import { extract } from './extractor';
+import type { BindingNamespace } from './urn';
import { isString } from './utility';
-export interface MetadataInterface {
- xmlString: string;
- getMetadata: () => string;
- exportMetadata: (exportFile: string) => void;
- getEntityID: () => string;
- getX509Certificate: (certType: string) => string | string[];
- getNameIDFormat: () => any[];
- getSingleLogoutService: (binding: string | undefined) => string | object;
- getSupportBindings: (services: string[]) => string[];
-}
-
-export default class Metadata implements MetadataInterface {
-
- xmlString: string;
- meta: any;
-
- /**
- * @param {string | Buffer} metadata xml
- * @param {object} extraParse for custom metadata extractor
- */
- constructor(xml: string | Buffer, extraParse: any = []) {
- this.xmlString = xml.toString();
- this.meta = extract(this.xmlString, extraParse.concat([
- {
- key: 'entityDescriptor',
- localPath: ['EntityDescriptor'],
- attributes: [],
- context: true
- },
- {
- key: 'entityID',
- localPath: ['EntityDescriptor'],
- attributes: ['entityID']
- },
- {
- // shared certificate for both encryption and signing
- key: 'sharedCertificate',
- localPath: ['EntityDescriptor', '~SSODescriptor', 'KeyDescriptor', 'KeyInfo', 'X509Data', 'X509Certificate'],
- attributes: []
- },
- {
- // explicit certificate declaration for encryption and signing
- key: 'certificate',
- localPath: ['EntityDescriptor', '~SSODescriptor', 'KeyDescriptor'],
- index: ['use'],
- attributePath: ['KeyInfo', 'X509Data', 'X509Certificate'],
- attributes: []
- },
- {
- key: 'singleLogoutService',
- localPath: ['EntityDescriptor', '~SSODescriptor', 'SingleLogoutService'],
- attributes: ['Binding', 'Location']
- },
- {
- key: 'nameIDFormat',
- localPath: ['EntityDescriptor', '~SSODescriptor', 'NameIDFormat'],
- attributes: [],
- }
- ]));
+export class Metadata {
+ private xmlString: string;
+ protected meta: any;
- // get shared certificate
- const sharedCertificate = this.meta.sharedCertificate;
- if (typeof sharedCertificate === 'string') {
- this.meta.certificate = {
- signing: sharedCertificate,
- encryption: sharedCertificate
- };
- delete this.meta.sharedCertificate;
- }
+ /**
+ * @param {string | Buffer} metadata xml
+ * @param {object} extraParse for custom metadata extractor
+ */
+ constructor(xml: string | Buffer, extraParse: any = []) {
+ this.xmlString = xml.toString();
+ this.meta = extract(
+ this.xmlString,
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+ extraParse.concat([
+ {
+ key: 'entityDescriptor',
+ localPath: ['EntityDescriptor'],
+ attributes: [],
+ context: true,
+ },
+ {
+ key: 'entityID',
+ localPath: ['EntityDescriptor'],
+ attributes: ['entityID'],
+ },
+ {
+ // shared certificate for both encryption and signing
+ key: 'sharedCertificate',
+ localPath: ['EntityDescriptor', '~SSODescriptor', 'KeyDescriptor', 'KeyInfo', 'X509Data', 'X509Certificate'],
+ attributes: [],
+ },
+ {
+ // explicit certificate declaration for encryption and signing
+ key: 'certificate',
+ localPath: ['EntityDescriptor', '~SSODescriptor', 'KeyDescriptor'],
+ index: ['use'],
+ attributePath: ['KeyInfo', 'X509Data', 'X509Certificate'],
+ attributes: [],
+ },
+ {
+ key: 'singleLogoutService',
+ localPath: ['EntityDescriptor', '~SSODescriptor', 'SingleLogoutService'],
+ attributes: ['Binding', 'Location'],
+ },
+ {
+ key: 'nameIDFormat',
+ localPath: ['EntityDescriptor', '~SSODescriptor', 'NameIDFormat'],
+ attributes: [],
+ },
+ ])
+ );
- if (
- Array.isArray(this.meta.entityDescriptor) &&
- this.meta.entityDescriptor.length > 1
- ) {
- throw new Error('ERR_MULTIPLE_METADATA_ENTITYDESCRIPTOR');
- }
+ // get shared certificate
+ const sharedCertificate = this.meta.sharedCertificate;
+ if (typeof sharedCertificate === 'string') {
+ this.meta.certificate = {
+ signing: sharedCertificate,
+ encryption: sharedCertificate,
+ };
+ delete this.meta.sharedCertificate;
+ }
- }
+ if (Array.isArray(this.meta.entityDescriptor) && this.meta.entityDescriptor.length > 1) {
+ throw new SamlifyError(SamlifyErrorCode.MultipleMetadataEntityDescriptor);
+ }
+ }
- /**
- * @desc Get the metadata in xml format
- * @return {string} metadata in xml format
- */
- public getMetadata(): string {
- return this.xmlString;
- }
+ /**
+ * @desc Get the metadata in xml format
+ * @return {string} metadata in xml format
+ */
+ public getMetadata(): string {
+ return this.xmlString;
+ }
- /**
- * @desc Export the metadata to specific file
- * @param {string} exportFile is the output file path
- */
- public exportMetadata(exportFile: string): void {
- fs.writeFileSync(exportFile, this.xmlString);
- }
+ /**
+ * @desc Export the metadata to specific file
+ * @param {string} exportFile is the output file path
+ */
+ public exportMetadata(exportFile: string): void {
+ fs.writeFileSync(exportFile, this.xmlString);
+ }
- /**
- * @desc Get the entityID in metadata
- * @return {string} entityID
- */
- public getEntityID(): string {
- return this.meta.entityID;
- }
+ /**
+ * @desc Get the entityID in metadata
+ * @return {string} entityID
+ */
+ public getEntityID(): string {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.meta.entityID;
+ }
- /**
- * @desc Get the x509 certificate declared in entity metadata
- * @param {string} use declares the type of certificate
- * @return {string} certificate in string format
- */
- public getX509Certificate(use: string): string | string[] {
- return this.meta.certificate[use] || null;
- }
+ /**
+ * @desc Get the x509 certificate declared in entity metadata
+ * @param {string} use declares the type of certificate
+ * @return {string} certificate in string format
+ */
+ public getX509Certificate(use: string): string | Buffer {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.meta.certificate[use] || null;
+ }
- /**
- * @desc Get the support NameID format declared in entity metadata
- * @return {array} support NameID format
- */
- public getNameIDFormat(): any {
- return this.meta.nameIDFormat;
- }
+ /**
+ * @desc Get the support NameID format declared in entity metadata
+ * @return {array} support NameID format
+ */
+ public getNameIDFormat(): any {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.meta.nameIDFormat;
+ }
- /**
- * @desc Get the entity endpoint for single logout service
- * @param {string} binding e.g. redirect, post
- * @return {string/object} location
- */
- public getSingleLogoutService(binding: string | undefined): string | object {
- if (binding && isString(binding)) {
- const bindType = namespace.binding[binding];
- let singleLogoutService = this.meta.singleLogoutService;
- if (!(singleLogoutService instanceof Array)) {
- singleLogoutService = [singleLogoutService];
- }
- const service = singleLogoutService.find(obj => obj.binding === bindType);
- if (service) {
- return service.location;
- }
- }
- return this.meta.singleLogoutService;
- }
+ /**
+ * @desc Get the entity endpoint for single logout service
+ * @param {string} protocol e.g. redirect, post
+ * @return {string} location
+ */
+ public getSingleLogoutService(protocol: BindingNamespace): string {
+ let singleLogoutService = this.meta.singleLogoutService;
+ if (!Array.isArray(singleLogoutService)) {
+ singleLogoutService = [singleLogoutService];
+ }
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+ const service = singleLogoutService.find((obj: any) => obj.binding === protocol && isString(obj.location));
+ if (service) {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return service.location;
+ }
+ throw new SamlifyError(SamlifyErrorCode.SingleLogoutLocationNotFound);
+ }
- /**
- * @desc Get the support bindings
- * @param {[string]} services
- * @return {[string]} support bindings
- */
- public getSupportBindings(services: string[]): string[] {
- let supportBindings = [];
- if (services) {
- supportBindings = services.reduce((acc: any, service) => {
- const supportBinding = Object.keys(service)[0];
- return acc.push(supportBinding);
- }, []);
- }
- return supportBindings;
- }
+ /**
+ * @desc Get the support bindings
+ * @param {[string]} services
+ * @return {[string]} support bindings
+ */
+ public getSupportBindings(services: string[]): string[] {
+ let supportBindings = [];
+ if (services) {
+ supportBindings = services.reduce((acc: any, service) => {
+ const supportBinding = Object.keys(service)[0];
+ // eslint-disable-next-line
+ return acc.push(supportBinding);
+ }, []);
+ }
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return supportBindings;
+ }
}
diff --git a/src/types.ts b/src/types.ts
index 92ca7ef7..66d170ba 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,127 +1,137 @@
-import { LoginResponseTemplate } from './libsaml';
-
-export { IdentityProvider as IdentityProviderConstructor } from './entity-idp';
-export { IdpMetadata as IdentityProviderMetadata } from './metadata-idp';
-
-export { ServiceProvider as ServiceProviderConstructor } from './entity-sp';
-export { SpMetadata as ServiceProviderMetadata } from './metadata-sp';
-
-export type MetadataFile = string | Buffer;
-
-type SSOService = {
- isDefault?: boolean;
- Binding: string;
- Location: string;
-};
-
-export interface MetadataIdpOptions {
- entityID?: string;
- signingCert?: string | Buffer;
- encryptCert?: string | Buffer;
- wantAuthnRequestsSigned?: boolean;
- nameIDFormat?: string[];
- singleSignOnService?: SSOService[];
- singleLogoutService?: SSOService[];
- requestSignatureAlgorithm?: string;
+import type { EncryptionAlgorithm, KeyEncryptionAlgorithm } from 'xml-encryption';
+import type { LoginResponseTemplate, LogoutResponseTemplate } from './libsaml';
+import type { BindingNamespace, MessageSignatureOrder } from './urn';
+
+interface SSOService {
+ isDefault?: boolean;
+ Binding: BindingNamespace;
+ Location: string;
}
-export type MetadataIdpConstructor =
- | MetadataIdpOptions
- | MetadataFile;
-
-export interface MetadataSpOptions {
- entityID?: string;
- signingCert?: string | Buffer;
- encryptCert?: string | Buffer;
- authnRequestsSigned?: boolean;
- wantAssertionsSigned?: boolean;
- wantMessageSigned?: boolean;
- signatureConfig?: { [key: string]: any };
- nameIDFormat?: string[];
- singleSignOnService?: SSOService[];
- singleLogoutService?: SSOService[];
- assertionConsumerService?: SSOService[];
- elementsOrder?: string[];
+export type RequestSignatureAlgorithm =
+ | 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
+ | 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
+ | 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512';
+
+// https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf (P.16, 18)
+export interface MetaElement {
+ AssertionConsumerService?: any[];
+ AttributeConsumingService?: any[];
+ KeyDescriptor?: any[];
+ NameIDFormat?: any[];
+ SingleLogoutService?: any[];
}
-export type MetadataSpConstructor =
- | MetadataSpOptions
- | MetadataFile;
+interface MetadataOptions {
+ encryptCert?: string | Buffer;
+ entityID?: string;
+ nameIDFormat?: string[];
+ signingCert?: string | Buffer;
+ singleLogoutService?: SSOService[];
+ singleSignOnService?: SSOService[];
+}
+
+interface MetadataIdpOptions extends MetadataOptions {
+ requestSignatureAlgorithm?: RequestSignatureAlgorithm;
+ wantAuthnRequestsSigned?: boolean;
+}
+
+interface MetadataSpOptions extends MetadataOptions {
+ assertionConsumerService?: SSOService[];
+ authnRequestsSigned?: boolean;
+ elementsOrder?: (keyof MetaElement)[];
+ signatureConfig?: { [key: string]: any };
+ wantAssertionsSigned?: boolean;
+ wantMessageSigned?: boolean;
+}
+
+type MetadataFile = string | Buffer;
-export type EntitySetting = ServiceProviderSettings & IdentityProviderSettings;
+export type MetadataIdpConstructorOptions = MetadataIdpOptions | MetadataFile;
+export type MetadataSpConstructorOptions = MetadataSpOptions | MetadataFile;
export interface SignatureConfig {
- prefix?: string;
- location?: {
- reference?: string;
- action?: 'append' | 'prepend' | 'before' | 'after';
- };
+ prefix?: string;
+ location?: {
+ reference?: string;
+ action?: 'append' | 'prepend' | 'before' | 'after';
+ };
}
export interface SAMLDocumentTemplate {
- context?: string;
+ context?: string;
}
-export type ServiceProviderSettings = {
- metadata?: string | Buffer;
- entityID?: string;
- authnRequestsSigned?: boolean;
- wantAssertionsSigned?: boolean;
- wantMessageSigned?: boolean;
- wantLogoutResponseSigned?: boolean;
- wantLogoutRequestSigned?: boolean;
- privateKey?: string | Buffer;
- privateKeyPass?: string;
- isAssertionEncrypted?: boolean;
- requestSignatureAlgorithm?: string;
- encPrivateKey?: string | Buffer;
- encPrivateKeyPass?: string | Buffer;
- assertionConsumerService?: SSOService[];
- singleLogoutService?: SSOService[];
- signatureConfig?: SignatureConfig;
- loginRequestTemplate?: SAMLDocumentTemplate;
- logoutRequestTemplate?: SAMLDocumentTemplate;
- signingCert?: string | Buffer;
- encryptCert?: string | Buffer;
- transformationAlgorithms?: string[];
- nameIDFormat?: string[];
- allowCreate?: boolean;
- // will be deprecated soon
- relayState?: string;
- // https://github.com/tngan/samlify/issues/337
- clockDrifts?: [number, number];
-};
-
-export type IdentityProviderSettings = {
- metadata?: string | Buffer;
-
- /** signature algorithm */
- requestSignatureAlgorithm?: string;
-
- /** template of login response */
- loginResponseTemplate?: LoginResponseTemplate;
-
- /** template of logout request */
- logoutRequestTemplate?: SAMLDocumentTemplate;
-
- /** customized function used for generating request ID */
- generateID?: () => string;
-
- entityID?: string;
- privateKey?: string | Buffer;
- privateKeyPass?: string;
- signingCert?: string | Buffer;
- encryptCert?: string | Buffer; /** todo */
- nameIDFormat?: string[];
- singleSignOnService?: SSOService[];
- singleLogoutService?: SSOService[];
- isAssertionEncrypted?: boolean;
- encPrivateKey?: string | Buffer;
- encPrivateKeyPass?: string;
- messageSigningOrder?: string;
- wantLogoutRequestSigned?: boolean;
- wantLogoutResponseSigned?: boolean;
- wantAuthnRequestsSigned?: boolean;
- wantLogoutRequestSignedResponseSigned?: boolean;
- tagPrefix?: { [key: string]: string };
-};
+export interface EntitySettings {
+ metadata?: string | Buffer;
+ entityID?: string;
+ assertionConsumerService?: SSOService[];
+ singleLogoutService?: SSOService[];
+
+ authnRequestsSigned?: boolean;
+ isAssertionEncrypted?: boolean;
+
+ /** signature algorithm */
+ requestSignatureAlgorithm?: RequestSignatureAlgorithm;
+ dataEncryptionAlgorithm?: EncryptionAlgorithm;
+ keyEncryptionAlgorithm?: KeyEncryptionAlgorithm;
+
+ messageSigningOrder?: MessageSignatureOrder;
+ signatureConfig?: SignatureConfig;
+ transformationAlgorithms?: string[];
+ wantAssertionsSigned?: boolean;
+ wantLogoutRequestSigned?: boolean;
+ wantLogoutResponseSigned?: boolean;
+ wantMessageSigned?: boolean;
+
+ signingCert?: string | Buffer;
+ privateKey?: string | Buffer;
+ privateKeyPass?: string;
+
+ encryptCert?: string | Buffer;
+ encPrivateKey?: string | Buffer;
+ encPrivateKeyPass?: string;
+
+ /** template of login request */
+ loginRequestTemplate?: SAMLDocumentTemplate;
+ /** template of logout request */
+ logoutRequestTemplate?: SAMLDocumentTemplate;
+ /** template of logout response */
+ logoutResponseTemplate?: LogoutResponseTemplate;
+
+ nameIDFormat?: string[];
+ allowCreate?: boolean;
+ // will be deprecated soon
+ relayState?: string;
+ // https://github.com/tngan/samlify/issues/337
+ clockDrifts?: [number, number];
+ /** customized function used for generating request ID */
+ generateID?: () => string;
+
+ /** Declare the tag of specific xml document node. `TagPrefixKey` currently supports `encryptedAssertion` only */
+ tagPrefix?: { encryptedAssertion?: string };
+}
+
+export interface ServiceProviderSettings extends EntitySettings {
+ authnRequestsSigned?: boolean;
+ wantAssertionsSigned?: boolean;
+ wantMessageSigned?: boolean;
+ assertionConsumerService?: SSOService[];
+ loginRequestTemplate?: SAMLDocumentTemplate;
+ logoutRequestTemplate?: SAMLDocumentTemplate;
+ transformationAlgorithms?: string[];
+ allowCreate?: boolean;
+ // will be deprecated soon
+ relayState?: string;
+ // https://github.com/tngan/samlify/issues/337
+ clockDrifts?: [number, number];
+}
+
+export interface IdentityProviderSettings extends EntitySettings {
+ /** template of login response */
+ loginResponseTemplate?: LoginResponseTemplate;
+
+ singleSignOnService?: SSOService[];
+ wantAuthnRequestsSigned?: boolean;
+ wantLogoutRequestSignedResponseSigned?: boolean;
+}
diff --git a/src/urn.ts b/src/urn.ts
index 7a15a25c..bff8a6e1 100644
--- a/src/urn.ts
+++ b/src/urn.ts
@@ -1,207 +1,187 @@
/**
-* @file urn.ts
-* @author tngan
-* @desc Includes all keywords need in samlify
-*/
+ * @file urn.ts
+ * @author tngan
+ * @desc Includes all keywords need in samlify
+ */
+import type { MetaElement } from './types';
export enum BindingNamespace {
- Redirect = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
- Post = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
- Artifact = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact'
+ Redirect = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
+ Post = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
+ Artifact = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
}
export enum MessageSignatureOrder {
- STE = 'sign-then-encrypt',
- ETS = 'encrypt-then-sign'
+ STE = 'sign-then-encrypt',
+ ETS = 'encrypt-then-sign',
}
export enum StatusCode {
- // top-tier
- Success = 'urn:oasis:names:tc:SAML:2.0:status:Success',
- Requester = 'urn:oasis:names:tc:SAML:2.0:status:Requester',
- Responder = 'urn:oasis:names:tc:SAML:2.0:status:Responder',
- VersionMismatch = 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch',
- // second-tier to provide more information
- AuthFailed = 'urn:oasis:names:tc:SAML:2.0:status:AuthnFailed',
- InvalidAttrNameOrValue = 'urn:oasis:names:tc:SAML:2.0:status:InvalidAttrNameOrValue',
- InvalidNameIDPolicy = 'urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy',
- NoAuthnContext = 'urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext',
- NoAvailableIDP = 'urn:oasis:names:tc:SAML:2.0:status:NoAvailableIDP',
- NoPassive = 'urn:oasis:names:tc:SAML:2.0:status:NoPassive',
- NoSupportedIDP = 'urn:oasis:names:tc:SAML:2.0:status:NoSupportedIDP',
- PartialLogout = 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout',
- ProxyCountExceeded = 'urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded',
- RequestDenied = 'urn:oasis:names:tc:SAML:2.0:status:RequestDenied',
- RequestUnsupported = 'urn:oasis:names:tc:SAML:2.0:status:RequestUnsupported',
- RequestVersionDeprecated = 'urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated',
- RequestVersionTooHigh = 'urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooHigh',
- RequestVersionTooLow = 'urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooLow',
- ResourceNotRecognized = 'urn:oasis:names:tc:SAML:2.0:status:ResourceNotRecognized',
- TooManyResponses = 'urn:oasis:names:tc:SAML:2.0:status:TooManyResponses',
- UnknownAttrProfile = 'urn:oasis:names:tc:SAML:2.0:status:UnknownAttrProfile',
- UnknownPrincipal = 'urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal',
- UnsupportedBinding = 'urn:oasis:names:tc:SAML:2.0:status:UnsupportedBinding',
+ // top-tier
+ Success = 'urn:oasis:names:tc:SAML:2.0:status:Success',
+ Requester = 'urn:oasis:names:tc:SAML:2.0:status:Requester',
+ Responder = 'urn:oasis:names:tc:SAML:2.0:status:Responder',
+ VersionMismatch = 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch',
+ // second-tier to provide more information
+ AuthFailed = 'urn:oasis:names:tc:SAML:2.0:status:AuthnFailed',
+ InvalidAttrNameOrValue = 'urn:oasis:names:tc:SAML:2.0:status:InvalidAttrNameOrValue',
+ InvalidNameIDPolicy = 'urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy',
+ NoAuthnContext = 'urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext',
+ NoAvailableIDP = 'urn:oasis:names:tc:SAML:2.0:status:NoAvailableIDP',
+ NoPassive = 'urn:oasis:names:tc:SAML:2.0:status:NoPassive',
+ NoSupportedIDP = 'urn:oasis:names:tc:SAML:2.0:status:NoSupportedIDP',
+ PartialLogout = 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout',
+ ProxyCountExceeded = 'urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded',
+ RequestDenied = 'urn:oasis:names:tc:SAML:2.0:status:RequestDenied',
+ RequestUnsupported = 'urn:oasis:names:tc:SAML:2.0:status:RequestUnsupported',
+ RequestVersionDeprecated = 'urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated',
+ RequestVersionTooHigh = 'urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooHigh',
+ RequestVersionTooLow = 'urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooLow',
+ ResourceNotRecognized = 'urn:oasis:names:tc:SAML:2.0:status:ResourceNotRecognized',
+ TooManyResponses = 'urn:oasis:names:tc:SAML:2.0:status:TooManyResponses',
+ UnknownAttrProfile = 'urn:oasis:names:tc:SAML:2.0:status:UnknownAttrProfile',
+ UnknownPrincipal = 'urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal',
+ UnsupportedBinding = 'urn:oasis:names:tc:SAML:2.0:status:UnsupportedBinding',
}
-const namespace = {
- binding: {
- redirect: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
- post: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
- artifact: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
- },
- names: {
- protocol: 'urn:oasis:names:tc:SAML:2.0:protocol',
- assertion: 'urn:oasis:names:tc:SAML:2.0:assertion',
- metadata: 'urn:oasis:names:tc:SAML:2.0:metadata',
- userLogout: 'urn:oasis:names:tc:SAML:2.0:logout:user',
- adminLogout: 'urn:oasis:names:tc:SAML:2.0:logout:admin',
- },
- authnContextClassRef: {
- password: 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password',
- passwordProtectedTransport: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport',
- },
- format: {
- emailAddress: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
- persistent: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
- transient: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
- entity: 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity',
- unspecified: 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
- kerberos: 'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos',
- windowsDomainQualifiedName: 'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName',
- x509SubjectName: 'urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName',
- },
- statusCode: {
- // permissible top-level status codes
- success: 'urn:oasis:names:tc:SAML:2.0:status:Success',
- requester: 'urn:oasis:names:tc:SAML:2.0:status:Requester',
- responder: 'urn:oasis:names:tc:SAML:2.0:status:Responder',
- versionMismatch: 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch',
- // second-level status codes
- authFailed: 'urn:oasis:names:tc:SAML:2.0:status:AuthnFailed',
- invalidAttrNameOrValue: 'urn:oasis:names:tc:SAML:2.0:status:InvalidAttrNameOrValue',
- invalidNameIDPolicy: 'urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy',
- noAuthnContext: 'urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext',
- noAvailableIDP: 'urn:oasis:names:tc:SAML:2.0:status:NoAvailableIDP',
- noPassive: 'urn:oasis:names:tc:SAML:2.0:status:NoPassive',
- noSupportedIDP: 'urn:oasis:names:tc:SAML:2.0:status:NoSupportedIDP',
- partialLogout: 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout',
- proxyCountExceeded: 'urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded',
- requestDenied: 'urn:oasis:names:tc:SAML:2.0:status:RequestDenied',
- requestUnsupported: 'urn:oasis:names:tc:SAML:2.0:status:RequestUnsupported',
- requestVersionDeprecated: 'urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated',
- requestVersionTooHigh: 'urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooHigh',
- requestVersionTooLow: 'urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooLow',
- resourceNotRecognized: 'urn:oasis:names:tc:SAML:2.0:status:ResourceNotRecognized',
- tooManyResponses: 'urn:oasis:names:tc:SAML:2.0:status:TooManyResponses',
- unknownAttrProfile: 'urn:oasis:names:tc:SAML:2.0:status:UnknownAttrProfile',
- unknownPrincipal: 'urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal',
- unsupportedBinding: 'urn:oasis:names:tc:SAML:2.0:status:UnsupportedBinding',
- },
-};
+export const names = {
+ protocol: 'urn:oasis:names:tc:SAML:2.0:protocol',
+ assertion: 'urn:oasis:names:tc:SAML:2.0:assertion',
+ metadata: 'urn:oasis:names:tc:SAML:2.0:metadata',
+ logout: {
+ user: 'urn:oasis:names:tc:SAML:2.0:logout:user',
+ admin: 'urn:oasis:names:tc:SAML:2.0:logout:admin',
+ },
+ // authnContextClassRef
+ ac: {
+ Password: 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password',
+ PasswordProtectedTransport: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport',
+ unspecified: 'urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified',
+ },
+ nameidFormat: {
+ emailAddress: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
+ persistent: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
+ transient: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
+ entity: 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity',
+ unspecified: 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
+ kerberos: 'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos',
+ WindowsDomainQualifiedName: 'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName',
+ X509SubjectName: 'urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName',
+ },
+ attrnameFormat: {
+ basic: 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
+ unspecified: 'urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified',
+ },
+} as const;
-const tags = {
- request: {
- AllowCreate: '{AllowCreate}',
- AssertionConsumerServiceURL: '{AssertionConsumerServiceURL}',
- AuthnContextClassRef: '{AuthnContextClassRef}',
- AssertionID: '{AssertionID}',
- Audience: '{Audience}',
- AuthnStatement: '{AuthnStatement}',
- AttributeStatement: '{AttributeStatement}',
- ConditionsNotBefore: '{ConditionsNotBefore}',
- ConditionsNotOnOrAfter: '{ConditionsNotOnOrAfter}',
- Destination: '{Destination}',
- EntityID: '{EntityID}',
- ID: '{ID}',
- Issuer: '{Issuer}',
- IssueInstant: '{IssueInstant}',
- InResponseTo: '{InResponseTo}',
- NameID: '{NameID}',
- NameIDFormat: '{NameIDFormat}',
- ProtocolBinding: '{ProtocolBinding}',
- SessionIndex: '{SessionIndex}',
- SubjectRecipient: '{SubjectRecipient}',
- SubjectConfirmationDataNotOnOrAfter: '{SubjectConfirmationDataNotOnOrAfter}',
- StatusCode: '{StatusCode}',
- },
- xmlTag: {
- loginRequest: 'AuthnRequest',
- logoutRequest: 'LogoutRequest',
- loginResponse: 'Response',
- logoutResponse: 'LogoutResponse',
- },
-};
+export const tags = {
+ request: {
+ AllowCreate: '{AllowCreate}',
+ AssertionConsumerServiceURL: '{AssertionConsumerServiceURL}',
+ AuthnContextClassRef: '{AuthnContextClassRef}',
+ AssertionID: '{AssertionID}',
+ Audience: '{Audience}',
+ AuthnStatement: '{AuthnStatement}',
+ AttributeStatement: '{AttributeStatement}',
+ ConditionsNotBefore: '{ConditionsNotBefore}',
+ ConditionsNotOnOrAfter: '{ConditionsNotOnOrAfter}',
+ Destination: '{Destination}',
+ EntityID: '{EntityID}',
+ ID: '{ID}',
+ Issuer: '{Issuer}',
+ IssueInstant: '{IssueInstant}',
+ InResponseTo: '{InResponseTo}',
+ NameID: '{NameID}',
+ NameIDFormat: '{NameIDFormat}',
+ ProtocolBinding: '{ProtocolBinding}',
+ SessionIndex: '{SessionIndex}',
+ SubjectRecipient: '{SubjectRecipient}',
+ SubjectConfirmationDataNotOnOrAfter: '{SubjectConfirmationDataNotOnOrAfter}',
+ StatusCode: '{StatusCode}',
+ },
+ xmlTag: {
+ loginRequest: 'AuthnRequest',
+ logoutRequest: 'LogoutRequest',
+ loginResponse: 'Response',
+ logoutResponse: 'LogoutResponse',
+ },
+} as const;
-const messageConfigurations = {
- signingOrder: {
- SIGN_THEN_ENCRYPT: 'sign-then-encrypt',
- ENCRYPT_THEN_SIGN: 'encrypt-then-sign',
- },
-};
+export const messageConfigurations = {
+ signingOrder: {
+ SIGN_THEN_ENCRYPT: 'sign-then-encrypt',
+ ENCRYPT_THEN_SIGN: 'encrypt-then-sign',
+ },
+} as const;
-const algorithms = {
- signature: {
- RSA_SHA1: 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
- RSA_SHA256: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
- RSA_SHA512: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512',
- },
- encryption: {
- data: {
- AES_128: 'http://www.w3.org/2001/04/xmlenc#aes128-cbc',
- AES_256: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',
- TRI_DEC: 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc',
- AES_128_GCM: 'http://www.w3.org/2009/xmlenc11#aes128-gcm'
- },
- key: {
- RSA_OAEP_MGF1P: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
- RSA_1_5: 'http://www.w3.org/2001/04/xmlenc#rsa-1_5',
- },
- },
- digest: {
- 'http://www.w3.org/2000/09/xmldsig#rsa-sha1': 'http://www.w3.org/2000/09/xmldsig#sha1',
- 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256': 'http://www.w3.org/2001/04/xmlenc#sha256',
- 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512': 'http://www.w3.org/2001/04/xmlenc#sha512', // support hashing algorithm sha512 in xml-crypto after 0.8.0
- },
-};
+export const algorithms = {
+ signature: {
+ RSA_SHA1: 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
+ RSA_SHA256: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
+ RSA_SHA512: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512',
+ },
+ encryption: {
+ data: {
+ AES_128: 'http://www.w3.org/2001/04/xmlenc#aes128-cbc',
+ AES_256: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',
+ TRI_DEC: 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc',
+ AES_128_GCM: 'http://www.w3.org/2009/xmlenc11#aes128-gcm',
+ },
+ key: {
+ RSA_OAEP_MGF1P: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
+ RSA_1_5: 'http://www.w3.org/2001/04/xmlenc#rsa-1_5', // no longer the default
+ },
+ },
+ digest: {
+ 'http://www.w3.org/2000/09/xmldsig#rsa-sha1': 'http://www.w3.org/2000/09/xmldsig#sha1',
+ 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256': 'http://www.w3.org/2001/04/xmlenc#sha256',
+ 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512': 'http://www.w3.org/2001/04/xmlenc#sha512', // support hashing algorithm sha512 in xml-crypto after 0.8.0
+ },
+} as const;
export enum ParserType {
- SAMLRequest = 'SAMLRequest',
- SAMLResponse = 'SAMLResponse',
- LogoutRequest = 'LogoutRequest',
- LogoutResponse = 'LogoutResponse'
+ SAMLRequest = 'SAMLRequest',
+ SAMLResponse = 'SAMLResponse',
+ LogoutRequest = 'LogoutRequest',
+ LogoutResponse = 'LogoutResponse',
}
-const wording = {
- urlParams: {
- samlRequest: 'SAMLRequest',
- samlResponse: 'SAMLResponse',
- logoutRequest: 'LogoutRequest',
- logoutResponse: 'LogoutResponse',
- sigAlg: 'SigAlg',
- signature: 'Signature',
- relayState: 'RelayState',
- },
- binding: {
- redirect: 'redirect',
- post: 'post',
- artifact: 'artifact',
- },
- certUse: {
- signing: 'signing',
- encrypt: 'encryption',
- },
- metadata: {
- sp: 'metadata-sp',
- idp: 'metadata-idp',
- },
-};
+export const wording = {
+ urlParams: {
+ samlRequest: 'SAMLRequest',
+ samlResponse: 'SAMLResponse',
+ logoutRequest: 'LogoutRequest',
+ logoutResponse: 'LogoutResponse',
+ sigAlg: 'SigAlg',
+ signature: 'Signature',
+ relayState: 'RelayState',
+ },
+ certUse: {
+ signing: 'signing',
+ encrypt: 'encryption',
+ },
+} as const;
// https://wiki.shibboleth.net/confluence/display/CONCEPT/MetadataForSP
// some idps restrict the order of elements in entity descriptors
-const elementsOrder = {
- default: ['KeyDescriptor', 'NameIDFormat', 'SingleLogoutService', 'AssertionConsumerService'],
- onelogin: ['KeyDescriptor', 'NameIDFormat', 'SingleLogoutService', 'AssertionConsumerService'],
- shibboleth: ['KeyDescriptor', 'SingleLogoutService', 'NameIDFormat', 'AssertionConsumerService', 'AttributeConsumingService'],
-};
-
-export { namespace, tags, algorithms, wording, elementsOrder, messageConfigurations };
+export const elementsOrder = {
+ default: [
+ 'KeyDescriptor',
+ 'NameIDFormat',
+ 'SingleLogoutService',
+ 'AssertionConsumerService',
+ ] as (keyof MetaElement)[],
+ onelogin: [
+ 'KeyDescriptor',
+ 'NameIDFormat',
+ 'SingleLogoutService',
+ 'AssertionConsumerService',
+ ] as (keyof MetaElement)[],
+ shibboleth: [
+ 'KeyDescriptor',
+ 'SingleLogoutService',
+ 'NameIDFormat',
+ 'AssertionConsumerService',
+ 'AttributeConsumingService',
+ ] as (keyof MetaElement)[],
+} as const;
diff --git a/src/utility.ts b/src/utility.ts
index 3efb4c74..586fa8c6 100644
--- a/src/utility.ts
+++ b/src/utility.ts
@@ -1,10 +1,10 @@
/**
-* @file utility.ts
-* @author tngan
-* @desc Library for some common functions (e.g. de/inflation, en/decoding)
-*/
-import { pki, util, asn1 } from 'node-forge';
-import { inflate, deflate } from 'pako';
+ * @file utility.ts
+ * @author tngan
+ * @desc Library for some common functions (e.g. de/inflation, en/decoding)
+ */
+import { asn1, pki, util } from 'node-forge';
+import { deflate, inflate } from 'pako';
const BASE64_STR = 'base64';
@@ -14,42 +14,36 @@ const BASE64_STR = 'base64';
* @param arr2 {[]}
*/
export function zipObject(arr1: string[], arr2: any[], skipDuplicated = true) {
- return arr1.reduce((res, l, i) => {
-
- if (skipDuplicated) {
- res[l] = arr2[i];
- return res;
- }
- // if key exists, aggregate with array in order to get rid of duplicate key
- if (res[l] !== undefined) {
- res[l] = Array.isArray(res[l])
- ? res[l].concat(arr2[i])
- : [res[l]].concat(arr2[i]);
- return res;
- }
-
- res[l] = arr2[i];
- return res;
-
- }, {});
+ return arr1.reduce((res, l, i) => {
+ if (skipDuplicated) {
+ res[l] = arr2[i];
+ return res;
+ }
+ // if key exists, aggregate with array in order to get rid of duplicate key
+ if (res[l] !== undefined) {
+ const arr: any[] = Array.isArray(res[l]) ? res[l] : [res[l]];
+ res[l] = arr.concat(arr2[i]);
+ return res;
+ }
+ res[l] = arr2[i];
+ return res;
+ }, {} as Record);
}
/**
* @desc Alternative to lodash.flattenDeep
* @reference https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_flattendeep
* @param input {[]}
*/
-export function flattenDeep(input: any[]) {
- return Array.isArray(input)
- ? input.reduce( (a, b) => a.concat(flattenDeep(b)) , [])
- : [input];
+export function flattenDeep(input: T | T[]): T[] {
+ return Array.isArray(input) ? input.reduce((a, b) => a.concat(flattenDeep(b)), [] as T[]) : [input];
}
/**
* @desc Alternative to lodash.last
* @reference https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_last
* @param input {[]}
*/
-export function last(input: any[]) {
- return input.slice(-1)[0];
+export function last(input: T[]) {
+ return input.slice(-1)[0];
}
/**
* @desc Alternative to lodash.uniq
@@ -57,169 +51,150 @@ export function last(input: any[]) {
* @param input {string[]}
*/
export function uniq(input: string[]) {
- const set = new Set(input);
- return [... set];
+ const set = new Set(input);
+ return [...set];
}
/**
- * @desc Alternative to lodash.get
+ * @desc Alternative to lodash.get
* @reference https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_get
- * @param obj
- * @param path
- * @param defaultValue
+ * @param obj
+ * @param path
+ * @param defaultValue
*/
-export function get(obj, path, defaultValue) {
- return path.split('.')
- .reduce((a, c) => (a && a[c] ? a[c] : (defaultValue || null)), obj);
+export function get(obj: any, path: string, defaultValue?: D): T | D {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return path.split('.').reduce((a, c) => (a && a[c] ? a[c] : defaultValue ?? null), obj) ?? null;
}
/**
- * @desc Check if the input is string
- * @param {any} input
+ * @desc Check if the input is string
+ * @param {any} input
*/
-export function isString(input: any) {
- return typeof input === 'string';
-}
-/**
-* @desc Encode string with base64 format
-* @param {string} message plain-text message
-* @return {string} base64 encoded string
-*/
-function base64Encode(message: string | number[]) {
- return Buffer.from(message as string).toString(BASE64_STR);
-}
-/**
-* @desc Decode string from base64 format
-* @param {string} base64Message encoded string
-* @param {boolean} isBytes determine the return value type (True: bytes False: string)
-* @return {bytes/string} decoded bytes/string depends on isBytes, default is {string}
-*/
-export function base64Decode(base64Message: string, isBytes?: boolean): string | Buffer {
- const bytes = Buffer.from(base64Message, BASE64_STR);
- return Boolean(isBytes) ? bytes : bytes.toString();
+export function isString(input: any): input is string {
+ return typeof input === 'string';
}
/**
-* @desc Compress the string
-* @param {string} message
-* @return {string} compressed string
-*/
-function deflateString(message: string): number[] {
- const input = Array.prototype.map.call(message, char => char.charCodeAt(0));
- return Array.from(deflate(input, { raw: true }));
+ * @desc Check if the input is an array of arrays
+ * @param {T[] | T[][]} input
+ */
+export function isArrayOfArrays(arr: T[] | T[][]): arr is T[][] {
+ return (arr as T[][]).every(Array.isArray);
}
/**
-* @desc Decompress the compressed string
-* @param {string} compressedString
-* @return {string} decompressed string
-*/
-export function inflateString(compressedString: string): string {
- const inputBuffer = Buffer.from(compressedString, BASE64_STR);
- const input = Array.prototype.map.call(inputBuffer.toString('binary'), char => char.charCodeAt(0));
- return Array.from(inflate(input, { raw: true }))
- .map(byte => String.fromCharCode(byte))
- .join('');
-}
-/**
-* @desc Abstract the normalizeCerString and normalizePemString
-* @param {buffer} File stream or string
-* @param {string} String for header and tail
-* @return {string} A formatted certificate string
-*/
-function _normalizeCerString(bin: string | Buffer, format: string) {
- return bin.toString().replace(/\n/g, '').replace(/\r/g, '').replace(`-----BEGIN ${format}-----`, '').replace(`-----END ${format}-----`, '').replace(/ /g, '');
+ * Create a buffer or return current buffer
+ * @param {string|Buffer} buf
+ */
+export function bufferFromIfNeeded(buf: string | Buffer): Buffer {
+ return Buffer.isBuffer(buf) ? buf : Buffer.from(buf);
}
/**
-* @desc Parse the .cer to string format without line break, header and footer
-* @param {string} certString declares the certificate contents
-* @return {string} certificiate in string format
-*/
-function normalizeCerString(certString: string | Buffer) {
- return _normalizeCerString(certString, 'CERTIFICATE');
+ * @desc Encode string with base64 format
+ * @param {string} message plain-text message
+ * @return {string} base64 encoded string
+ */
+export function base64Encode(message: string | number[]) {
+ return Buffer.from(message as string).toString(BASE64_STR);
}
/**
-* @desc Normalize the string in .pem format without line break, header and footer
-* @param {string} pemString
-* @return {string} private key in string format
-*/
-function normalizePemString(pemString: string | Buffer) {
- return _normalizeCerString(pemString.toString(), 'RSA PRIVATE KEY');
+ * @desc Decode string from base64 format
+ * @param {string} base64Message encoded string
+ * @param {boolean} isBytes determine the return value type (True: bytes False: string)
+ * @return {bytes/string} decoded bytes/string depends on isBytes, default is {string}
+ */
+export function base64Decode(base64Message: string, isBytes?: boolean): string | Buffer {
+ const bytes = Buffer.from(base64Message, BASE64_STR);
+ return isBytes ? bytes : bytes.toString();
}
/**
-* @desc Return the complete URL
-* @param {object} req HTTP request
-* @return {string} URL
-*/
-function getFullURL(req) {
- return `${req.protocol}://${req.get('host')}${req.originalUrl}`;
+ * @desc Compress the string
+ * @param {string} message
+ * @return {string} compressed string
+ */
+export function deflateString(message: string): number[] {
+ const input = Array.prototype.map.call(message, (char: string) => char.charCodeAt(0)) as number[];
+ return Array.from(deflate(input, { raw: true }));
}
/**
-* @desc Parse input string, return default value if it is undefined
-* @param {string/boolean}
-* @return {boolean}
-*/
-function parseString(str, defaultValue = '') {
- return str || defaultValue;
+ * @desc Decompress the compressed string
+ * @param {string} compressedString
+ * @return {string} decompressed string
+ */
+export function inflateString(compressedString: string): string {
+ const inputBuffer = Buffer.from(compressedString, BASE64_STR);
+ const input = Array.prototype.map.call(inputBuffer.toString('binary'), (char: string) =>
+ char.charCodeAt(0)
+ ) as number[];
+ return Array.from(inflate(input, { raw: true }))
+ .map((byte) => String.fromCharCode(byte))
+ .join('');
+}
+/**
+ * @desc Abstract the normalizeCerString and normalizePemString
+ * @param {buffer} File stream or string
+ * @param {string} String for header and tail
+ * @return {string} A formatted certificate string
+ */
+function _normalizeCerString(bin: string | Buffer, format: string) {
+ return bin
+ .toString()
+ .replace(/\n/g, '')
+ .replace(/\r/g, '')
+ .replace(`-----BEGIN ${format}-----`, '')
+ .replace(`-----END ${format}-----`, '')
+ .replace(/ /g, '')
+ .trim();
+}
+/**
+ * @desc Parse the .cer to string format without line break, header and footer
+ * @param {string} certString declares the certificate contents
+ * @return {string} certificiate in string format
+ */
+export function normalizeCerString(certString: string | Buffer) {
+ return _normalizeCerString(certString, 'CERTIFICATE');
}
/**
-* @desc Override the object by another object (rtl)
-* @param {object} default object
-* @param {object} object applied to the default object
-* @return {object} result object
-*/
-function applyDefault(obj1, obj2) {
- return Object.assign({}, obj1, obj2);
+ * @desc Normalize the string in .pem format without line break, header and footer
+ * @param {string} pemString
+ * @return {string} private key in string format
+ */
+export function normalizePemString(pemString: string | Buffer) {
+ return _normalizeCerString(pemString.toString(), 'RSA PRIVATE KEY');
}
/**
-* @desc Get public key in pem format from the certificate included in the metadata
-* @param {string} x509 certificate
-* @return {string} public key fetched from the certificate
-*/
-function getPublicKeyPemFromCertificate(x509Certificate: string) {
- const certDerBytes = util.decode64(x509Certificate);
- const obj = asn1.fromDer(certDerBytes);
- const cert = pki.certificateFromAsn1(obj);
- return pki.publicKeyToPem(cert.publicKey);
+ * @desc Get public key in pem format from the certificate included in the metadata
+ * @param {string | Buffer} x509 certificate
+ * @return {string} public key fetched from the certificate
+ */
+export function getPublicKeyPemFromCertificate(x509Certificate: string | Buffer) {
+ const certDerBytes = util.decode64(x509Certificate.toString());
+ const obj = asn1.fromDer(certDerBytes);
+ const cert = pki.certificateFromAsn1(obj);
+ return pki.publicKeyToPem(cert.publicKey);
}
/**
-* @desc Read private key from pem-formatted string
-* @param {string | Buffer} keyString pem-formattted string
-* @param {string} protected passphrase of the key
-* @return {string} string in pem format
-* If passphrase is used to protect the .pem content (recommend)
-*/
-export function readPrivateKey(keyString: string | Buffer, passphrase: string | undefined, isOutputString?: boolean) {
- return isString(passphrase) ? this.convertToString(pki.privateKeyToPem(pki.decryptRsaPrivateKey(String(keyString), passphrase)), isOutputString) : keyString;
+ * @desc Inline syntax sugar
+ */
+function toStringMaybe(input: T, yesOrNo: boolean): string | T {
+ return yesOrNo ? String(input) : input;
}
/**
-* @desc Inline syntax sugar
-*/
-function convertToString(input, isOutputString) {
- return Boolean(isOutputString) ? String(input) : input;
+ * @desc Read private key from pem-formatted string
+ * @param {string | Buffer} keyString pem-formattted string
+ * @param {string} protected passphrase of the key
+ * @return {string} string in pem format
+ * If passphrase is used to protect the .pem content (recommend)
+ */
+export function readPrivateKey(keyString: string | Buffer, passphrase: string | undefined, returnString = false) {
+ return isString(passphrase)
+ ? toStringMaybe(pki.privateKeyToPem(pki.decryptRsaPrivateKey(keyString.toString(), passphrase)), returnString)
+ : keyString;
}
/**
* @desc Check if the input is an array with non-zero size
*/
-export function isNonEmptyArray(a) {
- return Array.isArray(a) && a.length > 0;
+export function isNonEmptyArray(a?: T | null): a is T {
+ return Array.isArray(a) && a.length > 0;
}
export function notEmpty(value: TValue | null | undefined): value is TValue {
- return value !== null && value !== undefined;
+ return value !== null && value !== undefined;
}
-
-const utility = {
- isString,
- base64Encode,
- base64Decode,
- deflateString,
- inflateString,
- normalizeCerString,
- normalizePemString,
- getFullURL,
- parseString,
- applyDefault,
- getPublicKeyPemFromCertificate,
- readPrivateKey,
- convertToString,
- isNonEmptyArray,
-};
-
-export default utility;
diff --git a/src/validator.ts b/src/validator.ts
index c82f86c8..6e81236d 100644
--- a/src/validator.ts
+++ b/src/validator.ts
@@ -1,44 +1,35 @@
// unit is ms
type DriftTolerance = [number, number];
-function verifyTime(
- utcNotBefore: string | undefined,
- utcNotOnOrAfter: string | undefined,
- drift: DriftTolerance = [0, 0]
+export function verifyTime(
+ utcNotBefore: string | undefined,
+ utcNotOnOrAfter: string | undefined,
+ drift: DriftTolerance = [0, 0]
): boolean {
+ const now = new Date();
- const now = new Date();
+ if (!utcNotBefore && !utcNotOnOrAfter) {
+ // show warning because user intends to have time check but the document doesn't include corresponding information
+ console.warn("You intend to have time validation however the document doesn't include the valid range.");
+ return true;
+ }
- if (!utcNotBefore && !utcNotOnOrAfter) {
- // show warning because user intends to have time check but the document doesn't include corresponding information
- console.warn('You intend to have time validation however the document doesn\'t include the valid range.');
- return true;
- }
+ let notBeforeLocal: Date | null = null;
+ let notOnOrAfterLocal: Date | null = null;
- let notBeforeLocal: Date | null = null;
- let notOnOrAfterLocal: Date | null = null;
+ const [notBeforeDrift, notOnOrAfterDrift] = drift;
- const [notBeforeDrift, notOnOrAfterDrift] = drift;
+ if (utcNotBefore && !utcNotOnOrAfter) {
+ notBeforeLocal = new Date(utcNotBefore);
+ return +notBeforeLocal + notBeforeDrift <= +now;
+ }
+ if (!utcNotBefore && utcNotOnOrAfter) {
+ notOnOrAfterLocal = new Date(utcNotOnOrAfter);
+ return +now < +notOnOrAfterLocal + notOnOrAfterDrift;
+ }
- if (utcNotBefore && !utcNotOnOrAfter) {
- notBeforeLocal = new Date(utcNotBefore);
- return +notBeforeLocal + notBeforeDrift <= +now;
- }
- if (!utcNotBefore && utcNotOnOrAfter) {
- notOnOrAfterLocal = new Date(utcNotOnOrAfter);
- return +now < +notOnOrAfterLocal + notOnOrAfterDrift;
- }
-
- notBeforeLocal = new Date(utcNotBefore!);
- notOnOrAfterLocal = new Date(utcNotOnOrAfter!);
-
- return (
- +notBeforeLocal + notBeforeDrift <= +now &&
- +now < +notOnOrAfterLocal + notOnOrAfterDrift
- );
+ notBeforeLocal = utcNotBefore ? new Date(utcNotBefore) : new Date();
+ notOnOrAfterLocal = utcNotOnOrAfter ? new Date(utcNotOnOrAfter) : new Date();
+ return +notBeforeLocal + notBeforeDrift <= +now && +now < +notOnOrAfterLocal + notOnOrAfterDrift;
}
-
-export {
- verifyTime
-};
\ No newline at end of file
diff --git a/test/README.md b/test/README.md
index d848ee75..e768db2b 100644
--- a/test/README.md
+++ b/test/README.md
@@ -5,47 +5,47 @@
var samlify = require('./build/index');
var fs = require('fs');
var idpconfig = {
- privateKey: fs.readFileSync('./test/key/idp/privkey.pem'),
- privateKeyPass: 'q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW',
- isAssertionEncrypted: false,
- metadata: fs.readFileSync('./test/misc/idpmeta_rollingcert.xml')
+ privateKey: fs.readFileSync('./test/key/idp/privkey.pem'),
+ privateKeyPass: 'q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW',
+ isAssertionEncrypted: false,
+ metadata: fs.readFileSync('./test/misc/idpmeta_rollingcert.xml'),
};
var idp = samlify.IdentityProvider(idpconfig);
samlify.Extractor.extract(idp.entityMeta.xmlString, [
- {
- key: 'certificate',
- localPath: ['EntityDescriptor', '~SSODescriptor', 'KeyDescriptor'],
- index: ['use'],
- attributePath: ['KeyInfo', 'X509Data', 'X509Certificate'],
- attributes: []
- }
-])
+ {
+ key: 'certificate',
+ localPath: ['EntityDescriptor', '~SSODescriptor', 'KeyDescriptor'],
+ index: ['use'],
+ attributePath: ['KeyInfo', 'X509Data', 'X509Certificate'],
+ attributes: [],
+ },
+]);
// construct response signature
-const {
- IdPMetadata: idpMetadata,
- Utility: utility,
- SamlLib: libsaml,
-} = require('./');
+const { IdPMetadata: idpMetadata, Utility: utility, SamlLib: libsaml } = require('./');
const fs = require('fs');
const metadata = idpMetadata(fs.readFileSync('./test/misc/idpmeta_rollingcert.xml'));
const _idpKeyFolder = './test/key/idp/';
const _idpPrivPem1 = String(fs.readFileSync(_idpKeyFolder + 'privkey.pem'));
const _idpPrivPem2 = String(fs.readFileSync(_idpKeyFolder + 'privkey2.pem'));
function writer(str) {
- fs.writeFileSync('nogit.xml', str);
+ fs.writeFileSync('nogit.xml', str);
}
-writer(utility.base64Decode(libsaml.constructSAMLSignature({
- rawSamlMessage: String(fs.readFileSync('./test/misc/response.xml')),
- referenceTagXPath: libsaml.createXPath('Issuer'),
- signingCert: metadata.getX509Certificate('signing')[0],
- privateKey: _idpPrivPem1,
- privateKeyPass: 'q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW',
- signatureAlgorithm: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
- signatureConfig: {
- prefix: 'ds',
- location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Issuer']", action: 'after' },
- },
-})));
-```
\ No newline at end of file
+writer(
+ utility.base64Decode(
+ libsaml.constructSAMLSignature({
+ rawSamlMessage: String(fs.readFileSync('./test/misc/response.xml')),
+ referenceTagXPath: libsaml.createXPath('Issuer'),
+ signingCert: metadata.getX509Certificate('signing')[0],
+ privateKey: _idpPrivPem1,
+ privateKeyPass: 'q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW',
+ signatureAlgorithm: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
+ signatureConfig: {
+ prefix: 'ds',
+ location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Issuer']", action: 'after' },
+ },
+ })
+ )
+);
+```
diff --git a/test/extractor.ts b/test/extractor.ts
index 08584ffd..62cf1bb7 100644
--- a/test/extractor.ts
+++ b/test/extractor.ts
@@ -1,130 +1,133 @@
+/* eslint-disable @typescript-eslint/no-unsafe-call */
// This test file includes all the units related to the extractor
import test from 'ava';
import { readFileSync } from 'fs';
import { extract } from '../src/extractor';
-const _decodedResponse: string = String(readFileSync('./test/misc/response_signed.xml'));
-const _spmeta: string = String(readFileSync('./test/misc/spmeta.xml'));
+const _decodedResponse = String(readFileSync('./test/misc/response_signed.xml'));
+const _spmeta = String(readFileSync('./test/misc/spmeta.xml'));
(() => {
+ test('fetch multiple attributes', (t) => {
+ const result = extract(_decodedResponse, [
+ {
+ key: 'response',
+ localPath: ['Response'],
+ attributes: ['ID', 'Destination'],
+ },
+ ]);
+ t.is(result.response.id, '_8e8dc5f69a98cc4c1ff3427e5ce34606fd672f91e6');
+ t.is(result.response.destination, 'http://sp.example.com/demo1/index.php?acs');
+ });
- test('fetch multiple attributes', t => {
- const result = extract(_decodedResponse, [
- {
- key: 'response',
- localPath: ['Response'],
- attributes: ['ID', 'Destination']
- }
- ]);
- t.is(result.response.id, '_8e8dc5f69a98cc4c1ff3427e5ce34606fd672f91e6');
- t.is(result.response.destination, 'http://sp.example.com/demo1/index.php?acs');
- });
+ test('fetch single attributes', (t) => {
+ const result = extract(_decodedResponse, [
+ {
+ key: 'statusCode',
+ localPath: ['Response', 'Status', 'StatusCode'],
+ attributes: ['Value'],
+ },
+ ]);
+ t.is(result.statusCode, 'urn:oasis:names:tc:SAML:2.0:status:Success');
+ });
- test('fetch single attributes', t => {
- const result = extract(_decodedResponse, [
- {
- key: 'statusCode',
- localPath: ['Response', 'Status', 'StatusCode'],
- attributes: ['Value'],
- }
- ]);
- t.is(result.statusCode, 'urn:oasis:names:tc:SAML:2.0:status:Success');
- });
+ test('fetch the inner context of leaf node', (t) => {
+ const result = extract(_decodedResponse, [
+ {
+ key: 'audience',
+ localPath: ['Response', 'Assertion', 'Conditions', 'AudienceRestriction', 'Audience'],
+ attributes: [],
+ },
+ ]);
+ t.is(result.audience, 'https://sp.example.com/metadata');
+ });
- test('fetch the inner context of leaf node', t => {
- const result = extract(_decodedResponse, [
- {
- key: 'audience',
- localPath: ['Response', 'Assertion', 'Conditions', 'AudienceRestriction', 'Audience'],
- attributes: []
- }
- ]);
- t.is(result.audience, 'https://sp.example.com/metadata');
- });
+ test('fetch the entire context of a non-existing node ', (t) => {
+ const result = extract(_decodedResponse, [
+ {
+ key: 'assertionSignature',
+ localPath: ['Response', 'Assertion', 'Signature'],
+ attributes: [],
+ context: true,
+ },
+ ]);
+ t.is(result.assertionSignature, null);
+ });
- test('fetch the entire context of a non-existing node ', t => {
- const result = extract(_decodedResponse, [
- {
- key: 'assertionSignature',
- localPath: ['Response', 'Assertion', 'Signature'],
- attributes: [],
- context: true
- }
- ]);
- t.is(result.assertionSignature, null);
- });
+ test('fetch the entire context of an existed node', (t) => {
+ const result = extract(_decodedResponse, [
+ {
+ key: 'messageSignature',
+ localPath: ['Response', 'Signature'],
+ attributes: [],
+ context: true,
+ },
+ ]);
+ t.not(result.messageSignature, null);
+ });
- test('fetch the entire context of an existed node', t => {
- const result = extract(_decodedResponse, [
- {
- key: 'messageSignature',
- localPath: ['Response', 'Signature'],
- attributes: [],
- context: true
- }
- ]);
- t.not(result.messageSignature, null);
- });
+ test('fetch the unique inner context of multiple nodes', (t) => {
+ const result = extract(_decodedResponse, [
+ {
+ key: 'issuer',
+ localPath: [
+ ['Response', 'Issuer'],
+ ['Response', 'Assertion', 'Issuer'],
+ ],
+ attributes: [],
+ },
+ ]);
+ t.is(result.issuer.length, 1);
+ t.is(
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+ result.issuer.every((i: any) => i === 'https://idp.example.com/metadata'),
+ true
+ );
+ });
- test('fetch the unique inner context of multiple nodes', t => {
- const result = extract(_decodedResponse, [
- {
- key: 'issuer',
- localPath: [
- ['Response', 'Issuer'],
- ['Response', 'Assertion', 'Issuer']
- ],
- attributes: []
- }
- ]);
- t.is(result.issuer.length, 1);
- t.is(result.issuer.every(i => i === 'https://idp.example.com/metadata'), true);
- });
+ test('fetch the attribute with wildcard local path', (t) => {
+ const result = extract(_spmeta, [
+ {
+ key: 'certificate',
+ localPath: ['EntityDescriptor', '~SSODescriptor', 'KeyDescriptor'],
+ index: ['use'],
+ attributePath: ['KeyInfo', 'X509Data', 'X509Certificate'],
+ attributes: [],
+ },
+ ]);
+ t.not(result.certificate.signing, null);
+ t.not(result.certificate.encryption, null);
+ });
- test('fetch the attribute with wildcard local path', t => {
- const result = extract(_spmeta, [
- {
- key: 'certificate',
- localPath: ['EntityDescriptor', '~SSODescriptor', 'KeyDescriptor'],
- index: ['use'],
- attributePath: ['KeyInfo', 'X509Data', 'X509Certificate'],
- attributes: []
- }
- ]);
- t.not(result.certificate.signing, null);
- t.not(result.certificate.encryption, null);
- });
+ test('fetch the attribute with non-wildcard local path', (t) => {
+ const result = extract(_decodedResponse, [
+ {
+ key: 'attributes',
+ localPath: ['Response', 'Assertion', 'AttributeStatement', 'Attribute'],
+ index: ['Name'],
+ attributePath: ['AttributeValue'],
+ attributes: [],
+ },
+ ]);
+ t.is(result.attributes.uid, 'test');
+ t.is(result.attributes.mail, 'test@example.com');
+ t.is(result.attributes.eduPersonAffiliation.length, 2);
+ });
- test('fetch the attribute with non-wildcard local path', t => {
- const result = extract(_decodedResponse, [
- {
- key: 'attributes',
- localPath: ['Response', 'Assertion', 'AttributeStatement', 'Attribute'],
- index: ['Name'],
- attributePath: ['AttributeValue'],
- attributes: []
- }
- ]);
- t.is(result.attributes.uid, 'test');
- t.is(result.attributes.mail, 'test@example.com');
- t.is(result.attributes.eduPersonAffiliation.length, 2);
- });
-
- test('fetch with one attribute as key, another as value', t => {
- const result = extract(_spmeta, [
- {
- key: 'singleSignOnService',
- localPath: ['EntityDescriptor', '~SSODescriptor', 'AssertionConsumerService'],
- index: ['Binding'],
- attributePath: [],
- attributes: ['Location']
- }
- ]);
- const postEndpoint = result.singleSignOnService['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'];
- const artifactEndpoint = result.singleSignOnService['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact'];
-
- t.is(postEndpoint, 'https://sp.example.org/sp/sso');
- t.is(artifactEndpoint, 'https://sp.example.org/sp/sso');
- });
+ test('fetch with one attribute as key, another as value', (t) => {
+ const result = extract(_spmeta, [
+ {
+ key: 'singleSignOnService',
+ localPath: ['EntityDescriptor', '~SSODescriptor', 'AssertionConsumerService'],
+ index: ['Binding'],
+ attributePath: [],
+ attributes: ['Location'],
+ },
+ ]);
+ const postEndpoint = result.singleSignOnService['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'];
+ const artifactEndpoint = result.singleSignOnService['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact'];
+ t.is(postEndpoint, 'https://sp.example.org/sp/sso');
+ t.is(artifactEndpoint, 'https://sp.example.org/sp/sso');
+ });
})();
diff --git a/test/flow.ts b/test/flow.ts
index 70cfc55b..2edf7369 100644
--- a/test/flow.ts
+++ b/test/flow.ts
@@ -1,13 +1,18 @@
-import esaml2 = require('../index');
-import { readFileSync, writeFileSync } from 'fs';
+/* eslint-disable @typescript-eslint/no-unsafe-call */
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore
+import * as validator from '@authenio/samlify-xsd-schema-validator';
import test from 'ava';
-import { PostBindingContext } from '../src/entity';
-import * as uuid from 'uuid';
-import * as url from 'url';
-import util from '../src/utility';
-import * as tk from 'timekeeper';
+import { readFileSync } from 'fs';
+import tk from 'timekeeper';
+import { v4 as uuid } from 'uuid';
+import { identityProvider, libsaml, serviceProvider, setSchemaValidator } from '../src';
+import type { IdentityProvider } from '../src/entity-idp';
+import type { ServiceProvider } from '../src/entity-sp';
+import { isSamlifyError, SamlifyErrorCode } from '../src/error';
+import { BindingNamespace, MessageSignatureOrder, names, wording } from '../src/urn';
+import { base64Decode, base64Encode, isString } from '../src/utility';
-import * as validator from '@authenio/samlify-xsd-schema-validator';
// import * as validator from '@authenio/samlify-validate-with-xmllint';
// import * as validator from '@authenio/samlify-node-xmllint';
// import * as validator from '@authenio/samlify-libxml-xsd';
@@ -17,707 +22,954 @@ import * as validator from '@authenio/samlify-xsd-schema-validator';
// const validator = require('@authenio/samlify-node-xmllint');
// const validator = require('@authenio/samlify-libxml-xsd');
-esaml2.setSchemaValidator(validator);
-
-const isString = util.isString;
-
-const {
- IdentityProvider: identityProvider,
- ServiceProvider: serviceProvider,
- Utility: utility,
- SamlLib: libsaml,
- Constants: ref,
-} = esaml2;
-
-const binding = ref.namespace.binding;
+setSchemaValidator(validator);
// Custom template
const loginResponseTemplate = {
- context: '{Issuer}{Issuer}{NameID}{Audience}{AttributeStatement}',
- attributes: [
- { name: 'mail', valueTag: 'user.email', nameFormat: 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic', valueXsiType: 'xs:string' },
- { name: 'name', valueTag: 'user.name', nameFormat: 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic', valueXsiType: 'xs:string' },
- ],
+ context:
+ '{Issuer}{Issuer}{NameID}{Audience}{AttributeStatement}',
+ attributes: [
+ {
+ name: 'mail',
+ valueTag: 'user.email',
+ nameFormat: names.attrnameFormat.basic,
+ valueXsiType: 'xs:string',
+ },
+ {
+ name: 'name',
+ valueTag: 'user.name',
+ nameFormat: names.attrnameFormat.basic,
+ valueXsiType: 'xs:string',
+ },
+ ],
};
-const failedResponse: string = String(readFileSync('./test/misc/failed_response.xml'));
-
-const createTemplateCallback = (_idp, _sp, user) => template => {
- const _id = '_8e8dc5f69a98cc4c1ff3427e5ce34606fd672f91e6';
- const now = new Date();
- const spEntityID = _sp.entityMeta.getEntityID();
- const idpSetting = _idp.entitySetting;
- const fiveMinutesLater = new Date(now.getTime());
- fiveMinutesLater.setMinutes(fiveMinutesLater.getMinutes() + 5);
- const tvalue = {
- ID: _id,
- AssertionID: idpSetting.generateID ? idpSetting.generateID() : `${uuid.v4()}`,
- Destination: _sp.entityMeta.getAssertionConsumerService(binding.post),
- Audience: spEntityID,
- SubjectRecipient: spEntityID,
- NameIDFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
- NameID: user.email,
- Issuer: idp.entityMeta.getEntityID(),
- IssueInstant: now.toISOString(),
- ConditionsNotBefore: now.toISOString(),
- ConditionsNotOnOrAfter: fiveMinutesLater.toISOString(),
- SubjectConfirmationDataNotOnOrAfter: fiveMinutesLater.toISOString(),
- AssertionConsumerServiceURL: _sp.entityMeta.getAssertionConsumerService(binding.post),
- EntityID: spEntityID,
- InResponseTo: '_4606cc1f427fa981e6ffd653ee8d6972fc5ce398c4',
- StatusCode: 'urn:oasis:names:tc:SAML:2.0:status:Success',
- attrUserEmail: 'myemailassociatedwithsp@sp.com',
- attrUserName: 'mynameinsp',
- };
- return {
- id: _id,
- context: libsaml.replaceTagsByValue(template, tvalue),
- };
+const failedResponse = String(readFileSync('./test/misc/failed_response.xml'));
+
+const createTemplateCallback = (
+ _idp?: IdentityProvider,
+ _sp?: ServiceProvider,
+ user?: Record,
+ requestInfo?: RequestInfo
+) => (template: string, values: Record) => {
+ const _id = '_8e8dc5f69a98cc4c1ff3427e5ce34606fd672f91e6';
+ const now = new Date();
+ const spEntityID = _sp?.getEntityMeta().getEntityID();
+ const idpSetting = _idp?.getEntitySettings();
+ const fiveMinutesLater = new Date(now.getTime());
+ fiveMinutesLater.setMinutes(fiveMinutesLater.getMinutes() + 5);
+ const newValues = {
+ ...values,
+ ID: _id,
+ AssertionID: idpSetting?.generateID ? idpSetting.generateID() : `${uuid()}`,
+ // Destination: _sp?.entityMeta.getAssertionConsumerService(BindingNamespace.Post),
+ Audience: spEntityID,
+ SubjectRecipient: spEntityID,
+ NameIDFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
+ NameID: user?.email,
+ // Issuer: _idp?.entityMeta.getEntityID(),
+ IssueInstant: now.toISOString(),
+ ConditionsNotBefore: now.toISOString(),
+ ConditionsNotOnOrAfter: fiveMinutesLater.toISOString(),
+ SubjectConfirmationDataNotOnOrAfter: fiveMinutesLater.toISOString(),
+ AssertionConsumerServiceURL: _sp?.getEntityMeta().getAssertionConsumerService(BindingNamespace.Post),
+ EntityID: spEntityID,
+ InResponseTo: requestInfo?.extract.request.id ?? '_4606cc1f427fa981e6ffd653ee8d6972fc5ce398c4',
+ StatusCode: 'urn:oasis:names:tc:SAML:2.0:status:Success',
+ attrUserEmail: 'myemailassociatedwithsp@sp.com',
+ attrUserName: 'mynameinsp',
+ };
+ return [libsaml.replaceTagsByValue(template, newValues), newValues] as [string, Record];
};
// Define of metadata
const defaultIdpConfig = {
- privateKey: readFileSync('./test/key/idp/privkey.pem'),
- privateKeyPass: 'q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW',
- isAssertionEncrypted: true,
- encPrivateKey: readFileSync('./test/key/idp/encryptKey.pem'),
- encPrivateKeyPass: 'g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN',
- metadata: readFileSync('./test/misc/idpmeta.xml'),
+ privateKey: readFileSync('./test/key/idp/privkey.pem'),
+ privateKeyPass: 'q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW',
+ isAssertionEncrypted: true,
+ encPrivateKey: readFileSync('./test/key/idp/encryptKey.pem'),
+ encPrivateKeyPass: 'g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN',
+ metadata: readFileSync('./test/misc/idpmeta.xml'),
};
const oneloginIdpConfig = {
- privateKey: readFileSync('./test/key/idp/privkey.pem'),
- privateKeyPass: 'q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW',
- isAssertionEncrypted: true,
- encPrivateKey: readFileSync('./test/key/idp/encryptKey.pem'),
- encPrivateKeyPass: 'g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN',
- metadata: readFileSync('./test/misc/idpmeta_onelogoutservice.xml'),
+ privateKey: readFileSync('./test/key/idp/privkey.pem'),
+ privateKeyPass: 'q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW',
+ isAssertionEncrypted: true,
+ encPrivateKey: readFileSync('./test/key/idp/encryptKey.pem'),
+ encPrivateKeyPass: 'g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN',
+ metadata: readFileSync('./test/misc/idpmeta_onelogoutservice.xml'),
};
const defaultSpConfig = {
- privateKey: readFileSync('./test/key/sp/privkey.pem'),
- privateKeyPass: 'VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px',
- isAssertionEncrypted: true, // for logout purpose
- encPrivateKey: readFileSync('./test/key/sp/encryptKey.pem'),
- encPrivateKeyPass: 'BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU',
- metadata: readFileSync('./test/misc/spmeta.xml'),
+ privateKey: readFileSync('./test/key/sp/privkey.pem'),
+ privateKeyPass: 'VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px',
+ isAssertionEncrypted: true, // for logout purpose
+ encPrivateKey: readFileSync('./test/key/sp/encryptKey.pem'),
+ encPrivateKeyPass: 'BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU',
+ metadata: readFileSync('./test/misc/spmeta.xml'),
};
const noSignedIdpMetadata = readFileSync('./test/misc/idpmeta_nosign.xml').toString().trim();
const spmetaNoAssertSign = readFileSync('./test/misc/spmeta_noassertsign.xml').toString().trim();
-const sampleRequestInfo = { extract: { request: { id: 'request_id' } } };
+const sampleRequestInfo = { extract: { request: { id: 'request_id' } } } as const;
+type RequestInfo = { extract: { request: { id: string } } };
// Define entities
const idp = identityProvider(defaultIdpConfig);
const sp = serviceProvider(defaultSpConfig);
const idpNoEncrypt = identityProvider({ ...defaultIdpConfig, isAssertionEncrypted: false });
-const idpcustomNoEncrypt = identityProvider({ ...defaultIdpConfig, isAssertionEncrypted: false, loginResponseTemplate });
+const idpcustomNoEncrypt = identityProvider({
+ ...defaultIdpConfig,
+ isAssertionEncrypted: false,
+ loginResponseTemplate,
+});
const idpcustom = identityProvider({ ...defaultIdpConfig, loginResponseTemplate });
-const idpEncryptThenSign = identityProvider({ ...defaultIdpConfig, messageSigningOrder: 'encrypt-then-sign' });
+const idpEncryptThenSign = identityProvider({ ...defaultIdpConfig, messageSigningOrder: MessageSignatureOrder.ETS });
const spWantLogoutReqSign = serviceProvider({ ...defaultSpConfig, wantLogoutRequestSigned: true });
const idpWantLogoutResSign = identityProvider({ ...defaultIdpConfig, wantLogoutResponseSigned: true });
const spNoAssertSign = serviceProvider({ ...defaultSpConfig, metadata: spmetaNoAssertSign });
-const spNoAssertSignCustomConfig = serviceProvider({ ...defaultSpConfig,
- metadata: spmetaNoAssertSign,
- signatureConfig: {
- prefix: 'ds',
- location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Issuer']", action: 'after' },
- },
+const spNoAssertSignCustomConfig = serviceProvider({
+ ...defaultSpConfig,
+ metadata: spmetaNoAssertSign,
+ signatureConfig: {
+ prefix: 'ds',
+ location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Issuer']", action: 'after' },
+ },
});
const spWithClockDrift = serviceProvider({ ...defaultSpConfig, clockDrifts: [-2000, 2000] });
-function writer(str) {
- writeFileSync('test.txt', str);
-}
-
-test('create login request with redirect binding using default template and parse it', async t => {
- const { id, context } = sp.createLoginRequest(idp, 'redirect');
- t.is(typeof id, 'string');
- t.is(typeof context, 'string');
- const originalURL = url.parse(context, true);
- const SAMLRequest = originalURL.query.SAMLRequest;
- const Signature = originalURL.query.Signature;
- const SigAlg = originalURL.query.SigAlg;
- delete originalURL.query.Signature;
- const octetString = Object.keys(originalURL.query).map(q => q + '=' + encodeURIComponent(originalURL.query[q] as string)).join('&');
- const { samlContent, extract } = await idp.parseLoginRequest(sp, 'redirect', { query: { SAMLRequest, Signature, SigAlg }, octetString});
- t.is(extract.issuer, 'https://sp.example.org/metadata');
- t.is(typeof extract.request.id, 'string');
- t.is(extract.nameIDPolicy.format, 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress');
- t.is(extract.nameIDPolicy.allowCreate, 'false');
-});
-
-test('create login request with post binding using default template and parse it', async t => {
- const { relayState, type, entityEndpoint, id, context: SAMLRequest } = sp.createLoginRequest(idp, 'post') as PostBindingContext;
- t.is(typeof id, 'string');
- t.is(typeof SAMLRequest, 'string');
- t.is(typeof entityEndpoint, 'string');
- t.is(type, 'SAMLRequest');
- const { extract } = await idp.parseLoginRequest(sp, 'post', { body: { SAMLRequest }});
- t.is(extract.issuer, 'https://sp.example.org/metadata');
- t.is(typeof extract.request.id, 'string');
- t.is(extract.nameIDPolicy.format, 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress');
- t.is(extract.nameIDPolicy.allowCreate, 'false');
- t.is(typeof extract.signature, 'string');
-});
-
-test('signed in sp is not matched with the signed notation in idp with post request', t => {
- const _idp = identityProvider({ ...defaultIdpConfig, metadata: noSignedIdpMetadata });
- try {
- const { id, context } = sp.createLoginRequest(_idp, 'post');
- t.fail();
- } catch (e) {
- t.is(e.message, 'ERR_METADATA_CONFLICT_REQUEST_SIGNED_FLAG');
- }
-});
-
-test('signed in sp is not matched with the signed notation in idp with redirect request', t => {
- const _idp = identityProvider({ ...defaultIdpConfig, metadata: noSignedIdpMetadata });
- try {
- const { id, context } = sp.createLoginRequest(_idp, 'redirect');
- t.fail();
- } catch (e) {
- t.is(e.message, 'ERR_METADATA_CONFLICT_REQUEST_SIGNED_FLAG');
- }
-});
-
-test('create login request with redirect binding using [custom template]', t => {
- const _sp = serviceProvider({
- ...defaultSpConfig, loginRequestTemplate: {
- context: '{Issuer}',
- },
- });
- const { id, context } = _sp.createLoginRequest(idp, 'redirect', template => {
- return {
- id: 'exposed_testing_id',
- context: template, // all the tags are supposed to be replaced
- };
- });
- (id === 'exposed_testing_id' && isString(context)) ? t.pass() : t.fail();
-});
-
-test('create login request with post binding using [custom template]', t => {
- const _sp = serviceProvider({
- ...defaultSpConfig, loginRequestTemplate: {
- context: '{Issuer}',
- },
- });
- const { id, context, entityEndpoint, type, relayState } = _sp.createLoginRequest(idp, 'post', template => {
- return {
- id: 'exposed_testing_id',
- context: template, // all the tags are supposed to be replaced
- };
- }) as PostBindingContext;
- id === 'exposed_testing_id' &&
- isString(context) &&
- isString(relayState) &&
- isString(entityEndpoint) &&
- type === 'SAMLRequest'
- ? t.pass() : t.fail();
-});
-
-test('create login response with undefined binding', async t => {
- const user = { email: 'user@esaml2.com' };
- const error = await t.throwsAsync(() => idp.createLoginResponse(sp, {}, 'undefined', user, createTemplateCallback(idp, sp, user)));
- t.is(error.message, 'ERR_CREATE_RESPONSE_UNDEFINED_BINDING');
-});
-
-test('create post login response', async t => {
- const user = { email: 'user@esaml2.com' };
- const { id, context } = await idp.createLoginResponse(sp, sampleRequestInfo, 'post', user, createTemplateCallback(idp, sp, user));
- isString(id) && isString(context) ? t.pass() : t.fail();
-});
-
-test('create logout request with redirect binding', t => {
- const { id, context } = sp.createLogoutRequest(idp, 'redirect', { logoutNameID: 'user@esaml2' });
- isString(id) && isString(context) ? t.pass() : t.fail();
-});
-
-test('create logout request with post binding', t => {
- const { relayState, type, entityEndpoint, id, context } = sp.createLogoutRequest(idp, 'post', { logoutNameID: 'user@esaml2' }) as PostBindingContext;
- isString(id) && isString(context) && isString(entityEndpoint) && type === 'SAMLRequest' ? t.pass() : t.fail();
-});
-
-test('create logout request when idp only has one binding', t => {
- const testIdp = identityProvider(oneloginIdpConfig);
- const { id, context } = sp.createLogoutRequest(testIdp, 'redirect', { logoutNameID: 'user@esaml2' });
- isString(id) && isString(context) ? t.pass() : t.fail();
-});
-
-test('create logout response with undefined binding', t => {
- try {
- const { id, context } = idp.createLogoutResponse(sp, {}, 'undefined', '', createTemplateCallback(idp, sp, {}));
- t.fail();
- } catch (e) {
- t.is(e.message, 'ERR_CREATE_LOGOUT_RESPONSE_UNDEFINED_BINDING');
- }
-});
-
-test('create logout response with redirect binding', t => {
- const { id, context } = idp.createLogoutResponse(sp, {}, 'redirect', '', createTemplateCallback(idp, sp, {}));
- isString(id) && isString(context) ? t.pass() : t.fail();
-});
-
-test('create logout response with post binding', t => {
- const { relayState, type, entityEndpoint, id, context } = idp.createLogoutResponse(sp, {}, 'post', '', createTemplateCallback(idp, sp, {})) as PostBindingContext;
- isString(id) && isString(context) && isString(entityEndpoint) && type === 'SAMLResponse' ? t.pass() : t.fail();
+// function writer(str) {
+// writeFileSync('test.txt', str);
+// }
+
+test('create login request with redirect binding using default template and parse it', async (t) => {
+ const { id, context } = sp.createLoginRequest(idp, BindingNamespace.Redirect);
+ t.is(typeof id, 'string');
+ t.is(typeof context, 'string');
+ const url = new URL(`https://${context}`);
+ const SAMLRequest = url.searchParams.get(wording.urlParams.samlRequest);
+ const Signature = url.searchParams.get(wording.urlParams.signature);
+ const SigAlg = url.searchParams.get(wording.urlParams.sigAlg);
+ url.searchParams.delete('Signature');
+ const octetString = Array.from(url.searchParams.keys())
+ .map((q) => q + '=' + encodeURIComponent(url.searchParams.get(q) as string))
+ .join('&');
+ const { /*samlContent,*/ extract } = await idp.parseLoginRequest(sp, BindingNamespace.Redirect, {
+ query: { SAMLRequest, Signature, SigAlg },
+ octetString,
+ });
+ t.is(extract.issuer, 'https://sp.example.org/metadata');
+ t.is(typeof extract.request.id, 'string');
+ t.is(extract.nameIDPolicy.format, 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress');
+ t.is(extract.nameIDPolicy.allowCreate, 'false');
+});
+
+test('create login request with post binding using default template and parse it', async (t) => {
+ const result = sp.createLoginRequest(idp, BindingNamespace.Post);
+ t.is('entityEndpoint' in result, true);
+ if (!('entityEndpoint' in result)) return;
+ t.true(isString(result.id));
+ t.true(isString(result.context));
+ t.true(isString(result.entityEndpoint));
+ t.is(result.type, 'SAMLRequest');
+ const { extract } = await idp.parseLoginRequest(sp, BindingNamespace.Post, { body: { SAMLRequest: result.context } });
+ t.is(extract.issuer, 'https://sp.example.org/metadata');
+ t.is(typeof extract.request.id, 'string');
+ t.is(extract.nameIDPolicy.format, 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress');
+ t.is(extract.nameIDPolicy.allowCreate, 'false');
+ t.is(typeof extract.signature, 'string');
+});
+
+test('signed in sp is not matched with the signed notation in idp with post request', (t) => {
+ const _idp = identityProvider({ ...defaultIdpConfig, metadata: noSignedIdpMetadata });
+ try {
+ sp.createLoginRequest(_idp, BindingNamespace.Post);
+ t.fail();
+ } catch (e) {
+ t.is(isSamlifyError(e), true);
+ t.is(e.code, SamlifyErrorCode.MetadataConflictRequestSignedFlag);
+ }
+});
+
+test('signed in sp is not matched with the signed notation in idp with redirect request', (t) => {
+ const _idp = identityProvider({ ...defaultIdpConfig, metadata: noSignedIdpMetadata });
+ try {
+ sp.createLoginRequest(_idp, BindingNamespace.Redirect);
+ t.fail();
+ } catch (e) {
+ t.is(isSamlifyError(e), true);
+ t.is(e.code, SamlifyErrorCode.MetadataConflictRequestSignedFlag);
+ }
+});
+
+test('create login request with redirect binding using [custom template]', (t) => {
+ const _sp = serviceProvider({
+ ...defaultSpConfig,
+ loginRequestTemplate: {
+ context:
+ '{Issuer}',
+ },
+ });
+ const { context, id } = _sp.createLoginRequest(idp, BindingNamespace.Redirect, (template, values) => {
+ values.ID = 'exposed_testing_id';
+ return [
+ template,
+ values, // all the tags are supposed to be replaced
+ ] as const;
+ });
+ id === 'exposed_testing_id' && isString(context) ? t.pass() : t.fail();
+});
+
+test('create login request with post binding using [custom template]', (t) => {
+ const _sp = serviceProvider({
+ ...defaultSpConfig,
+ loginRequestTemplate: {
+ context:
+ '{Issuer}',
+ },
+ });
+ const result = _sp.createLoginRequest(idp, BindingNamespace.Post, (template, values) => {
+ values.ID = 'exposed_testing_id';
+ return [
+ template,
+ values, // all the tags are supposed to be replaced
+ ] as const;
+ });
+ t.is('entityEndpoint' in result, true);
+ if (!('entityEndpoint' in result)) return;
+ t.true(isString(result.id));
+ t.true(isString(result.context));
+ t.true(isString(result.relayState));
+ t.true(isString(result.entityEndpoint));
+ t.is(result.type, 'SAMLRequest');
+ t.is(result.id, 'exposed_testing_id');
+});
+
+test('create login response with undefined binding', async (t) => {
+ const user = { email: 'user@esaml2.com' };
+ const error = await t.throwsAsync(
+ () =>
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-expect-error
+ idp.createLoginResponse(sp, {}, 'undefined', user, createTemplateCallback(idp, sp, user)) // eslint-disable-line
+ );
+ t.is(isSamlifyError(error), true);
+ if (isSamlifyError(error)) t.is(error.code, SamlifyErrorCode.UnsupportedBinding);
+});
+
+test('create post login response', async (t) => {
+ const user = { email: 'user@esaml2.com' };
+ const { id, context } = await idp.createLoginResponse(
+ sp,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idp, sp, user)
+ );
+ isString(id) && isString(context) ? t.pass() : t.fail();
+});
+
+test('create logout request with redirect binding', (t) => {
+ const { id, context } = sp.createLogoutRequest(idp, BindingNamespace.Redirect, { logoutNameID: 'user@esaml2' });
+ isString(id) && isString(context) ? t.pass() : t.fail();
+});
+
+test('create logout request with post binding', (t) => {
+ const result = sp.createLogoutRequest(idp, BindingNamespace.Post, {
+ logoutNameID: 'user@esaml2',
+ });
+ t.is('entityEndpoint' in result, true);
+ if (!('entityEndpoint' in result)) return;
+ t.true(isString(result.id));
+ t.true(isString(result.context));
+ t.true(isString(result.relayState));
+ t.true(isString(result.entityEndpoint));
+ t.is(result.type, 'SAMLRequest');
+});
+
+test('create logout request when idp only has one binding', (t) => {
+ const testIdp = identityProvider(oneloginIdpConfig);
+ const { id, context } = sp.createLogoutRequest(testIdp, BindingNamespace.Redirect, { logoutNameID: 'user@esaml2' });
+ isString(id) && isString(context) ? t.pass() : t.fail();
+});
+
+test('create logout response with undefined binding', (t) => {
+ try {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-expect-error
+ idp.createLogoutResponse(sp, {}, 'undefined', '', createTemplateCallback(idp, sp, {}));
+ t.fail();
+ } catch (e) {
+ t.is(isSamlifyError(e), true);
+ t.is(e.code, SamlifyErrorCode.UnsupportedBinding);
+ }
+});
+
+test('create logout response with redirect binding', (t) => {
+ const { id, context } = idp.createLogoutResponse(
+ sp,
+ {},
+ BindingNamespace.Redirect,
+ '',
+ createTemplateCallback(idp, sp, {})
+ );
+ isString(id) && isString(context) ? t.pass() : t.fail();
+});
+
+test('create logout response with post binding', (t) => {
+ const result = idp.createLogoutResponse(sp, {}, BindingNamespace.Post, '', createTemplateCallback(idp, sp, {}));
+ t.is('entityEndpoint' in result, true);
+ if (!('entityEndpoint' in result)) return;
+ t.true(isString(result.id));
+ t.true(isString(result.context));
+ t.true(isString(result.relayState));
+ t.true(isString(result.entityEndpoint));
+ t.is(result.type, 'SAMLResponse');
});
// Check if the response data parsing is correct
// All test cases are using customize template
// simulate idp-initiated sso
-test('send response with signed assertion and parse it', async t => {
- // sender (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const user = { email: 'user@esaml2.com' };
- const { id, context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(sp, sampleRequestInfo, 'post', user, createTemplateCallback(idpNoEncrypt, sp, user));
- // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const { samlContent, extract } = await sp.parseLoginResponse(idpNoEncrypt, 'post', { body: { SAMLResponse } });
- t.is(typeof id, 'string');
- t.is(samlContent.startsWith(''), true);
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.response.inResponseTo, 'request_id');
-});
-
-test('send response with signed assertion + custom transformation algorithms and parse it', async t => {
- // sender (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const signedAssertionSp = serviceProvider(
- {
- ...defaultSpConfig,
- transformationAlgorithms: [
- 'http://www.w3.org/2000/09/xmldsig#enveloped-signature',
- 'http://www.w3.org/2001/10/xml-exc-c14n#'
- ]
- }
- );
-
- const user = { email: 'user@esaml2.com' };
- const { id, context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(signedAssertionSp, sampleRequestInfo, 'post', user, createTemplateCallback(idpNoEncrypt, sp, user));
- // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const { samlContent, extract } = await sp.parseLoginResponse(idpNoEncrypt, 'post', { body: { SAMLResponse } });
- t.is(typeof id, 'string');
- t.is(samlContent.startsWith(''), true);
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.response.inResponseTo, 'request_id');
-
- // Verify xmldsig#enveloped-signature is included in the response
- if (samlContent.indexOf('http://www.w3.org/2000/09/xmldsig#enveloped-signature') === -1) {
- t.fail();
- }
-});
-
-test('send response with [custom template] signed assertion and parse it', async t => {
- // sender (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const requestInfo = { extract: { request: { id: 'request_id' } } };
- const user = { email: 'user@esaml2.com'};
- const { id, context: SAMLResponse } = await idpcustomNoEncrypt.createLoginResponse(
- sp,
- requestInfo,
- 'post',
- user,
- // declare the callback to do custom template replacement
- createTemplateCallback(idpcustomNoEncrypt, sp, user),
- );
- // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const { samlContent, extract } = await sp.parseLoginResponse(idpcustomNoEncrypt, 'post', { body: { SAMLResponse } });
- t.is(typeof id, 'string');
- t.is(samlContent.startsWith(''), true);
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.attributes.name, 'mynameinsp');
- t.is(extract.attributes.mail, 'myemailassociatedwithsp@sp.com');
- t.is(extract.response.inResponseTo, '_4606cc1f427fa981e6ffd653ee8d6972fc5ce398c4');
-});
-
-test('send response with signed message and parse it', async t => {
- // sender (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const user = { email: 'user@esaml2.com' };
- const { id, context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(spNoAssertSign, sampleRequestInfo, 'post', user, createTemplateCallback(idpNoEncrypt, spNoAssertSign, user));
- // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const { samlContent, extract } = await spNoAssertSign.parseLoginResponse(idpNoEncrypt, 'post', { body: { SAMLResponse } });
- t.is(typeof id, 'string');
- t.is(samlContent.startsWith(''), true);
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.response.inResponseTo, 'request_id');
-});
-
-test('send response with [custom template] and signed message and parse it', async t => {
- // sender (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const requestInfo = { extract: { authnrequest: { id: 'request_id' } } };
- const user = { email: 'user@esaml2.com'};
- const { id, context: SAMLResponse } = await idpcustomNoEncrypt.createLoginResponse(
- spNoAssertSign,
- { extract: { authnrequest: { id: 'request_id' } } }, 'post',
- { email: 'user@esaml2.com' },
- createTemplateCallback(idpcustomNoEncrypt, spNoAssertSign, user),
- );
- // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const { samlContent, extract } = await spNoAssertSign.parseLoginResponse(idpcustomNoEncrypt, 'post', { body: { SAMLResponse } });
- t.is(typeof id, 'string');
- t.is(samlContent.startsWith(''), true);
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.attributes.name, 'mynameinsp');
- t.is(extract.attributes.mail, 'myemailassociatedwithsp@sp.com');
- t.is(extract.response.inResponseTo, '_4606cc1f427fa981e6ffd653ee8d6972fc5ce398c4');
-});
-
-test('send login response with signed assertion + signed message and parse it', async t => {
- const spWantMessageSign = serviceProvider({
- ...defaultSpConfig,
- wantMessageSigned: true,
- });
- const user = { email: 'user@esaml2.com' };
- const { id, context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(spWantMessageSign, sampleRequestInfo, 'post', user, createTemplateCallback(idpNoEncrypt, spWantMessageSign, user));
- // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const { samlContent, extract } = await spWantMessageSign.parseLoginResponse (idpNoEncrypt, 'post', { body: { SAMLResponse } });
- t.is(typeof id, 'string');
- t.is(samlContent.startsWith(''), true);
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.response.inResponseTo, 'request_id');
-});
-
-test('send login response with [custom template] and signed assertion + signed message and parse it', async t => {
- const spWantMessageSign = serviceProvider({
- ...defaultSpConfig,
- wantMessageSigned: true,
- });
- const user = { email: 'user@esaml2.com'};
- const { id, context: SAMLResponse } = await idpcustomNoEncrypt.createLoginResponse(
- spWantMessageSign,
- { extract: { authnrequest: { id: 'request_id' } } }, 'post',
- { email: 'user@esaml2.com' },
- createTemplateCallback(idpcustomNoEncrypt, spWantMessageSign, user),
- );
- // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const { samlContent, extract } = await spWantMessageSign.parseLoginResponse(idpcustomNoEncrypt, 'post', { body: { SAMLResponse } });
- t.is(typeof id, 'string');
- t.is(samlContent.startsWith(''), true);
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.attributes.name, 'mynameinsp');
- t.is(extract.attributes.mail, 'myemailassociatedwithsp@sp.com');
- t.is(extract.response.inResponseTo, '_4606cc1f427fa981e6ffd653ee8d6972fc5ce398c4');
-});
-
-test('send login response with encrypted non-signed assertion and parse it', async t => {
- const user = { email: 'user@esaml2.com' };
- const { id, context: SAMLResponse } = await idp.createLoginResponse(spNoAssertSign, sampleRequestInfo, 'post', user, createTemplateCallback(idp, spNoAssertSign, user));
- // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const { samlContent, extract } = await spNoAssertSign.parseLoginResponse(idp, 'post', { body: { SAMLResponse } });
- t.is(typeof id, 'string');
- t.is(samlContent.startsWith(''), true);
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.response.inResponseTo, 'request_id');
-});
-
-test('send login response with encrypted signed assertion and parse it', async t => {
- const user = { email: 'user@esaml2.com' };
- const { id, context: SAMLResponse } = await idp.createLoginResponse(sp, sampleRequestInfo, 'post', user, createTemplateCallback(idp, sp, user));
- // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const { samlContent, extract } = await sp.parseLoginResponse(idp, 'post', { body: { SAMLResponse } });
- t.is(typeof id, 'string');
- t.is(samlContent.startsWith(''), true);
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.response.inResponseTo, 'request_id');
-});
-
-test('send login response with [custom template] and encrypted signed assertion and parse it', async t => {
- const user = { email: 'user@esaml2.com'};
- const { id, context: SAMLResponse } = await idpcustom.createLoginResponse(
- sp,
- { extract: { request: { id: 'request_id' } } }, 'post',
- { email: 'user@esaml2.com' },
- createTemplateCallback(idpcustom, sp, user),
- );
- // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const { samlContent, extract } = await sp.parseLoginResponse(idpcustom, 'post', { body: { SAMLResponse } });
- t.is(typeof id, 'string');
- t.is(samlContent.startsWith(''), true);
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.attributes.name, 'mynameinsp');
- t.is(extract.attributes.mail, 'myemailassociatedwithsp@sp.com');
- t.is(extract.response.inResponseTo, '_4606cc1f427fa981e6ffd653ee8d6972fc5ce398c4');
-});
-
-test('send login response with encrypted signed assertion + signed message and parse it', async t => {
- const spWantMessageSign = serviceProvider({
- ...defaultSpConfig,
- wantMessageSigned: true,
- });
- const user = { email: 'user@esaml2.com' };
- const { id, context: SAMLResponse } = await idp.createLoginResponse(spWantMessageSign, sampleRequestInfo, 'post', user, createTemplateCallback(idp, spWantMessageSign, user));
- // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const { samlContent, extract } = await spWantMessageSign.parseLoginResponse(idp, 'post', { body: { SAMLResponse } });
-
- t.is(typeof id, 'string');
- t.is(samlContent.startsWith(''), true);
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.response.inResponseTo, 'request_id');
-});
-
-test('send login response with [custom template] encrypted signed assertion + signed message and parse it', async t => {
- const spWantMessageSign = serviceProvider({
- ...defaultSpConfig,
- wantMessageSigned: true,
- });
- const requestInfo = { extract: { authnrequest: { id: 'request_id' } } };
- const user = { email: 'user@esaml2.com'};
- const { id, context: SAMLResponse } = await idpcustom.createLoginResponse(
- spWantMessageSign,
- { extract: { authnrequest: { id: 'request_id' } } }, 'post',
- { email: 'user@esaml2.com' },
- createTemplateCallback(idpcustom, spWantMessageSign, user),
- );
- // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const { samlContent, extract } = await spWantMessageSign.parseLoginResponse(idpcustom, 'post', { body: { SAMLResponse } });
- t.is(typeof id, 'string');
- t.is(samlContent.startsWith(''), true);
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.attributes.name, 'mynameinsp');
- t.is(extract.attributes.mail, 'myemailassociatedwithsp@sp.com');
- t.is(extract.response.inResponseTo, '_4606cc1f427fa981e6ffd653ee8d6972fc5ce398c4');
+test('send response with signed assertion and parse it', async (t) => {
+ // sender (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const user = { email: 'user@esaml2.com' };
+ const { id, context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(
+ sp,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idpNoEncrypt, sp, user, sampleRequestInfo)
+ );
+ // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const { samlContent, extract } = await sp.parseLoginResponse(idpNoEncrypt, BindingNamespace.Post, {
+ body: { SAMLResponse },
+ });
+ t.is(typeof id, 'string');
+ t.is(samlContent.startsWith(''), true);
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.response.inResponseTo, 'request_id');
+});
+
+test('send response with signed assertion + custom transformation algorithms and parse it', async (t) => {
+ // sender (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const signedAssertionSp = serviceProvider({
+ ...defaultSpConfig,
+ transformationAlgorithms: [
+ 'http://www.w3.org/2000/09/xmldsig#enveloped-signature',
+ 'http://www.w3.org/2001/10/xml-exc-c14n#',
+ ],
+ });
+
+ const user = { email: 'user@esaml2.com' };
+ const { id, context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(
+ signedAssertionSp,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idpNoEncrypt, sp, user, sampleRequestInfo)
+ );
+ // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const { samlContent, extract } = await sp.parseLoginResponse(idpNoEncrypt, BindingNamespace.Post, {
+ body: { SAMLResponse },
+ });
+ t.is(typeof id, 'string');
+ t.is(samlContent.startsWith(''), true);
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.response.inResponseTo, 'request_id');
+
+ // Verify xmldsig#enveloped-signature is included in the response
+ if (samlContent.indexOf('http://www.w3.org/2000/09/xmldsig#enveloped-signature') === -1) {
+ t.fail();
+ }
+});
+
+test('send response with [custom template] signed assertion and parse it', async (t) => {
+ // sender (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const requestInfo = { extract: { request: { id: 'request_id' } } };
+ const user = { email: 'user@esaml2.com' };
+ const { id, context: SAMLResponse } = await idpcustomNoEncrypt.createLoginResponse(
+ sp,
+ requestInfo,
+ BindingNamespace.Post,
+ user,
+ // declare the callback to do custom template replacement
+ createTemplateCallback(idpcustomNoEncrypt, sp, user)
+ );
+ // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const { samlContent, extract } = await sp.parseLoginResponse(idpcustomNoEncrypt, BindingNamespace.Post, {
+ body: { SAMLResponse },
+ });
+ t.is(typeof id, 'string');
+ t.is(samlContent.startsWith(''), true);
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.attributes.name, 'mynameinsp');
+ t.is(extract.attributes.mail, 'user@esaml2.com');
+ t.is(extract.response.inResponseTo, '_4606cc1f427fa981e6ffd653ee8d6972fc5ce398c4');
+});
+
+test('send response with signed message and parse it', async (t) => {
+ // sender (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const user = { email: 'user@esaml2.com' };
+ const { id, context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(
+ spNoAssertSign,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idpNoEncrypt, spNoAssertSign, user, sampleRequestInfo)
+ );
+ // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const { samlContent, extract } = await spNoAssertSign.parseLoginResponse(idpNoEncrypt, BindingNamespace.Post, {
+ body: { SAMLResponse },
+ });
+ t.is(typeof id, 'string');
+ t.is(samlContent.startsWith(''), true);
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.response.inResponseTo, 'request_id');
+});
+
+test('send response with [custom template] and signed message and parse it', async (t) => {
+ // sender (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ // const requestInfo = { extract: { authnrequest: { id: 'request_id' } } };
+ const user = { email: 'user@esaml2.com' };
+ const { id, context: SAMLResponse } = await idpcustomNoEncrypt.createLoginResponse(
+ spNoAssertSign,
+ { extract: { authnrequest: { id: 'request_id' } } },
+ BindingNamespace.Post,
+ { email: 'user@esaml2.com' },
+ createTemplateCallback(idpcustomNoEncrypt, spNoAssertSign, user)
+ );
+ // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const { samlContent, extract } = await spNoAssertSign.parseLoginResponse(idpcustomNoEncrypt, BindingNamespace.Post, {
+ body: { SAMLResponse },
+ });
+ t.is(typeof id, 'string');
+ t.is(samlContent.startsWith(''), true);
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.attributes.name, 'mynameinsp');
+ t.is(extract.attributes.mail, 'user@esaml2.com');
+ t.is(extract.response.inResponseTo, '_4606cc1f427fa981e6ffd653ee8d6972fc5ce398c4');
+});
+
+test('send login response with signed assertion + signed message and parse it', async (t) => {
+ const spWantMessageSign = serviceProvider({
+ ...defaultSpConfig,
+ wantMessageSigned: true,
+ });
+ const user = { email: 'user@esaml2.com' };
+ const { id, context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(
+ spWantMessageSign,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idpNoEncrypt, spWantMessageSign, user, sampleRequestInfo)
+ );
+ // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const { samlContent, extract } = await spWantMessageSign.parseLoginResponse(idpNoEncrypt, BindingNamespace.Post, {
+ body: { SAMLResponse },
+ });
+ t.is(typeof id, 'string');
+ t.is(samlContent.startsWith(''), true);
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.response.inResponseTo, 'request_id');
+});
+
+test('send login response with [custom template] and signed assertion + signed message and parse it', async (t) => {
+ const spWantMessageSign = serviceProvider({
+ ...defaultSpConfig,
+ wantMessageSigned: true,
+ });
+ const user = { email: 'user@esaml2.com' };
+ const { id, context: SAMLResponse } = await idpcustomNoEncrypt.createLoginResponse(
+ spWantMessageSign,
+ { extract: { authnrequest: { id: 'request_id' } } },
+ BindingNamespace.Post,
+ { email: 'user@esaml2.com' },
+ createTemplateCallback(idpcustomNoEncrypt, spWantMessageSign, user)
+ );
+ // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const { samlContent, extract } = await spWantMessageSign.parseLoginResponse(
+ idpcustomNoEncrypt,
+ BindingNamespace.Post,
+ {
+ body: { SAMLResponse },
+ }
+ );
+ t.is(typeof id, 'string');
+ t.is(samlContent.startsWith(''), true);
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.attributes.name, 'mynameinsp');
+ t.is(extract.attributes.mail, 'user@esaml2.com');
+ t.is(extract.response.inResponseTo, '_4606cc1f427fa981e6ffd653ee8d6972fc5ce398c4');
+});
+
+test('send login response with encrypted non-signed assertion and parse it', async (t) => {
+ const user = { email: 'user@esaml2.com' };
+ const { id, context: SAMLResponse } = await idp.createLoginResponse(
+ spNoAssertSign,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idp, spNoAssertSign, user, sampleRequestInfo)
+ );
+ // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const { samlContent, extract } = await spNoAssertSign.parseLoginResponse(idp, BindingNamespace.Post, {
+ body: { SAMLResponse },
+ });
+ t.is(typeof id, 'string');
+ t.is(samlContent.startsWith(''), true);
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.response.inResponseTo, 'request_id');
+});
+
+test('send login response with encrypted signed assertion and parse it', async (t) => {
+ const user = { email: 'user@esaml2.com' };
+ const { id, context: SAMLResponse } = await idp.createLoginResponse(
+ sp,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idp, sp, user, sampleRequestInfo)
+ );
+ // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const { samlContent, extract } = await sp.parseLoginResponse(idp, BindingNamespace.Post, { body: { SAMLResponse } });
+ t.is(typeof id, 'string');
+ t.is(samlContent.startsWith(''), true);
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.response.inResponseTo, 'request_id');
+});
+
+test('send login response with [custom template] and encrypted signed assertion and parse it', async (t) => {
+ const user = { email: 'user@esaml2.com' };
+ const { id, context: SAMLResponse } = await idpcustom.createLoginResponse(
+ sp,
+ { extract: { request: { id: 'request_id' } } },
+ BindingNamespace.Post,
+ { email: 'user@esaml2.com' },
+ createTemplateCallback(idpcustom, sp, user)
+ );
+ // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const { samlContent, extract } = await sp.parseLoginResponse(idpcustom, BindingNamespace.Post, {
+ body: { SAMLResponse },
+ });
+ t.is(typeof id, 'string');
+ t.is(samlContent.startsWith(''), true);
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.attributes.name, 'mynameinsp');
+ t.is(extract.attributes.mail, 'user@esaml2.com');
+ t.is(extract.response.inResponseTo, '_4606cc1f427fa981e6ffd653ee8d6972fc5ce398c4');
+});
+
+test('send login response with encrypted signed assertion + signed message and parse it', async (t) => {
+ const spWantMessageSign = serviceProvider({
+ ...defaultSpConfig,
+ wantMessageSigned: true,
+ });
+ const user = { email: 'user@esaml2.com' };
+ const { id, context: SAMLResponse } = await idp.createLoginResponse(
+ spWantMessageSign,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idp, spWantMessageSign, user, sampleRequestInfo)
+ );
+ // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const { samlContent, extract } = await spWantMessageSign.parseLoginResponse(idp, BindingNamespace.Post, {
+ body: { SAMLResponse },
+ });
+
+ t.is(typeof id, 'string');
+ t.is(samlContent.startsWith(''), true);
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.response.inResponseTo, 'request_id');
+});
+
+test('send login response with [custom template] encrypted signed assertion + signed message and parse it', async (t) => {
+ const spWantMessageSign = serviceProvider({
+ ...defaultSpConfig,
+ wantMessageSigned: true,
+ });
+ // const requestInfo = { extract: { authnrequest: { id: 'request_id' } } };
+ const user = { email: 'user@esaml2.com' };
+ const { id, context: SAMLResponse } = await idpcustom.createLoginResponse(
+ spWantMessageSign,
+ { extract: { authnrequest: { id: 'request_id' } } },
+ BindingNamespace.Post,
+ { email: 'user@esaml2.com' },
+ createTemplateCallback(idpcustom, spWantMessageSign, user)
+ );
+ // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const { samlContent, extract } = await spWantMessageSign.parseLoginResponse(idpcustom, BindingNamespace.Post, {
+ body: { SAMLResponse },
+ });
+ t.is(typeof id, 'string');
+ t.is(samlContent.startsWith(''), true);
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.attributes.name, 'mynameinsp');
+ t.is(extract.attributes.mail, 'user@esaml2.com');
+ t.is(extract.response.inResponseTo, '_4606cc1f427fa981e6ffd653ee8d6972fc5ce398c4');
});
// simulate idp-init slo
-test('idp sends a redirect logout request without signature and sp parses it', async t => {
- const { id, context } = idp.createLogoutRequest(sp, 'redirect', { logoutNameID: 'user@esaml2.com' });
- const query = url.parse(context).query;
- t.is(query!.includes('SAMLRequest='), true);
- t.is(typeof id, 'string');
- t.is(typeof context, 'string');
- const originalURL = url.parse(context, true);
- const SAMLRequest = encodeURIComponent(originalURL.query.SAMLRequest as string);
- let result;
- const { samlContent, extract } = result = await sp.parseLogoutRequest(idp, 'redirect', { query: { SAMLRequest }});
- t.is(result.sigAlg, null);
- t.is(typeof samlContent, 'string');
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.signature, null);
- t.is(typeof extract.request.id, 'string');
- t.is(extract.request.destination, 'https://sp.example.org/sp/slo');
- t.is(extract.issuer, 'https://idp.example.com/metadata');
-});
-
-test('idp sends a redirect logout request with signature and sp parses it', async t => {
- const { id, context } = idp.createLogoutRequest(spWantLogoutReqSign, 'redirect', { logoutNameID: 'user@esaml2.com' });
- const query = url.parse(context).query;
- t.is(query!.includes('SAMLRequest='), true);
- t.is(query!.includes('SigAlg='), true);
- t.is(query!.includes('Signature='), true);
- t.is(typeof id, 'string');
- t.is(typeof context, 'string');
- const originalURL = url.parse(context, true);
- const SAMLRequest = originalURL.query.SAMLRequest;
- const Signature = originalURL.query.Signature;
- const SigAlg = originalURL.query.SigAlg;
- delete originalURL.query.Signature;
- const octetString = Object.keys(originalURL.query).map(q => q + '=' + encodeURIComponent(originalURL.query[q] as string)).join('&');
- const { extract } = await spWantLogoutReqSign.parseLogoutRequest(idp, 'redirect', { query: { SAMLRequest, Signature, SigAlg }, octetString});
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.issuer, 'https://idp.example.com/metadata');
- t.is(typeof extract.request.id, 'string');
- t.is(extract.request.destination, 'https://sp.example.org/sp/slo');
- t.is(extract.signature, null); // redirect binding doesn't embed the signature
-});
-
-test('idp sends a post logout request without signature and sp parses it', async t => {
- const { relayState, type, entityEndpoint, id, context } = idp.createLogoutRequest(sp, 'post', { logoutNameID: 'user@esaml2.com' }) as PostBindingContext;
- t.is(typeof id, 'string');
- t.is(typeof context, 'string');
- t.is(typeof entityEndpoint, 'string');
- t.is(type, 'SAMLRequest');
- const { extract } = await sp.parseLogoutRequest(idp, 'post', { body: { SAMLRequest: context } });
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.issuer, 'https://idp.example.com/metadata');
- t.is(typeof extract.request.id, 'string');
- t.is(extract.request.destination, 'https://sp.example.org/sp/slo');
- t.is(extract.signature, null);
-});
-
-test('idp sends a post logout request with signature and sp parses it', async t => {
- const { relayState, type, entityEndpoint, id, context } = idp.createLogoutRequest(spWantLogoutReqSign, 'post', { logoutNameID: 'user@esaml2.com' }) as PostBindingContext;
- t.is(typeof id, 'string');
- t.is(typeof context, 'string');
- t.is(typeof entityEndpoint, 'string');
- t.is(type, 'SAMLRequest');
- const { extract } = await spWantLogoutReqSign.parseLogoutRequest(idp, 'post', { body: { SAMLRequest: context } });
- t.is(extract.nameID, 'user@esaml2.com');
- t.is(extract.issuer, 'https://idp.example.com/metadata');
- t.is(extract.request.destination, 'https://sp.example.org/sp/slo');
- t.is(typeof extract.request.id, 'string');
- t.is(typeof extract.signature, 'string');
+test('idp sends a redirect logout request without signature and sp parses it', async (t) => {
+ const { id, context } = idp.createLogoutRequest(sp, BindingNamespace.Redirect, { logoutNameID: 'user@esaml2.com' });
+ const searchParams = new URL(context).searchParams;
+ t.is(searchParams.has('SAMLRequest'), true);
+ t.is(typeof id, 'string');
+ t.is(typeof context, 'string');
+ const originalURL = new URL(context);
+ const SAMLRequest = encodeURIComponent(originalURL.searchParams.get('SAMLRequest') as string);
+ let result: any;
+ const { samlContent, extract } = (result = await sp.parseLogoutRequest(idp, BindingNamespace.Redirect, {
+ query: { SAMLRequest },
+ }));
+ t.is(result.sigAlg, null);
+ t.is(typeof samlContent, 'string');
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.signature, null);
+ t.is(typeof extract.request.id, 'string');
+ t.is(extract.request.destination, 'https://sp.example.org/sp/slo');
+ t.is(extract.issuer, 'https://idp.example.com/metadata');
+});
+
+test('idp sends a redirect logout request with signature and sp parses it', async (t) => {
+ const { id, context } = idp.createLogoutRequest(spWantLogoutReqSign, BindingNamespace.Redirect, {
+ logoutNameID: 'user@esaml2.com',
+ });
+ const searchParams = new URL(context).searchParams;
+ t.is(searchParams.has('SAMLRequest'), true);
+ t.is(searchParams.has('SigAlg'), true);
+ t.is(searchParams.has('Signature'), true);
+ t.is(typeof id, 'string');
+ t.is(typeof context, 'string');
+ const originalURL = new URL(context);
+ const SAMLRequest = originalURL.searchParams.get('SAMLRequest');
+ const Signature = originalURL.searchParams.get('Signature');
+ const SigAlg = originalURL.searchParams.get('SigAlg');
+ originalURL.searchParams.delete('Signature');
+ const octetString = Array.from(originalURL.searchParams)
+ .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
+ .join('&');
+ const { extract } = await spWantLogoutReqSign.parseLogoutRequest(idp, BindingNamespace.Redirect, {
+ query: { SAMLRequest, Signature, SigAlg },
+ octetString,
+ });
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.issuer, 'https://idp.example.com/metadata');
+ t.is(typeof extract.request.id, 'string');
+ t.is(extract.request.destination, 'https://sp.example.org/sp/slo');
+ t.is(extract.signature, null); // redirect binding doesn't embed the signature
+});
+
+test('idp sends a post logout request without signature and sp parses it', async (t) => {
+ const result = idp.createLogoutRequest(sp, BindingNamespace.Post, {
+ logoutNameID: 'user@esaml2.com',
+ });
+ t.is('entityEndpoint' in result, true);
+ if (!('entityEndpoint' in result)) return;
+ t.true(isString(result.id));
+ t.true(isString(result.context));
+ t.true(isString(result.entityEndpoint));
+ t.is(result.type, 'SAMLRequest');
+ const { extract } = await sp.parseLogoutRequest(idp, BindingNamespace.Post, {
+ body: { SAMLRequest: result.context },
+ });
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.issuer, 'https://idp.example.com/metadata');
+ t.is(typeof extract.request.id, 'string');
+ t.is(extract.request.destination, 'https://sp.example.org/sp/slo');
+ t.is(extract.signature, null);
+});
+
+test('idp sends a post logout request with signature and sp parses it', async (t) => {
+ const result = idp.createLogoutRequest(spWantLogoutReqSign, BindingNamespace.Post, {
+ logoutNameID: 'user@esaml2.com',
+ });
+ t.is('entityEndpoint' in result, true);
+ if (!('entityEndpoint' in result)) return;
+ t.true(isString(result.id));
+ t.true(isString(result.context));
+ t.true(isString(result.entityEndpoint));
+ t.is(result.type, 'SAMLRequest');
+ const { extract } = await spWantLogoutReqSign.parseLogoutRequest(idp, BindingNamespace.Post, {
+ body: { SAMLRequest: result.context },
+ });
+ t.is(extract.nameID, 'user@esaml2.com');
+ t.is(extract.issuer, 'https://idp.example.com/metadata');
+ t.is(extract.request.destination, 'https://sp.example.org/sp/slo');
+ t.is(typeof extract.request.id, 'string');
+ t.is(typeof extract.signature, 'string');
});
// simulate init-slo
-test('sp sends a post logout response without signature and parse', async t => {
- const { context: SAMLResponse } = sp.createLogoutResponse(idp, null, 'post', '', createTemplateCallback(idp, sp, {})) as PostBindingContext;
- const { samlContent, extract } = await idp.parseLogoutResponse(sp, 'post', { body: { SAMLResponse }});
- t.is(extract.signature, null);
- t.is(extract.issuer, 'https://sp.example.org/metadata');
- t.is(typeof extract.response.id, 'string');
- t.is(extract.response.destination, 'https://idp.example.org/sso/SingleLogoutService');
-});
-
-test('sp sends a post logout response with signature and parse', async t => {
- const { relayState, type, entityEndpoint, id, context: SAMLResponse } = sp.createLogoutResponse(idpWantLogoutResSign, sampleRequestInfo, 'post', '', createTemplateCallback(idpWantLogoutResSign, sp, {})) as PostBindingContext;
- const { samlContent, extract } = await idpWantLogoutResSign.parseLogoutResponse(sp, 'post', { body: { SAMLResponse }});
- t.is(typeof extract.signature, 'string');
- t.is(extract.issuer, 'https://sp.example.org/metadata');
- t.is(typeof extract.response.id, 'string');
- t.is(extract.response.destination, 'https://idp.example.org/sso/SingleLogoutService');
-});
-
-test('send login response with encrypted non-signed assertion with EncryptThenSign and parse it', async t => {
- const user = { email: 'user@esaml2.com' };
- const { id, context: SAMLResponse } = await idpEncryptThenSign.createLoginResponse(spNoAssertSignCustomConfig, sampleRequestInfo, 'post', user, createTemplateCallback(idpEncryptThenSign, spNoAssertSignCustomConfig, user), true);
- const { samlContent, extract } = await spNoAssertSignCustomConfig.parseLoginResponse(idpEncryptThenSign, 'post', { body: { SAMLResponse } });
- t.is(typeof id, 'string');
- t.is(samlContent.startsWith(''), true);
- t.is(extract.nameID, 'user@esaml2.com');
-});
-
-test('Customize prefix (saml2) for encrypted assertion tag', async t => {
- const user = { email: 'test@email.com' };
- const idpCustomizePfx = identityProvider(Object.assign(defaultIdpConfig, { tagPrefix: {
- encryptedAssertion: 'saml2',
- }}));
- const { id, context: SAMLResponse } = await idpCustomizePfx.createLoginResponse(sp, sampleRequestInfo, 'post', user, createTemplateCallback(idpCustomizePfx, sp, user));
- t.is((utility.base64Decode(SAMLResponse) as string).includes('saml2:EncryptedAssertion'), true);
- const { samlContent, extract } = await sp.parseLoginResponse(idpCustomizePfx, 'post', { body: { SAMLResponse } });
-});
-
-test('Customize prefix (default is saml) for encrypted assertion tag', async t => {
- const user = { email: 'test@email.com' };
- const { id, context: SAMLResponse } = await idp.createLoginResponse(sp, sampleRequestInfo, 'post', user, createTemplateCallback(idp, sp, user));
- t.is((utility.base64Decode(SAMLResponse) as string).includes('saml:EncryptedAssertion'), true);
- const { samlContent, extract } = await sp.parseLoginResponse(idp, 'post', { body: { SAMLResponse } });
-});
-
-test('avoid malformatted response', async t => {
- // sender (caution: only use metadata and public key when declare pair-up in oppoent entity)
- const user = { email: 'user@email.com' };
- const { context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(sp, sampleRequestInfo, 'post', user, createTemplateCallback(idpNoEncrypt, sp, user));
- const rawResponse = String(utility.base64Decode(SAMLResponse, true));
- const attackResponse = `evil@evil.com${rawResponse}`;
- try {
- await sp.parseLoginResponse(idpNoEncrypt, 'post', { body: { SAMLResponse: utility.base64Encode(attackResponse) } });
- } catch (e) {
- // it must throw an error
- t.is(true, true);
- }
-});
-
-test('should reject signature wrapped response - case 1', async t => {
- //
- const user = { email: 'user@esaml2.com' };
- const { id, context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(sp, sampleRequestInfo, 'post', user, createTemplateCallback(idpNoEncrypt, sp, user));
- //Decode
- const buffer = Buffer.from(SAMLResponse, 'base64');
- const xml = buffer.toString();
- //Create version of response without signature
- const stripped = xml
- .replace(//, '');
- //Create version of response with altered IDs and new username
- const outer = xml
- .replace(/assertion" ID="_[0-9a-f]{3}/g, 'assertion" ID="_000')
- .replace('user@esaml2.com', 'admin@esaml2.com');
- //Put stripped version under SubjectConfirmationData of modified version
- const xmlWrapped = outer.replace(/]*\/>/, '' + stripped.replace('', '') + '');
- const wrappedResponse = Buffer.from(xmlWrapped).toString('base64');
- try {
- await sp.parseLoginResponse(idpNoEncrypt, 'post', { body: { SAMLResponse: wrappedResponse } });
- } catch (e) {
- t.is(e.message, 'ERR_POTENTIAL_WRAPPING_ATTACK');
- }
-});
-
-test('should reject signature wrapped response - case 2', async t => {
- //
- const user = { email: 'user@esaml2.com' };
- const { id, context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(sp, sampleRequestInfo, 'post', user, createTemplateCallback(idpNoEncrypt, sp, user));
- //Decode
- const buffer = Buffer.from(SAMLResponse, 'base64');
- const xml = buffer.toString();
- //Create version of response without signature
- const stripped = xml
- .replace(//, '');
- //Create version of response with altered IDs and new username
- const outer = xml
- .replace(/assertion" ID="_[0-9a-f]{3}/g, 'assertion" ID="_000')
- .replace('user@esaml2.com', 'admin@esaml2.com');
- //Put stripped version under SubjectConfirmationData of modified version
- const xmlWrapped = outer.replace(/<\/saml:Conditions>/, '' + stripped.replace('', '') + '');
- const wrappedResponse = Buffer.from(xmlWrapped).toString('base64');
- try {
- const result = await sp.parseLoginResponse(idpNoEncrypt, 'post', { body: { SAMLResponse: wrappedResponse } });
- } catch (e) {
- t.is(e.message, 'ERR_POTENTIAL_WRAPPING_ATTACK');
- }
-});
-
-test('should throw two-tiers code error when the response does not return success status', async t => {
- try {
- const _result = await sp.parseLoginResponse(idpNoEncrypt, 'post', { body: { SAMLResponse: utility.base64Encode(failedResponse) } });
- } catch (e) {
- t.is(e.message, 'ERR_FAILED_STATUS with top tier code: urn:oasis:names:tc:SAML:2.0:status:Requester, second tier code: urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy');
- }
-});
-
-test.serial('should throw ERR_SUBJECT_UNCONFIRMED for the expired SAML response without clock drift setup', async t => {
-
- const now = new Date();
- const fiveMinutesOneSecLater = new Date(now.getTime());
- fiveMinutesOneSecLater.setMinutes(fiveMinutesOneSecLater.getMinutes() + 5);
- fiveMinutesOneSecLater.setSeconds(fiveMinutesOneSecLater.getSeconds() + 1);
-
- const user = { email: 'user@esaml2.com' };
-
- try {
- const { context: SAMLResponse } = await idp.createLoginResponse(sp, sampleRequestInfo, 'post', user, createTemplateCallback(idp, sp, user));
- // simulate the time on client side when response arrives after 5.1 sec
- tk.freeze(fiveMinutesOneSecLater);
- await sp.parseLoginResponse(idp, 'post', { body: { SAMLResponse } });
- // test failed, it shouldn't happen
- t.is(true, false);
- } catch (e) {
- t.is(e, 'ERR_SUBJECT_UNCONFIRMED');
- } finally {
- tk.reset();
- }
-});
-
-test.serial('should not throw ERR_SUBJECT_UNCONFIRMED for the expired SAML response with clock drift setup', async t => {
-
- const now = new Date();
- const fiveMinutesOneSecLater = new Date(now.getTime());
- fiveMinutesOneSecLater.setMinutes(fiveMinutesOneSecLater.getMinutes() + 5);
- fiveMinutesOneSecLater.setSeconds(fiveMinutesOneSecLater.getSeconds() + 1);
- const user = { email: 'user@esaml2.com' };
-
- try {
- const { context: SAMLResponse } = await idp.createLoginResponse(spWithClockDrift, sampleRequestInfo, 'post', user, createTemplateCallback(idp, spWithClockDrift, user));
- // simulate the time on client side when response arrives after 5.1 sec
- tk.freeze(fiveMinutesOneSecLater);
- await spWithClockDrift.parseLoginResponse(idp, 'post', { body: { SAMLResponse } });
- t.is(true, true);
- } catch (e) {
- // test failed, it shouldn't happen
- t.is(e, false);
- } finally {
- tk.reset();
- }
-
-});
\ No newline at end of file
+test('sp sends a post logout response without signature and parse', async (t) => {
+ const { context: SAMLResponse } = sp.createLogoutResponse(
+ idp,
+ null,
+ BindingNamespace.Post,
+ '',
+ createTemplateCallback(idp, sp, {})
+ );
+ const { /*samlContent,*/ extract } = await idp.parseLogoutResponse(sp, BindingNamespace.Post, {
+ body: { SAMLResponse },
+ });
+ t.is(extract.signature, null);
+ t.is(extract.issuer, 'https://sp.example.org/metadata');
+ t.is(typeof extract.response.id, 'string');
+ t.is(extract.response.destination, 'https://idp.example.org/sso/SingleLogoutService');
+});
+
+test('sp sends a post logout response with signature and parse', async (t) => {
+ const { /*relayState, type, entityEndpoint, id,*/ context: SAMLResponse } = sp.createLogoutResponse(
+ idpWantLogoutResSign,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ '',
+ createTemplateCallback(idpWantLogoutResSign, sp, {})
+ );
+ const { /*samlContent,*/ extract } = await idpWantLogoutResSign.parseLogoutResponse(sp, BindingNamespace.Post, {
+ body: { SAMLResponse },
+ });
+ t.is(typeof extract.signature, 'string');
+ t.is(extract.issuer, 'https://sp.example.org/metadata');
+ t.is(typeof extract.response.id, 'string');
+ t.is(extract.response.destination, 'https://idp.example.org/sso/SingleLogoutService');
+});
+
+test('send login response with encrypted non-signed assertion with EncryptThenSign and parse it', async (t) => {
+ const user = { email: 'user@esaml2.com' };
+ const { id, context: SAMLResponse } = await idpEncryptThenSign.createLoginResponse(
+ spNoAssertSignCustomConfig,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idpEncryptThenSign, spNoAssertSignCustomConfig, user),
+ true
+ );
+ const { samlContent, extract } = await spNoAssertSignCustomConfig.parseLoginResponse(
+ idpEncryptThenSign,
+ BindingNamespace.Post,
+ {
+ body: { SAMLResponse },
+ }
+ );
+ t.is(typeof id, 'string');
+ t.is(samlContent.startsWith(''), true);
+ t.is(extract.nameID, 'user@esaml2.com');
+});
+
+test('Customize prefix (saml2) for encrypted assertion tag', async (t) => {
+ const user = { email: 'test@email.com' };
+ const idpCustomizePfx = identityProvider(
+ Object.assign(defaultIdpConfig, {
+ tagPrefix: {
+ encryptedAssertion: 'saml2',
+ },
+ })
+ );
+ const { /*id,*/ context: SAMLResponse } = await idpCustomizePfx.createLoginResponse(
+ sp,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idpCustomizePfx, sp, user)
+ );
+ t.is((base64Decode(SAMLResponse) as string).includes('saml2:EncryptedAssertion'), true);
+ await sp.parseLoginResponse(idpCustomizePfx, BindingNamespace.Post, {
+ body: { SAMLResponse },
+ });
+});
+
+test('Customize prefix (default is saml) for encrypted assertion tag', async (t) => {
+ const user = { email: 'test@email.com' };
+ const { /*id,*/ context: SAMLResponse } = await idp.createLoginResponse(
+ sp,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idp, sp, user)
+ );
+ t.is((base64Decode(SAMLResponse) as string).includes('saml:EncryptedAssertion'), true);
+ await sp.parseLoginResponse(idp, BindingNamespace.Post, { body: { SAMLResponse } });
+});
+
+test('avoid malformatted response', async (t) => {
+ // sender (caution: only use metadata and public key when declare pair-up in oppoent entity)
+ const user = { email: 'user@email.com' };
+ const { context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(
+ sp,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idpNoEncrypt, sp, user)
+ );
+ const rawResponse = String(base64Decode(SAMLResponse, true));
+ const attackResponse = `evil@evil.com${rawResponse}`;
+ try {
+ await sp.parseLoginResponse(idpNoEncrypt, BindingNamespace.Post, {
+ body: { SAMLResponse: base64Encode(attackResponse) },
+ });
+ } catch (e) {
+ // it must throw an error
+ t.is(true, true);
+ }
+});
+
+test('should reject signature wrapped response - case 1', async (t) => {
+ //
+ const user = { email: 'user@esaml2.com' };
+ const { /*id,*/ context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(
+ sp,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idpNoEncrypt, sp, user)
+ );
+ //Decode
+ const buffer = Buffer.from(SAMLResponse, 'base64');
+ const xml = buffer.toString();
+ //Create version of response without signature
+ const stripped = xml.replace(//, '');
+ //Create version of response with altered IDs and new username
+ const outer = xml
+ .replace(/assertion" ID="_[0-9a-f]{3}/g, 'assertion" ID="_000')
+ .replace('user@esaml2.com', 'admin@esaml2.com');
+ //Put stripped version under SubjectConfirmationData of modified version
+ const xmlWrapped = outer.replace(
+ /]*\/>/,
+ '' +
+ stripped.replace('', '') +
+ ''
+ );
+ const wrappedResponse = Buffer.from(xmlWrapped).toString('base64');
+ try {
+ await sp.parseLoginResponse(idpNoEncrypt, BindingNamespace.Post, { body: { SAMLResponse: wrappedResponse } });
+ } catch (e) {
+ t.is(isSamlifyError(e), true);
+ t.is(e.code, SamlifyErrorCode.PotentialWrappingAttack);
+ }
+});
+
+test('should reject signature wrapped response - case 2', async (t) => {
+ //
+ const user = { email: 'user@esaml2.com' };
+ const { /*id,*/ context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(
+ sp,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idpNoEncrypt, sp, user)
+ );
+ //Decode
+ const buffer = Buffer.from(SAMLResponse, 'base64');
+ const xml = buffer.toString();
+ //Create version of response without signature
+ const stripped = xml.replace(//, '');
+ //Create version of response with altered IDs and new username
+ const outer = xml
+ .replace(/assertion" ID="_[0-9a-f]{3}/g, 'assertion" ID="_000')
+ .replace('user@esaml2.com', 'admin@esaml2.com');
+ //Put stripped version under SubjectConfirmationData of modified version
+ const xmlWrapped = outer.replace(
+ /<\/saml:Conditions>/,
+ '' +
+ stripped.replace('', '') +
+ ''
+ );
+ const wrappedResponse = Buffer.from(xmlWrapped).toString('base64');
+ try {
+ await sp.parseLoginResponse(idpNoEncrypt, BindingNamespace.Post, {
+ body: { SAMLResponse: wrappedResponse },
+ });
+ } catch (e) {
+ t.is(isSamlifyError(e), true);
+ t.is(e.code, SamlifyErrorCode.PotentialWrappingAttack);
+ }
+});
+
+test('should throw two-tiers code error when the response does not return success status', async (t) => {
+ try {
+ await sp.parseLoginResponse(idpNoEncrypt, BindingNamespace.Post, {
+ body: { SAMLResponse: base64Encode(failedResponse) },
+ });
+ } catch (e) {
+ t.is(isSamlifyError(e), true);
+ t.is(e.code, SamlifyErrorCode.FailedStatus);
+ t.is(
+ e.message,
+ 'with top tier code: urn:oasis:names:tc:SAML:2.0:status:Requester, second tier code: urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy'
+ );
+ }
+});
+
+test.serial('should throw SUBJECT_UNCONFIRMED for the expired SAML response without clock drift setup', async (t) => {
+ const now = new Date();
+ const fiveMinutesOneSecLater = new Date(now.getTime());
+ fiveMinutesOneSecLater.setMinutes(fiveMinutesOneSecLater.getMinutes() + 5);
+ fiveMinutesOneSecLater.setSeconds(fiveMinutesOneSecLater.getSeconds() + 1);
+
+ const user = { email: 'user@esaml2.com' };
+
+ try {
+ const { context: SAMLResponse } = await idp.createLoginResponse(
+ sp,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idp, sp, user)
+ );
+ // simulate the time on client side when response arrives after 5.1 sec
+ tk.freeze(fiveMinutesOneSecLater);
+ await sp.parseLoginResponse(idp, BindingNamespace.Post, { body: { SAMLResponse } });
+ // test failed, it shouldn't happen
+ t.is(true, false);
+ } catch (e) {
+ t.is(isSamlifyError(e), true);
+ t.is(e.code, SamlifyErrorCode.SubjectUnconfirmed);
+ } finally {
+ tk.reset();
+ }
+});
+
+test.serial('should not throw SUBJECT_UNCONFIRMED for the expired SAML response with clock drift setup', async (t) => {
+ const now = new Date();
+ const fiveMinutesOneSecLater = new Date(now.getTime());
+ fiveMinutesOneSecLater.setMinutes(fiveMinutesOneSecLater.getMinutes() + 5);
+ fiveMinutesOneSecLater.setSeconds(fiveMinutesOneSecLater.getSeconds() + 1);
+ const user = { email: 'user@esaml2.com' };
+
+ try {
+ const { context: SAMLResponse } = await idp.createLoginResponse(
+ spWithClockDrift,
+ sampleRequestInfo,
+ BindingNamespace.Post,
+ user,
+ createTemplateCallback(idp, spWithClockDrift, user)
+ );
+ // simulate the time on client side when response arrives after 5.1 sec
+ tk.freeze(fiveMinutesOneSecLater);
+ await spWithClockDrift.parseLoginResponse(idp, BindingNamespace.Post, { body: { SAMLResponse } });
+ t.is(true, true);
+ } catch (e) {
+ // test failed, it shouldn't happen
+ t.is(e, false);
+ } finally {
+ tk.reset();
+ }
+});
diff --git a/test/index.ts b/test/index.ts
index 07cfe83e..71d75361 100644
--- a/test/index.ts
+++ b/test/index.ts
@@ -1,21 +1,20 @@
-import esaml2 = require('../index');
-import { readFileSync, writeFileSync } from 'fs';
+/* eslint-disable @typescript-eslint/no-unsafe-return */
+/* eslint-disable @typescript-eslint/no-unsafe-call */
import test from 'ava';
+import { readFileSync } from 'fs';
+import { identityProvider, libsaml, metadataIdp, metadataSp, serviceProvider } from '../src';
+import { isSamlifyError, SamlifyErrorCode } from '../src/error';
+import { algorithms, BindingNamespace, elementsOrder, names } from '../src/urn';
+import {
+ base64Decode,
+ base64Encode,
+ deflateString,
+ inflateString,
+ normalizeCerString,
+ normalizePemString,
+} from '../src/utility';
import { verifyTime } from '../src/validator';
-const {
- IdentityProvider: identityProvider,
- ServiceProvider: serviceProvider,
- IdPMetadata: idpMetadata,
- SPMetadata: spMetadata,
- Utility: utility,
- SamlLib: libsaml,
- Constants: ref,
-} = esaml2;
-
-const binding = ref.namespace.binding;
-const algorithms = ref.algorithms;
-const wording = ref.wording;
const signatureAlgorithms = algorithms.signature;
const _spKeyFolder = './test/key/sp/';
@@ -23,392 +22,504 @@ const _spPrivPem = String(readFileSync(_spKeyFolder + 'privkey.pem'));
const _spPrivKeyPass = 'VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px';
const defaultIdpConfig = {
- privateKey: readFileSync('./test/key/idp/privkey.pem'),
- privateKeyPass: 'q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW',
- isAssertionEncrypted: true,
- encPrivateKey: readFileSync('./test/key/idp/encryptKey.pem'),
- encPrivateKeyPass: 'g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN',
- metadata: readFileSync('./test/misc/idpmeta.xml'),
+ privateKey: readFileSync('./test/key/idp/privkey.pem'),
+ privateKeyPass: 'q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW',
+ isAssertionEncrypted: true,
+ encPrivateKey: readFileSync('./test/key/idp/encryptKey.pem'),
+ encPrivateKeyPass: 'g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN',
+ metadata: readFileSync('./test/misc/idpmeta.xml'),
};
const defaultSpConfig = {
- privateKey: readFileSync('./test/key/sp/privkey.pem'),
- privateKeyPass: 'VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px',
- isAssertionEncrypted: true,
- encPrivateKey: readFileSync('./test/key/sp/encryptKey.pem'),
- encPrivateKeyPass: 'BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU',
- metadata: readFileSync('./test/misc/spmeta.xml'),
+ privateKey: readFileSync('./test/key/sp/privkey.pem'),
+ privateKeyPass: 'VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px',
+ isAssertionEncrypted: true,
+ encPrivateKey: readFileSync('./test/key/sp/encryptKey.pem'),
+ encPrivateKeyPass: 'BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU',
+ metadata: readFileSync('./test/misc/spmeta.xml'),
};
const idp = identityProvider(defaultIdpConfig);
const idpRollingCert = identityProvider({
- ...defaultIdpConfig,
- metadata: readFileSync('./test/misc/idpmeta_rollingcert.xml'),
+ ...defaultIdpConfig,
+ metadata: readFileSync('./test/misc/idpmeta_rollingcert.xml'),
});
const sp = serviceProvider(defaultSpConfig);
-const IdPMetadata = idpMetadata(readFileSync('./test/misc/idpmeta.xml'));
-const SPMetadata = spMetadata(readFileSync('./test/misc/spmeta.xml'));
+const IdPMetadata = metadataIdp(readFileSync('./test/misc/idpmeta.xml'));
+const SPMetadata = metadataSp(readFileSync('./test/misc/spmeta.xml'));
const sampleSignedResponse = readFileSync('./test/misc/response_signed.xml').toString();
const wrongResponse = readFileSync('./test/misc/invalid_response.xml').toString();
const spCertKnownGood = readFileSync('./test/key/sp/knownGoodCert.cer').toString().trim();
const spPemKnownGood = readFileSync('./test/key/sp/knownGoodEncryptKey.pem').toString().trim();
-test('base64 encoding returns encoded string', t => {
- t.is(utility.base64Encode('Hello World'), 'SGVsbG8gV29ybGQ=');
+test('base64 encoding returns encoded string', (t) => {
+ t.is(base64Encode('Hello World'), 'SGVsbG8gV29ybGQ=');
});
-test('base64 decoding returns decoded string', t => {
- t.is(utility.base64Decode('SGVsbG8gV29ybGQ='), 'Hello World');
+test('base64 decoding returns decoded string', (t) => {
+ t.is(base64Decode('SGVsbG8gV29ybGQ='), 'Hello World');
});
-test('deflate + base64 encoded', t => {
- t.is(utility.base64Encode(utility.deflateString('Hello World')), '80jNyclXCM8vykkBAA==');
+test('deflate + base64 encoded', (t) => {
+ t.is(base64Encode(deflateString('Hello World')), '80jNyclXCM8vykkBAA==');
});
-test('base64 decoded + inflate', t => {
- t.is(utility.inflateString('80jNyclXCM8vykkBAA=='), 'Hello World');
+test('base64 decoded + inflate', (t) => {
+ t.is(inflateString('80jNyclXCM8vykkBAA=='), 'Hello World');
});
-test('parse cer format resulting clean certificate', t => {
- t.is(utility.normalizeCerString(readFileSync('./test/key/sp/cert.cer')), spCertKnownGood);
+test('parse cer format resulting clean certificate', (t) => {
+ t.is(normalizeCerString(readFileSync('./test/key/sp/cert.cer')), spCertKnownGood);
});
-test('normalize pem key returns clean string', t => {
- const ekey = readFileSync('./test/key/sp/encryptKey.pem').toString();
- t.is(utility.normalizePemString(ekey), spPemKnownGood);
+test('normalize pem key returns clean string', (t) => {
+ const ekey = readFileSync('./test/key/sp/encryptKey.pem').toString();
+ t.is(normalizePemString(ekey), spPemKnownGood);
});
-test('getAssertionConsumerService with one binding', t => {
- const expectedPostLocation = 'https:sp.example.org/sp/sso/post';
- const _sp = serviceProvider({
- privateKey: './test/key/sp/privkey.pem',
- privateKeyPass: 'VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px',
- isAssertionEncrypted: true,
- encPrivateKey: './test/key/sp/encryptKey.pem',
- encPrivateKeyPass: 'BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU',
- assertionConsumerService: [{
- Binding: binding.post,
- Location: expectedPostLocation,
- }],
- singleLogoutService: [{
- Binding: binding.redirect,
- Location: 'https:sp.example.org/sp/slo',
- }],
- });
- t.is(_sp.entityMeta.getAssertionConsumerService(wording.binding.post), expectedPostLocation);
+test('getAssertionConsumerService with one binding', (t) => {
+ const expectedPostLocation = 'https:sp.example.org/sp/sso/post';
+ const _sp = serviceProvider({
+ privateKey: './test/key/sp/privkey.pem',
+ privateKeyPass: 'VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px',
+ isAssertionEncrypted: true,
+ encPrivateKey: './test/key/sp/encryptKey.pem',
+ encPrivateKeyPass: 'BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU',
+ assertionConsumerService: [
+ {
+ Binding: BindingNamespace.Post,
+ Location: expectedPostLocation,
+ },
+ ],
+ singleLogoutService: [
+ {
+ Binding: BindingNamespace.Redirect,
+ Location: 'https:sp.example.org/sp/slo',
+ },
+ ],
+ });
+ t.is(_sp.getEntityMeta().getAssertionConsumerService(BindingNamespace.Post), expectedPostLocation);
});
-test('getAssertionConsumerService with two bindings', t => {
- const expectedPostLocation = 'https:sp.example.org/sp/sso/post';
- const expectedArtifactLocation = 'https:sp.example.org/sp/sso/artifact';
- const _sp = serviceProvider({
- privateKey: './test/key/sp/privkey.pem',
- privateKeyPass: 'VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px',
- isAssertionEncrypted: true,
- encPrivateKey: './test/key/sp/encryptKey.pem',
- encPrivateKeyPass: 'BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU',
- assertionConsumerService: [{
- Binding: binding.post,
- Location: expectedPostLocation,
- }, {
- Binding: binding.artifact,
- Location: expectedArtifactLocation,
- }],
- singleLogoutService: [{
- Binding: binding.redirect,
- Location: 'https:sp.example.org/sp/slo',
- }, {
- Binding: binding.post,
- Location: 'https:sp.example.org/sp/slo',
- }],
- });
- t.is(_sp.entityMeta.getAssertionConsumerService(wording.binding.post), expectedPostLocation);
- t.is(_sp.entityMeta.getAssertionConsumerService(wording.binding.artifact), expectedArtifactLocation);
+test('getAssertionConsumerService with two bindings', (t) => {
+ const expectedPostLocation = 'https:sp.example.org/sp/sso/post';
+ const expectedArtifactLocation = 'https:sp.example.org/sp/sso/artifact';
+ const _sp = serviceProvider({
+ privateKey: './test/key/sp/privkey.pem',
+ privateKeyPass: 'VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px',
+ isAssertionEncrypted: true,
+ encPrivateKey: './test/key/sp/encryptKey.pem',
+ encPrivateKeyPass: 'BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU',
+ assertionConsumerService: [
+ {
+ Binding: BindingNamespace.Post,
+ Location: expectedPostLocation,
+ },
+ {
+ Binding: BindingNamespace.Artifact,
+ Location: expectedArtifactLocation,
+ },
+ ],
+ singleLogoutService: [
+ {
+ Binding: BindingNamespace.Redirect,
+ Location: 'https:sp.example.org/sp/slo',
+ },
+ {
+ Binding: BindingNamespace.Post,
+ Location: 'https:sp.example.org/sp/slo',
+ },
+ ],
+ });
+ t.is(_sp.getEntityMeta().getAssertionConsumerService(BindingNamespace.Post), expectedPostLocation);
+ t.is(_sp.getEntityMeta().getAssertionConsumerService(BindingNamespace.Artifact), expectedArtifactLocation);
});
(() => {
-
-
- const _originRequest: string = String(readFileSync('./test/misc/request.xml'));
- const _decodedResponse: string = String(readFileSync('./test/misc/response_signed.xml'));
- const _falseDecodedRequestSHA1: string = String(readFileSync('./test/misc/false_signed_request_sha1.xml'));
- const _decodedRequestSHA256: string = String(readFileSync('./test/misc/signed_request_sha256.xml'));
- const _falseDecodedRequestSHA256: string = String(readFileSync('./test/misc/false_signed_request_sha256.xml'));
- const _decodedRequestSHA512: string = String(readFileSync('./test/misc/signed_request_sha512.xml'));
- const _falseDecodedRequestSHA512: string = String(readFileSync('./test/misc/false_signed_request_sha512.xml'));
-
- const octetString: string = 'SAMLRequest=fVNdj9MwEHxH4j9Yfm%2Fi5PpBrLaotEJUOrioKTzwgoy9oZZiO9ibu%2FLvcXLtKUhHnyzZM7Mzu+tlEKZp+abDkz3A7w4CkrNpbODDw4p23nIngg7cCgOBo+TV5vM9zxPGW+%2FQSdfQEeU2Q4QAHrWzlOx3K%2FrjHSsWbFEzdsfETDE2z5ksVKHqYlHP84WooVBS5lNKvoEPkbeiUYaS0rtHrcB%2FiRVWtCoJRuNRM4QO9jagsBiRLJtO2GKSzY%2F5HZ%2FlfDr7TskuIrUVOIidEFueplq1CZyFaRtIpDNpVT1U4B+1hKQ9tUO5IegHbZW2v25n%2FPkMCvzT8VhOyofqSMnmmnvrbOgM+Iv818P9i4nwrwcFxmVp1IJzb+K9kIGu374hZNm3mQ9R%2Ffp1rgEUSqBYpmPsC7nlfd%2F2u9I1Wv4hH503Av8fKkuy4UarST1AORihm41SHkKI4ZrGPW09CIyzQN8BTce1LmsFaliy2ACEM5KtM63wOvRTiNYlPoe7xhtjt01cmwPU65ubJbnscfG6jMeT8+qS%2FlWpwV96w2BEXN%2FHn2P9Fw%3D%3D&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1';
- const octetStringSHA256: string = 'SAMLRequest=fZJbTwIxEIX%2Fyqbvy3Yv3BogQYiRBJWw6INvY3eAJt0WO10v%2F966YIKJkPRpek7nfDMdEdT6IKaN35s1vjVIPvqstSHRXoxZ44ywQIqEgRpJeCnK6f1SZB0uDs56K61mZ5brDiBC55U1LFrMx2wrB8P%2BIB%2FGeQHbuOgVwxigB3EqewXfDjDPZJ9Fz%2BgoWMYsvBB8RA0uDHkwPpR42o1THvNswzMRTtHtpEX2wqJ5QFEGfOvce38QSaKtBL235EXOeZoQ2aRUZqexVDvzaEp070pikveG3W5otTrx3ShTBdl1tNejiMTdZrOKV4%2FlhkXTX9yZNdTU6E4dntbLfzIVnGdtJpDEJqOfaYqW1k0ua2v0UIGHUXKuHx3X%2BhBSLuYrq5X8im6tq8Ffhkg7aVtRVbxtpQJrUHpaVQ6JAozW9mPmEDyGzYEmZMnk2PbvB5p8Aw%3D%3D&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256';
- const octetStringSHA512: string = 'SAMLRequest=fZJfT8IwFMW%2FytL3sY5tCA0jQYiRBIUw9MG3a3cnTboWezv%2FfHvr0AQT9fX2nJ7zu%2B2UoNVHMe%2F8wezwuUPy0VurDYn%2BoGSdM8ICKRIGWiThpajmN2sxHHBxdNZbaTU7s%2FzvACJ0XlnDotWyZFBkDcAE47wZjeNcXqTxGAsZy0lR1EUzAiwaFt2jo2ApWbgh%2BIg6XBnyYHwY8bSIUx7z4Z4PRZaLbDLg4%2FyBRcuAogz43nnw%2FiiSRFsJ%2BmDJi4zzNCGySaXMk8ZKPZmNqdC9KIlJNgr5IWr7xXepTB1k%2F6M9nkQkrvf7bbzdVHsWzb9xF9ZQ16L7SrjbrX%2FplHM%2B7DuBJDabfm5T9LRu9re2RQ81eJgm5%2Frp6VlvQ8vVcmu1ku%2FRlXUt%2BL8h0kHaT1QdN71UYAtKz%2BvaIVGA0dq%2BLhyCx5I1oAlZMjvF%2FvxAsw8%3D&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha512';
- const signatureB64SHA512: string = 'pLoxKnpOVA1mvLpOZCyzCyB/P01Qcy7cEFskzycm5sdNFYjmZAMGT6yxCgTRvzIloX2J7abZdAkU1dA8kY2yPQrWCuQFOxeSCqnGpHg5/bBKzFiGwWtlyHgh7LXEEo2zKWspJh7BhwRIbtOAnN3XvCPDO58wKHnEdxo9TneTyFmy5hcfYKcF7LlI8jSFkmsPvCsMMJ8TawgnKlwdIU0Ze/cp64Y24cpYxVIKtCC950VRuxAt3bmr7pqtIEsHKkqTOrPv5pWo2XqRG0UhvzjYCbpC8aGOuqLe8hfTfgpQ6ebUkqrgAufkLrinOGpZrlQQDFr0iVIKR30bInDGjg2G+g==';
- const signatureB64SHA256: string = 'iC7RXfHuIu4gBLGABv0qtt96XFvyC7QSX8cDyLjJj+WNOTRMO5J/AYKelVhuc2AZuyGcf/sfeeVmcW7wyKTBHiGS+AWUCljmG43mPWERPfsa7og+GxrsHDSFh5nD70mQF44bXvpo/oVOxHx/lPiDG5LZg2KBccNXqJxMVUhnyU6xeGBctYY5ZQ4y7MGOx7hWTWjHyv+wyFd44Bcq0kpunTls91z03GkYo/Oxd4KllbfR5D2v6awjrc79wMYL1CcZiKZ941ter6tHOHCwtZRhTqV3Dl42zOKUOCyGcjJnVzJre1QBA7hrn3WB5/fu5kE6/E9ENRWp8ZRJLbU8C2Oogg==';
- const signatureB64SHA1: string = 'UKPzYQivZOavFV3QjOH/B9AwKls9n5hZIzOL+V93Yi7lJ7siNkAA9WZgErtFVpDTN6ngSwvlfP/hXZcS33RcCGBWi1SX+xuwuk2U7bZgdkkw4tIH8zcgiRy8bK0IpMoXmLbApU2QsiNwRDMZq3iQdlaMhlsJh85VI+90SQk7fewseiw5Ui6BIpFSH96gLYjWMDPpwk+0GkhkkVaP5vo+I6mBQryD9YPFRu7JfCrnw2T6gldXlGu0IN326+qajKheAGmPSLWBmeFYhquJ5ipgfQGU/KCNIEUr6hkW8NU0+6EVaZl/A9Fyfs1+8KCQ6HxZ7FGyewQjJIx3a8XvBM5vDg==';
- const dummySignRequest: string = 'PHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iXzgwOTcwN2YwMDMwYTVkMDA2MjBjOWQ5ZGY5N2Y2MjdhZmU5ZGNjMjQiIFZlcnNpb249IjIuMCIgUHJvdmlkZXJOYW1lPSJTUCB0ZXN0IiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDctMTZUMjM6NTI6NDVaIiBEZXN0aW5hdGlvbj0iaHR0cDovL2lkcC5leGFtcGxlLmNvbS9TU09TZXJ2aWNlLnBocCIgUHJvdG9jb2xCaW5kaW5nPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YmluZGluZ3M6SFRUUC1QT1NUIiBBc3NlcnRpb25Db25zdW1lclNlcnZpY2VVUkw9Imh0dHBzOi8vc3AuZXhhbXBsZS5vcmcvc3Avc3NvIj48c2FtbDpJc3N1ZXIgSWQ9Il8wIj5odHRwczovL3NwLmV4YW1wbGUub3JnL21ldGFkYXRhPC9zYW1sOklzc3Vlcj48c2FtbHA6TmFtZUlEUG9saWN5IEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIiBBbGxvd0NyZWF0ZT0idHJ1ZSIvPjxzYW1scDpSZXF1ZXN0ZWRBdXRobkNvbnRleHQgQ29tcGFyaXNvbj0iZXhhY3QiPjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbHA6UmVxdWVzdGVkQXV0aG5Db250ZXh0PjxTaWduYXR1cmUgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxTaWduZWRJbmZvPjxDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PFNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPjxSZWZlcmVuY2UgVVJJPSIjXzAiPjxUcmFuc2Zvcm1zPjxUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L1RyYW5zZm9ybXM+PERpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PERpZ2VzdFZhbHVlPnRRRGlzQlhLVFErOU9YSk81cjdLdUpnYStLST08L0RpZ2VzdFZhbHVlPjwvUmVmZXJlbmNlPjwvU2lnbmVkSW5mbz48U2lnbmF0dXJlVmFsdWU+b3hSa3ZhdTdVdllnRkVaN1lOQVVOZjMwNjdWN1RuNUM5WFNJaWV0MWFadzJGWWV2Tlc1YlV5LzBteHAzYWo2QXZmRmpubXB6QWI4OEJqZHdBejJCRXJEVG9tUmN1WkI3TGIwZllUZjMxTjJvWk9YME1pUGlRT0g1NEk2M3FKVzRYbzNWcWRGN0dCdUZaWkh5bGxmU0J2N2dmQ3RqSkR3RlNDeldLNzBCOXIzY0ZNUkpaTGhDSjlvUGVuKzRVOXNjU1lPNmcrc3pCWkxsNkFpSjA2UEhjOGp6RUtHd2ZRcmNaazhrREtVbHZOZkpNVUx5cThkcHgyVnZVQXg0cDVld2ZNT3dCOVczSGwzUFBhMGRPNzd6WmlmM0NnbHBjTjA2ZittNlVZRy93bm9UUUV5S1c5aE9lKzJ2R004MFc3N2VXdTBkbWlhUHVxVDFvazhMWFB1cTFBPT08L1NpZ25hdHVyZVZhbHVlPjxLZXlJbmZvPjxYNTA5RGF0YT48WDUwOUNlcnRpZmljYXRlPk1JSURvekNDQW91Z0F3SUJBZ0lKQUtOc21MOFFiZnB3TUEwR0NTcUdTSWIzRFFFQkN3VUFNR2d4Q3pBSkJnTlZCQVlUQWtoTE1SSXdFQVlEVlFRSURBbEliMjVuSUV0dmJtY3hDekFKQmdOVkJBY01Ba2hMTVJNd0VRWURWUVFLREFwdWIyUmxMWE5oYld3eU1TTXdJUVlKS29aSWh2Y05BUWtCRmhSdWIyUmxMbk5oYld3eVFHZHRZV2xzTG1OdmJUQWVGdzB4TlRBM01EVXhOelUyTkRkYUZ3MHhPREEzTURReE56VTJORGRhTUdneEN6QUpCZ05WQkFZVEFraExNUkl3RUFZRFZRUUlEQWxJYjI1bklFdHZibWN4Q3pBSkJnTlZCQWNNQWtoTE1STXdFUVlEVlFRS0RBcHViMlJsTFhOaGJXd3lNU013SVFZSktvWklodmNOQVFrQkZoUnViMlJsTG5OaGJXd3lRR2R0WVdsc0xtTnZiVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNUUpBQjhKcnNMUWJVdUphOGFrekxxTzFFWnFDbFMwdFFwK3crNXdndWZwMDdXd0duL3NobWE4ZGNRTmoxZGJqc3pJNUhCZVZGak9LSXhsZmptTkI5b3ZoUVBzdEJqUC9VUFFZcDFJcDJJb0hDWVg5SERnTXozeHlYS2JIdGhVelphRUN6K3ArN1d0Z3doY3pSa0JMRE9tMmsxNXFoUFlHUHcwdkgyemJWUkdXVUJTOWR5Mk1wM3RxbFZiUDB4WjlDRE5raENKa1Y5U01OZm9DVlcvVllQcUsyUUJvN2tpNG9ibTV4NWl4RlFTU0hzS2JWQVJWenlRSDVpTmpGZTFUZEFwM3JEd3JFNUxjMU5RbFFheFI1R25iMk5aQXBET1JSWklWbE52MldVZGk5UXZNMHlDempROTBqUDBPQW9nSGhSWWF4ZzAvdmdORXllNDZoK1BpWTBDQXdFQUFhTlFNRTR3SFFZRFZSME9CQllFRkVWa2pjTEFJVG5ka3kwOTBBeTc0UXFDbVFLSU1COEdBMVVkSXdRWU1CYUFGRVZramNMQUlUbmRreTA5MEF5NzRRcUNtUUtJTUF3R0ExVWRFd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRzRsWVgzS1FYZW5lejRMcERuWmhjRkJFWmk5WXN0VUtQRjVFS2QrV3BscFZiY1RRYzFBMy9aK3VIUm15VjhoK3BRemVGNkxpb2IzN0c4N1lwYWNQcGxKSTY2Y2YyUmo3ajhoU0JOYmRyKzY2RTJxcGNFaEFGMWlKbXpCTnloYi95ZGxFdVZwbjgvRXNvUCtIdkJlaURsNWdvbjM1NjJNelpJZ1YvcExkVGZ4SHlXNmh6QVFoakdxMlVoY3ZSK2dYTlZKdkhQMmVTNGpsSG5Ka0I5YmZvMGt2Zjg3UStENlhLWDNxNWMzbU84dHFXNlVwcUhTQyt1TEVwelppTkxldUZhNFRVSWhnQmdqRGpsUnJOREt1OG5kYW5jU24zeUJIWW5xSjJ0OWNSK2NvRm5uallBQlFwTnJ2azRtdG1YWThTWG9CellHOVkrbHFlQXVuNiswWXlFPTwvWDUwOUNlcnRpZmljYXRlPjwvWDUwOURhdGE+PC9LZXlJbmZvPjwvU2lnbmF0dXJlPjwvc2FtbHA6QXV0aG5SZXF1ZXN0Pg==';
- const dummySignRequestSHA256: string = 'PHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iXzgwOTcwN2YwMDMwYTVkMDA2MjBjOWQ5ZGY5N2Y2MjdhZmU5ZGNjMjQiIFZlcnNpb249IjIuMCIgUHJvdmlkZXJOYW1lPSJTUCB0ZXN0IiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDctMTZUMjM6NTI6NDVaIiBEZXN0aW5hdGlvbj0iaHR0cDovL2lkcC5leGFtcGxlLmNvbS9TU09TZXJ2aWNlLnBocCIgUHJvdG9jb2xCaW5kaW5nPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YmluZGluZ3M6SFRUUC1QT1NUIiBBc3NlcnRpb25Db25zdW1lclNlcnZpY2VVUkw9Imh0dHBzOi8vc3AuZXhhbXBsZS5vcmcvc3Avc3NvIj48c2FtbDpJc3N1ZXIgSWQ9Il8wIj5odHRwczovL3NwLmV4YW1wbGUub3JnL21ldGFkYXRhPC9zYW1sOklzc3Vlcj48c2FtbHA6TmFtZUlEUG9saWN5IEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIiBBbGxvd0NyZWF0ZT0idHJ1ZSIvPjxzYW1scDpSZXF1ZXN0ZWRBdXRobkNvbnRleHQgQ29tcGFyaXNvbj0iZXhhY3QiPjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbHA6UmVxdWVzdGVkQXV0aG5Db250ZXh0PjxTaWduYXR1cmUgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxTaWduZWRJbmZvPjxDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PFNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGEyNTYiLz48UmVmZXJlbmNlIFVSST0iI18wIj48VHJhbnNmb3Jtcz48VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9UcmFuc2Zvcm1zPjxEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48RGlnZXN0VmFsdWU+d3VKWlJSdWlGb0FQZVZXVllReXhOWXpjbUpJdXB0dTZmaE10MVZuQVZQbz08L0RpZ2VzdFZhbHVlPjwvUmVmZXJlbmNlPjwvU2lnbmVkSW5mbz48U2lnbmF0dXJlVmFsdWU+V0VUTUtaL1pzTm5pbDVjVCtHeTFKbmJWMVVscUN2N205SlppZ1NLTXFhbFlOL1ZDclMxelpFMkVOekxFSjhCN1ZaVkMyRVJBT2pHL1lHbWJ4Si95K2Z6YVR1bGh0blhrYUZncytmNEdJZDBISDY0MldKRnRBeUg2RS81SUVVWUVXYUk0TzA5MWgvd2EvM2EyNEJZK3R5L0ExSmIxLzM5NXpXVi84NUZETXFNemdVRDdRYkQ4TG5mcThkS1hJZDdQWmdnVnpQTFpvRHo0YXpaL3V4VG9aUkxwKy9XVjZHQy91Y2lLMmVmR1hMb09NMm1wcElDc05qVk9mT1NEM2pXS3BjQk11bDBRMjJZMGFoaXlKWDlFcnZkSEcwV0RMcXI0RXc5TGFqVVNydFovaGNqR1ZIemhZZCs1YklYSXp6ZWlmbUF6Snp4WFM4cmhjNGVoV25OYTJ3PT08L1NpZ25hdHVyZVZhbHVlPjxLZXlJbmZvPjxYNTA5RGF0YT48WDUwOUNlcnRpZmljYXRlPk1JSURvekNDQW91Z0F3SUJBZ0lKQUtOc21MOFFiZnB3TUEwR0NTcUdTSWIzRFFFQkN3VUFNR2d4Q3pBSkJnTlZCQVlUQWtoTE1SSXdFQVlEVlFRSURBbEliMjVuSUV0dmJtY3hDekFKQmdOVkJBY01Ba2hMTVJNd0VRWURWUVFLREFwdWIyUmxMWE5oYld3eU1TTXdJUVlKS29aSWh2Y05BUWtCRmhSdWIyUmxMbk5oYld3eVFHZHRZV2xzTG1OdmJUQWVGdzB4TlRBM01EVXhOelUyTkRkYUZ3MHhPREEzTURReE56VTJORGRhTUdneEN6QUpCZ05WQkFZVEFraExNUkl3RUFZRFZRUUlEQWxJYjI1bklFdHZibWN4Q3pBSkJnTlZCQWNNQWtoTE1STXdFUVlEVlFRS0RBcHViMlJsTFhOaGJXd3lNU013SVFZSktvWklodmNOQVFrQkZoUnViMlJsTG5OaGJXd3lRR2R0WVdsc0xtTnZiVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNUUpBQjhKcnNMUWJVdUphOGFrekxxTzFFWnFDbFMwdFFwK3crNXdndWZwMDdXd0duL3NobWE4ZGNRTmoxZGJqc3pJNUhCZVZGak9LSXhsZmptTkI5b3ZoUVBzdEJqUC9VUFFZcDFJcDJJb0hDWVg5SERnTXozeHlYS2JIdGhVelphRUN6K3ArN1d0Z3doY3pSa0JMRE9tMmsxNXFoUFlHUHcwdkgyemJWUkdXVUJTOWR5Mk1wM3RxbFZiUDB4WjlDRE5raENKa1Y5U01OZm9DVlcvVllQcUsyUUJvN2tpNG9ibTV4NWl4RlFTU0hzS2JWQVJWenlRSDVpTmpGZTFUZEFwM3JEd3JFNUxjMU5RbFFheFI1R25iMk5aQXBET1JSWklWbE52MldVZGk5UXZNMHlDempROTBqUDBPQW9nSGhSWWF4ZzAvdmdORXllNDZoK1BpWTBDQXdFQUFhTlFNRTR3SFFZRFZSME9CQllFRkVWa2pjTEFJVG5ka3kwOTBBeTc0UXFDbVFLSU1COEdBMVVkSXdRWU1CYUFGRVZramNMQUlUbmRreTA5MEF5NzRRcUNtUUtJTUF3R0ExVWRFd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRzRsWVgzS1FYZW5lejRMcERuWmhjRkJFWmk5WXN0VUtQRjVFS2QrV3BscFZiY1RRYzFBMy9aK3VIUm15VjhoK3BRemVGNkxpb2IzN0c4N1lwYWNQcGxKSTY2Y2YyUmo3ajhoU0JOYmRyKzY2RTJxcGNFaEFGMWlKbXpCTnloYi95ZGxFdVZwbjgvRXNvUCtIdkJlaURsNWdvbjM1NjJNelpJZ1YvcExkVGZ4SHlXNmh6QVFoakdxMlVoY3ZSK2dYTlZKdkhQMmVTNGpsSG5Ka0I5YmZvMGt2Zjg3UStENlhLWDNxNWMzbU84dHFXNlVwcUhTQyt1TEVwelppTkxldUZhNFRVSWhnQmdqRGpsUnJOREt1OG5kYW5jU24zeUJIWW5xSjJ0OWNSK2NvRm5uallBQlFwTnJ2azRtdG1YWThTWG9CellHOVkrbHFlQXVuNiswWXlFPTwvWDUwOUNlcnRpZmljYXRlPjwvWDUwOURhdGE+PC9LZXlJbmZvPjwvU2lnbmF0dXJlPjwvc2FtbHA6QXV0aG5SZXF1ZXN0Pg==';
- const dummySignRequestSHA512: string = 'PHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iXzgwOTcwN2YwMDMwYTVkMDA2MjBjOWQ5ZGY5N2Y2MjdhZmU5ZGNjMjQiIFZlcnNpb249IjIuMCIgUHJvdmlkZXJOYW1lPSJTUCB0ZXN0IiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDctMTZUMjM6NTI6NDVaIiBEZXN0aW5hdGlvbj0iaHR0cDovL2lkcC5leGFtcGxlLmNvbS9TU09TZXJ2aWNlLnBocCIgUHJvdG9jb2xCaW5kaW5nPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YmluZGluZ3M6SFRUUC1QT1NUIiBBc3NlcnRpb25Db25zdW1lclNlcnZpY2VVUkw9Imh0dHBzOi8vc3AuZXhhbXBsZS5vcmcvc3Avc3NvIj48c2FtbDpJc3N1ZXIgSWQ9Il8wIj5odHRwczovL3NwLmV4YW1wbGUub3JnL21ldGFkYXRhPC9zYW1sOklzc3Vlcj48c2FtbHA6TmFtZUlEUG9saWN5IEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIiBBbGxvd0NyZWF0ZT0idHJ1ZSIvPjxzYW1scDpSZXF1ZXN0ZWRBdXRobkNvbnRleHQgQ29tcGFyaXNvbj0iZXhhY3QiPjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbHA6UmVxdWVzdGVkQXV0aG5Db250ZXh0PjxTaWduYXR1cmUgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxTaWduZWRJbmZvPjxDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PFNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGE1MTIiLz48UmVmZXJlbmNlIFVSST0iI18wIj48VHJhbnNmb3Jtcz48VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9UcmFuc2Zvcm1zPjxEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGE1MTIiLz48RGlnZXN0VmFsdWU+RWN3emlpSzZmazFNK2RETkpHNVlFeWpGY3Fjc0dzRmZNNGFDUkJKcENWTlltVWs4NWJxQk8rblRFN3RmRnd5Uk1yOUZBODBpSnN3MlFwM3R4QTE1Q2c9PTwvRGlnZXN0VmFsdWU+PC9SZWZlcmVuY2U+PC9TaWduZWRJbmZvPjxTaWduYXR1cmVWYWx1ZT5MVmFYajQ3MlZEalBvQU1hZ1BNcEswdGwvckV1c2llVXc4SXZrVVJmVVJDKzl1YXNqRXgxZjR4S1dkYUJLa09zQUhIZ1RMVlpxUnBNY1RBVnJTWDM5SnN1TmRDZnlycXBZTWlBY0w0RXhTM3dOSXdBenFCY1RiUlgxdEY2Nzk5cENYVXVOTE84NVdyN3FwZG5RTnFkTWc1L0E5a0xzUjFSc2dOeFhtandPM1dKUDhucFJ5dXYrVjJvNXhvN01FOVYyaVE4ODRhWVhnNUJodWQ5S1huSU5TZWw5YjN2NnV6T3V2VlFSM1ZCTlFWUXhRaGNUNlFpZ1BkR1hqZDl0cEU4TXV0UG5ZS1NNbHJKc1Ird2wzV2ZacmhwQ2E4U2JGS0RjNnBja1lmVUJYV3pRVVFJVkpXRm5icXBlemJsSUk2NmtlNlRvSzVseVpiajRSajFEcytjMHc9PTwvU2lnbmF0dXJlVmFsdWU+PEtleUluZm8+PFg1MDlEYXRhPjxYNTA5Q2VydGlmaWNhdGU+TUlJRG96Q0NBb3VnQXdJQkFnSUpBS05zbUw4UWJmcHdNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1HZ3hDekFKQmdOVkJBWVRBa2hMTVJJd0VBWURWUVFJREFsSWIyNW5JRXR2Ym1jeEN6QUpCZ05WQkFjTUFraExNUk13RVFZRFZRUUtEQXB1YjJSbExYTmhiV3d5TVNNd0lRWUpLb1pJaHZjTkFRa0JGaFJ1YjJSbExuTmhiV3d5UUdkdFlXbHNMbU52YlRBZUZ3MHhOVEEzTURVeE56VTJORGRhRncweE9EQTNNRFF4TnpVMk5EZGFNR2d4Q3pBSkJnTlZCQVlUQWtoTE1SSXdFQVlEVlFRSURBbEliMjVuSUV0dmJtY3hDekFKQmdOVkJBY01Ba2hMTVJNd0VRWURWUVFLREFwdWIyUmxMWE5oYld3eU1TTXdJUVlKS29aSWh2Y05BUWtCRmhSdWIyUmxMbk5oYld3eVFHZHRZV2xzTG1OdmJUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQU1RSkFCOEpyc0xRYlV1SmE4YWt6THFPMUVacUNsUzB0UXArdys1d2d1ZnAwN1d3R24vc2htYThkY1FOajFkYmpzekk1SEJlVkZqT0tJeGxmam1OQjlvdmhRUHN0QmpQL1VQUVlwMUlwMklvSENZWDlIRGdNejN4eVhLYkh0aFV6WmFFQ3orcCs3V3Rnd2hjelJrQkxET20yazE1cWhQWUdQdzB2SDJ6YlZSR1dVQlM5ZHkyTXAzdHFsVmJQMHhaOUNETmtoQ0prVjlTTU5mb0NWVy9WWVBxSzJRQm83a2k0b2JtNXg1aXhGUVNTSHNLYlZBUlZ6eVFINWlOakZlMVRkQXAzckR3ckU1TGMxTlFsUWF4UjVHbmIyTlpBcERPUlJaSVZsTnYyV1VkaTlRdk0weUN6alE5MGpQME9Bb2dIaFJZYXhnMC92Z05FeWU0NmgrUGlZMENBd0VBQWFOUU1FNHdIUVlEVlIwT0JCWUVGRVZramNMQUlUbmRreTA5MEF5NzRRcUNtUUtJTUI4R0ExVWRJd1FZTUJhQUZFVmtqY0xBSVRuZGt5MDkwQXk3NFFxQ21RS0lNQXdHQTFVZEV3UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFHNGxZWDNLUVhlbmV6NExwRG5aaGNGQkVaaTlZc3RVS1BGNUVLZCtXcGxwVmJjVFFjMUEzL1ordUhSbXlWOGgrcFF6ZUY2TGlvYjM3Rzg3WXBhY1BwbEpJNjZjZjJSajdqOGhTQk5iZHIrNjZFMnFwY0VoQUYxaUptekJOeWhiL3lkbEV1VnBuOC9Fc29QK0h2QmVpRGw1Z29uMzU2Mk16WklnVi9wTGRUZnhIeVc2aHpBUWhqR3EyVWhjdlIrZ1hOVkp2SFAyZVM0amxIbkprQjliZm8wa3ZmODdRK0Q2WEtYM3E1YzNtTzh0cVc2VXBxSFNDK3VMRXB6WmlOTGV1RmE0VFVJaGdCZ2pEamxSck5ES3U4bmRhbmNTbjN5QkhZbnFKMnQ5Y1IrY29Gbm5qWUFCUXBOcnZrNG10bVhZOFNYb0J6WUc5WStscWVBdW42KzBZeUU9PC9YNTA5Q2VydGlmaWNhdGU+PC9YNTA5RGF0YT48L0tleUluZm8+PC9TaWduYXR1cmU+PC9zYW1scDpBdXRoblJlcXVlc3Q+';
-
- test('sign a SAML message with RSA-SHA1', t => {
- t.is(libsaml.constructMessageSignature(octetString, _spPrivPem, _spPrivKeyPass).toString('base64'), signatureB64SHA1);
- });
- test('sign a SAML message with RSA-SHA256', t => {
- t.is(libsaml.constructMessageSignature(octetStringSHA256, _spPrivPem, _spPrivKeyPass, undefined, signatureAlgorithms.RSA_SHA256).toString('base64'), signatureB64SHA256);
- });
- test('sign a SAML message with RSA-SHA512', t => {
- t.is(libsaml.constructMessageSignature(octetStringSHA512, _spPrivPem, _spPrivKeyPass, undefined, signatureAlgorithms.RSA_SHA512).toString('base64'), signatureB64SHA512);
- });
- test('verify binary SAML message signed with RSA-SHA1', t => {
- const signature = libsaml.constructMessageSignature(octetString, _spPrivPem, _spPrivKeyPass, false);
- t.is(libsaml.verifyMessageSignature(SPMetadata, octetString, signature), true);
- });
- test('verify binary SAML message signed with RSA-SHA256', t => {
- const signature = libsaml.constructMessageSignature(octetStringSHA256, _spPrivPem, _spPrivKeyPass, false, signatureAlgorithms.RSA_SHA256);
- t.is(libsaml.verifyMessageSignature(SPMetadata, octetStringSHA256, signature, signatureAlgorithms.RSA_SHA256), true);
- });
- test('verify binary SAML message signed with RSA-SHA512', t => {
- const signature = libsaml.constructMessageSignature(octetStringSHA512, _spPrivPem, _spPrivKeyPass, false, signatureAlgorithms.RSA_SHA512);
- t.is(libsaml.verifyMessageSignature(SPMetadata, octetStringSHA512, signature, signatureAlgorithms.RSA_SHA512), true);
- });
- test('verify stringified SAML message signed with RSA-SHA1', t => {
- const signature = libsaml.constructMessageSignature(octetString, _spPrivPem, _spPrivKeyPass);
- t.is(libsaml.verifyMessageSignature(SPMetadata, octetString, Buffer.from(signature, 'base64')), true);
- });
- test('verify stringified SAML message signed with RSA-SHA256', t => {
- const signature = libsaml.constructMessageSignature(octetStringSHA256, _spPrivPem, _spPrivKeyPass);
- t.is(libsaml.verifyMessageSignature(SPMetadata, octetStringSHA256, Buffer.from(signature, 'base64')), true);
- });
- test('verify stringified SAML message signed with RSA-SHA512', t => {
- const signature = libsaml.constructMessageSignature(octetStringSHA512, _spPrivPem, _spPrivKeyPass);
- t.is(libsaml.verifyMessageSignature(SPMetadata, octetStringSHA512, Buffer.from(signature, 'base64')), true);
- });
- test('construct signature with RSA-SHA1', t => {
- t.is(libsaml.constructSAMLSignature({
- rawSamlMessage: _originRequest,
- referenceTagXPath: libsaml.createXPath('Issuer'),
- signingCert: SPMetadata.getX509Certificate('signing') as string,
- privateKey: _spPrivPem,
- privateKeyPass: _spPrivKeyPass,
- signatureAlgorithm: signatureAlgorithms.RSA_SHA1,
- }), dummySignRequest);
- });
- test('construct signature with RSA-SHA256', t => {
- t.is(libsaml.constructSAMLSignature({
- rawSamlMessage: _originRequest,
- referenceTagXPath: libsaml.createXPath('Issuer'),
- signingCert: SPMetadata.getX509Certificate('signing') as string,
- privateKey: _spPrivPem,
- privateKeyPass: _spPrivKeyPass,
- signatureAlgorithm: signatureAlgorithms.RSA_SHA256,
- }), dummySignRequestSHA256);
- });
- test('construct signature with RSA-SHA512', t => {
- t.is(libsaml.constructSAMLSignature({
- rawSamlMessage: _originRequest,
- referenceTagXPath: libsaml.createXPath('Issuer'),
- signingCert: SPMetadata.getX509Certificate('signing') as string,
- privateKey: _spPrivPem,
- privateKeyPass: _spPrivKeyPass,
- signatureAlgorithm: signatureAlgorithms.RSA_SHA512,
- }), dummySignRequestSHA512);
- });
- test('verify a XML signature signed by RSA-SHA1 with metadata', t => {
- t.is(libsaml.verifySignature(_decodedResponse, { metadata: IdPMetadata })[0], true);
- });
- test('integrity check for request signed with RSA-SHA1', t => {
- try {
- libsaml.verifySignature(_falseDecodedRequestSHA1, { metadata: SPMetadata, signatureAlgorithm: signatureAlgorithms.RSA_SHA1 });
- } catch (e) {
- t.is(e.message, 'ERR_FAILED_TO_VERIFY_SIGNATURE');
- }
- });
- test('verify a XML signature signed by RSA-SHA256 with metadata', t => {
- t.is(libsaml.verifySignature(_decodedRequestSHA256, { metadata: SPMetadata, signatureAlgorithm: signatureAlgorithms.RSA_SHA256 })[0], true);
- });
- test('integrity check for request signed with RSA-SHA256', t => {
- try {
- libsaml.verifySignature(_falseDecodedRequestSHA256, { metadata: SPMetadata, signatureAlgorithm: signatureAlgorithms.RSA_SHA256 });
- } catch (e) {
- t.is(e.message, 'ERR_FAILED_TO_VERIFY_SIGNATURE');
- }
- });
- test('verify a XML signature signed by RSA-SHA512 with metadata', t => {
- t.is(libsaml.verifySignature(_decodedRequestSHA512, { metadata: SPMetadata, signatureAlgorithm: signatureAlgorithms.RSA_SHA512 })[0], true);
- });
- test('integrity check for request signed with RSA-SHA512', t => {
- try {
- libsaml.verifySignature(_falseDecodedRequestSHA512, { metadata: SPMetadata, signatureAlgorithm: signatureAlgorithms.RSA_SHA512 });
- } catch (e) {
- t.is(e.message, 'ERR_FAILED_TO_VERIFY_SIGNATURE');
- }
- });
-
- test('verify a XML signature with metadata but with rolling certificate', t => {
-
- const responseSignedByCert1 = String(readFileSync('./test/misc/response_signed_cert1.xml'));
- const responseSignedByCert2 = String(readFileSync('./test/misc/response_signed_cert2.xml'));
- t.is(libsaml.verifySignature(responseSignedByCert1, { metadata: idpRollingCert.entityMeta, signatureAlgorithm: signatureAlgorithms.RSA_SHA256 })[0], true);
- t.is(libsaml.verifySignature(responseSignedByCert2, { metadata: idpRollingCert.entityMeta, signatureAlgorithm: signatureAlgorithms.RSA_SHA256 })[0], true);
-
- });
-
- test('verify a XML signature signed by RSA-SHA1 with .cer keyFile', t => {
- const xml = String(readFileSync('./test/misc/signed_request_sha1.xml'));
- t.is(libsaml.verifySignature(xml, { keyFile: './test/key/sp/cert.cer' })[0], true);
- });
- test('verify a XML signature signed by RSA-SHA256 with .cer keyFile', t => {
- const xml = String(readFileSync('./test/misc/signed_request_sha256.xml'));
- t.is(libsaml.verifySignature(xml, { keyFile: './test/key/sp/cert.cer' })[0], true);
- });
- test('verify a XML signature signed by RSA-SHA512 with .cer keyFile', t => {
- const xml = String(readFileSync('./test/misc/signed_request_sha512.xml'));
- t.is(libsaml.verifySignature(xml, { keyFile: './test/key/sp/cert.cer' })[0], true);
- });
- test('encrypt assertion test passes', async t => {
- await t.notThrowsAsync(() => libsaml.encryptAssertion(idp, sp, sampleSignedResponse));
- });
- test('encrypt assertion response without assertion returns error', async t => {
- const error = await t.throwsAsync(() => libsaml.encryptAssertion(idp, sp, wrongResponse));
- t.is(error.message, 'ERR_MULTIPLE_ASSERTION');
- });
- test('encrypt assertion with invalid xml syntax returns error', async t => {
- const error = await t.throwsAsync(() => libsaml.encryptAssertion(idp, sp, 'This is not a xml format string'));
- t.is(error.message, 'ERR_MULTIPLE_ASSERTION');
- });
- test('encrypt assertion with empty string returns error', async t => {
- const error = await t.throwsAsync(() => libsaml.encryptAssertion(idp, sp, ''));
- t.is(error.message, 'ERR_UNDEFINED_ASSERTION');
- });
- test('encrypt assertion with undefined string returns error', async t => {
- const error = await t.throwsAsync(() => libsaml.encryptAssertion(idp, sp, undefined));
- t.is(error.message, 'ERR_UNDEFINED_ASSERTION');
- });
- test('building attribute statement with one attribute', t => {
- const attributes = [{
- name: 'email',
- valueTag: 'user.email',
- nameFormat: 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
- valueXsiType: 'xs:string'
- }];
- const expectedStatement = '{attrUserEmail}';
-
- t.is(libsaml.attributeStatementBuilder(attributes), expectedStatement);
- });
- test('building attribute statement with multiple attributes', t => {
- const attributes = [{
- name: 'email',
- valueTag: 'user.email',
- nameFormat: 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
- valueXsiType: 'xs:string',
- }, {
- name: 'firstname',
- valueTag: 'user.firstname',
- nameFormat: 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
- valueXsiType: 'xs:string',
- }];
- const expectedStatement = '{attrUserEmail}{attrUserFirstname}';
- t.is(libsaml.attributeStatementBuilder(attributes), expectedStatement);
- });
+ const _originRequest = String(readFileSync('./test/misc/request.xml'));
+ const _decodedResponse = String(readFileSync('./test/misc/response_signed.xml'));
+ const _falseDecodedRequestSHA1 = String(readFileSync('./test/misc/false_signed_request_sha1.xml'));
+ const _decodedRequestSHA256 = String(readFileSync('./test/misc/signed_request_sha256.xml'));
+ const _falseDecodedRequestSHA256 = String(readFileSync('./test/misc/false_signed_request_sha256.xml'));
+ const _decodedRequestSHA512 = String(readFileSync('./test/misc/signed_request_sha512.xml'));
+ const _falseDecodedRequestSHA512 = String(readFileSync('./test/misc/false_signed_request_sha512.xml'));
+
+ const octetString =
+ 'SAMLRequest=fVNdj9MwEHxH4j9Yfm%2Fi5PpBrLaotEJUOrioKTzwgoy9oZZiO9ibu%2FLvcXLtKUhHnyzZM7Mzu+tlEKZp+abDkz3A7w4CkrNpbODDw4p23nIngg7cCgOBo+TV5vM9zxPGW+%2FQSdfQEeU2Q4QAHrWzlOx3K%2FrjHSsWbFEzdsfETDE2z5ksVKHqYlHP84WooVBS5lNKvoEPkbeiUYaS0rtHrcB%2FiRVWtCoJRuNRM4QO9jagsBiRLJtO2GKSzY%2F5HZ%2FlfDr7TskuIrUVOIidEFueplq1CZyFaRtIpDNpVT1U4B+1hKQ9tUO5IegHbZW2v25n%2FPkMCvzT8VhOyofqSMnmmnvrbOgM+Iv818P9i4nwrwcFxmVp1IJzb+K9kIGu374hZNm3mQ9R%2Ffp1rgEUSqBYpmPsC7nlfd%2F2u9I1Wv4hH503Av8fKkuy4UarST1AORihm41SHkKI4ZrGPW09CIyzQN8BTce1LmsFaliy2ACEM5KtM63wOvRTiNYlPoe7xhtjt01cmwPU65ubJbnscfG6jMeT8+qS%2FlWpwV96w2BEXN%2FHn2P9Fw%3D%3D&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1';
+ const octetStringSHA256 =
+ 'SAMLRequest=fZJbTwIxEIX%2Fyqbvy3Yv3BogQYiRBJWw6INvY3eAJt0WO10v%2F966YIKJkPRpek7nfDMdEdT6IKaN35s1vjVIPvqstSHRXoxZ44ywQIqEgRpJeCnK6f1SZB0uDs56K61mZ5brDiBC55U1LFrMx2wrB8P%2BIB%2FGeQHbuOgVwxigB3EqewXfDjDPZJ9Fz%2BgoWMYsvBB8RA0uDHkwPpR42o1THvNswzMRTtHtpEX2wqJ5QFEGfOvce38QSaKtBL235EXOeZoQ2aRUZqexVDvzaEp070pikveG3W5otTrx3ShTBdl1tNejiMTdZrOKV4%2FlhkXTX9yZNdTU6E4dntbLfzIVnGdtJpDEJqOfaYqW1k0ua2v0UIGHUXKuHx3X%2BhBSLuYrq5X8im6tq8Ffhkg7aVtRVbxtpQJrUHpaVQ6JAozW9mPmEDyGzYEmZMnk2PbvB5p8Aw%3D%3D&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256';
+ const octetStringSHA512 =
+ 'SAMLRequest=fZJfT8IwFMW%2FytL3sY5tCA0jQYiRBIUw9MG3a3cnTboWezv%2FfHvr0AQT9fX2nJ7zu%2B2UoNVHMe%2F8wezwuUPy0VurDYn%2BoGSdM8ICKRIGWiThpajmN2sxHHBxdNZbaTU7s%2FzvACJ0XlnDotWyZFBkDcAE47wZjeNcXqTxGAsZy0lR1EUzAiwaFt2jo2ApWbgh%2BIg6XBnyYHwY8bSIUx7z4Z4PRZaLbDLg4%2FyBRcuAogz43nnw%2FiiSRFsJ%2BmDJi4zzNCGySaXMk8ZKPZmNqdC9KIlJNgr5IWr7xXepTB1k%2F6M9nkQkrvf7bbzdVHsWzb9xF9ZQ16L7SrjbrX%2FplHM%2B7DuBJDabfm5T9LRu9re2RQ81eJgm5%2Frp6VlvQ8vVcmu1ku%2FRlXUt%2BL8h0kHaT1QdN71UYAtKz%2BvaIVGA0dq%2BLhyCx5I1oAlZMjvF%2FvxAsw8%3D&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha512';
+ const signatureB64SHA512 =
+ 'pLoxKnpOVA1mvLpOZCyzCyB/P01Qcy7cEFskzycm5sdNFYjmZAMGT6yxCgTRvzIloX2J7abZdAkU1dA8kY2yPQrWCuQFOxeSCqnGpHg5/bBKzFiGwWtlyHgh7LXEEo2zKWspJh7BhwRIbtOAnN3XvCPDO58wKHnEdxo9TneTyFmy5hcfYKcF7LlI8jSFkmsPvCsMMJ8TawgnKlwdIU0Ze/cp64Y24cpYxVIKtCC950VRuxAt3bmr7pqtIEsHKkqTOrPv5pWo2XqRG0UhvzjYCbpC8aGOuqLe8hfTfgpQ6ebUkqrgAufkLrinOGpZrlQQDFr0iVIKR30bInDGjg2G+g==';
+ const signatureB64SHA256 =
+ 'iC7RXfHuIu4gBLGABv0qtt96XFvyC7QSX8cDyLjJj+WNOTRMO5J/AYKelVhuc2AZuyGcf/sfeeVmcW7wyKTBHiGS+AWUCljmG43mPWERPfsa7og+GxrsHDSFh5nD70mQF44bXvpo/oVOxHx/lPiDG5LZg2KBccNXqJxMVUhnyU6xeGBctYY5ZQ4y7MGOx7hWTWjHyv+wyFd44Bcq0kpunTls91z03GkYo/Oxd4KllbfR5D2v6awjrc79wMYL1CcZiKZ941ter6tHOHCwtZRhTqV3Dl42zOKUOCyGcjJnVzJre1QBA7hrn3WB5/fu5kE6/E9ENRWp8ZRJLbU8C2Oogg==';
+ const signatureB64SHA1 =
+ 'UKPzYQivZOavFV3QjOH/B9AwKls9n5hZIzOL+V93Yi7lJ7siNkAA9WZgErtFVpDTN6ngSwvlfP/hXZcS33RcCGBWi1SX+xuwuk2U7bZgdkkw4tIH8zcgiRy8bK0IpMoXmLbApU2QsiNwRDMZq3iQdlaMhlsJh85VI+90SQk7fewseiw5Ui6BIpFSH96gLYjWMDPpwk+0GkhkkVaP5vo+I6mBQryD9YPFRu7JfCrnw2T6gldXlGu0IN326+qajKheAGmPSLWBmeFYhquJ5ipgfQGU/KCNIEUr6hkW8NU0+6EVaZl/A9Fyfs1+8KCQ6HxZ7FGyewQjJIx3a8XvBM5vDg==';
+ const dummySignRequest =
+ 'PHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iXzgwOTcwN2YwMDMwYTVkMDA2MjBjOWQ5ZGY5N2Y2MjdhZmU5ZGNjMjQiIFZlcnNpb249IjIuMCIgUHJvdmlkZXJOYW1lPSJTUCB0ZXN0IiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDctMTZUMjM6NTI6NDVaIiBEZXN0aW5hdGlvbj0iaHR0cDovL2lkcC5leGFtcGxlLmNvbS9TU09TZXJ2aWNlLnBocCIgUHJvdG9jb2xCaW5kaW5nPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YmluZGluZ3M6SFRUUC1QT1NUIiBBc3NlcnRpb25Db25zdW1lclNlcnZpY2VVUkw9Imh0dHBzOi8vc3AuZXhhbXBsZS5vcmcvc3Avc3NvIj48c2FtbDpJc3N1ZXIgSWQ9Il8wIj5odHRwczovL3NwLmV4YW1wbGUub3JnL21ldGFkYXRhPC9zYW1sOklzc3Vlcj48c2FtbHA6TmFtZUlEUG9saWN5IEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIiBBbGxvd0NyZWF0ZT0idHJ1ZSIvPjxzYW1scDpSZXF1ZXN0ZWRBdXRobkNvbnRleHQgQ29tcGFyaXNvbj0iZXhhY3QiPjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbHA6UmVxdWVzdGVkQXV0aG5Db250ZXh0PjxTaWduYXR1cmUgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxTaWduZWRJbmZvPjxDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PFNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPjxSZWZlcmVuY2UgVVJJPSIjXzAiPjxUcmFuc2Zvcm1zPjxUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L1RyYW5zZm9ybXM+PERpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PERpZ2VzdFZhbHVlPnRRRGlzQlhLVFErOU9YSk81cjdLdUpnYStLST08L0RpZ2VzdFZhbHVlPjwvUmVmZXJlbmNlPjwvU2lnbmVkSW5mbz48U2lnbmF0dXJlVmFsdWU+b3hSa3ZhdTdVdllnRkVaN1lOQVVOZjMwNjdWN1RuNUM5WFNJaWV0MWFadzJGWWV2Tlc1YlV5LzBteHAzYWo2QXZmRmpubXB6QWI4OEJqZHdBejJCRXJEVG9tUmN1WkI3TGIwZllUZjMxTjJvWk9YME1pUGlRT0g1NEk2M3FKVzRYbzNWcWRGN0dCdUZaWkh5bGxmU0J2N2dmQ3RqSkR3RlNDeldLNzBCOXIzY0ZNUkpaTGhDSjlvUGVuKzRVOXNjU1lPNmcrc3pCWkxsNkFpSjA2UEhjOGp6RUtHd2ZRcmNaazhrREtVbHZOZkpNVUx5cThkcHgyVnZVQXg0cDVld2ZNT3dCOVczSGwzUFBhMGRPNzd6WmlmM0NnbHBjTjA2ZittNlVZRy93bm9UUUV5S1c5aE9lKzJ2R004MFc3N2VXdTBkbWlhUHVxVDFvazhMWFB1cTFBPT08L1NpZ25hdHVyZVZhbHVlPjxLZXlJbmZvPjxYNTA5RGF0YT48WDUwOUNlcnRpZmljYXRlPk1JSURvekNDQW91Z0F3SUJBZ0lKQUtOc21MOFFiZnB3TUEwR0NTcUdTSWIzRFFFQkN3VUFNR2d4Q3pBSkJnTlZCQVlUQWtoTE1SSXdFQVlEVlFRSURBbEliMjVuSUV0dmJtY3hDekFKQmdOVkJBY01Ba2hMTVJNd0VRWURWUVFLREFwdWIyUmxMWE5oYld3eU1TTXdJUVlKS29aSWh2Y05BUWtCRmhSdWIyUmxMbk5oYld3eVFHZHRZV2xzTG1OdmJUQWVGdzB4TlRBM01EVXhOelUyTkRkYUZ3MHhPREEzTURReE56VTJORGRhTUdneEN6QUpCZ05WQkFZVEFraExNUkl3RUFZRFZRUUlEQWxJYjI1bklFdHZibWN4Q3pBSkJnTlZCQWNNQWtoTE1STXdFUVlEVlFRS0RBcHViMlJsTFhOaGJXd3lNU013SVFZSktvWklodmNOQVFrQkZoUnViMlJsTG5OaGJXd3lRR2R0WVdsc0xtTnZiVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNUUpBQjhKcnNMUWJVdUphOGFrekxxTzFFWnFDbFMwdFFwK3crNXdndWZwMDdXd0duL3NobWE4ZGNRTmoxZGJqc3pJNUhCZVZGak9LSXhsZmptTkI5b3ZoUVBzdEJqUC9VUFFZcDFJcDJJb0hDWVg5SERnTXozeHlYS2JIdGhVelphRUN6K3ArN1d0Z3doY3pSa0JMRE9tMmsxNXFoUFlHUHcwdkgyemJWUkdXVUJTOWR5Mk1wM3RxbFZiUDB4WjlDRE5raENKa1Y5U01OZm9DVlcvVllQcUsyUUJvN2tpNG9ibTV4NWl4RlFTU0hzS2JWQVJWenlRSDVpTmpGZTFUZEFwM3JEd3JFNUxjMU5RbFFheFI1R25iMk5aQXBET1JSWklWbE52MldVZGk5UXZNMHlDempROTBqUDBPQW9nSGhSWWF4ZzAvdmdORXllNDZoK1BpWTBDQXdFQUFhTlFNRTR3SFFZRFZSME9CQllFRkVWa2pjTEFJVG5ka3kwOTBBeTc0UXFDbVFLSU1COEdBMVVkSXdRWU1CYUFGRVZramNMQUlUbmRreTA5MEF5NzRRcUNtUUtJTUF3R0ExVWRFd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRzRsWVgzS1FYZW5lejRMcERuWmhjRkJFWmk5WXN0VUtQRjVFS2QrV3BscFZiY1RRYzFBMy9aK3VIUm15VjhoK3BRemVGNkxpb2IzN0c4N1lwYWNQcGxKSTY2Y2YyUmo3ajhoU0JOYmRyKzY2RTJxcGNFaEFGMWlKbXpCTnloYi95ZGxFdVZwbjgvRXNvUCtIdkJlaURsNWdvbjM1NjJNelpJZ1YvcExkVGZ4SHlXNmh6QVFoakdxMlVoY3ZSK2dYTlZKdkhQMmVTNGpsSG5Ka0I5YmZvMGt2Zjg3UStENlhLWDNxNWMzbU84dHFXNlVwcUhTQyt1TEVwelppTkxldUZhNFRVSWhnQmdqRGpsUnJOREt1OG5kYW5jU24zeUJIWW5xSjJ0OWNSK2NvRm5uallBQlFwTnJ2azRtdG1YWThTWG9CellHOVkrbHFlQXVuNiswWXlFPTwvWDUwOUNlcnRpZmljYXRlPjwvWDUwOURhdGE+PC9LZXlJbmZvPjwvU2lnbmF0dXJlPjwvc2FtbHA6QXV0aG5SZXF1ZXN0Pg==';
+ const dummySignRequestSHA256 =
+ 'PHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iXzgwOTcwN2YwMDMwYTVkMDA2MjBjOWQ5ZGY5N2Y2MjdhZmU5ZGNjMjQiIFZlcnNpb249IjIuMCIgUHJvdmlkZXJOYW1lPSJTUCB0ZXN0IiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDctMTZUMjM6NTI6NDVaIiBEZXN0aW5hdGlvbj0iaHR0cDovL2lkcC5leGFtcGxlLmNvbS9TU09TZXJ2aWNlLnBocCIgUHJvdG9jb2xCaW5kaW5nPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YmluZGluZ3M6SFRUUC1QT1NUIiBBc3NlcnRpb25Db25zdW1lclNlcnZpY2VVUkw9Imh0dHBzOi8vc3AuZXhhbXBsZS5vcmcvc3Avc3NvIj48c2FtbDpJc3N1ZXIgSWQ9Il8wIj5odHRwczovL3NwLmV4YW1wbGUub3JnL21ldGFkYXRhPC9zYW1sOklzc3Vlcj48c2FtbHA6TmFtZUlEUG9saWN5IEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIiBBbGxvd0NyZWF0ZT0idHJ1ZSIvPjxzYW1scDpSZXF1ZXN0ZWRBdXRobkNvbnRleHQgQ29tcGFyaXNvbj0iZXhhY3QiPjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbHA6UmVxdWVzdGVkQXV0aG5Db250ZXh0PjxTaWduYXR1cmUgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxTaWduZWRJbmZvPjxDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PFNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGEyNTYiLz48UmVmZXJlbmNlIFVSST0iI18wIj48VHJhbnNmb3Jtcz48VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9UcmFuc2Zvcm1zPjxEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48RGlnZXN0VmFsdWU+d3VKWlJSdWlGb0FQZVZXVllReXhOWXpjbUpJdXB0dTZmaE10MVZuQVZQbz08L0RpZ2VzdFZhbHVlPjwvUmVmZXJlbmNlPjwvU2lnbmVkSW5mbz48U2lnbmF0dXJlVmFsdWU+V0VUTUtaL1pzTm5pbDVjVCtHeTFKbmJWMVVscUN2N205SlppZ1NLTXFhbFlOL1ZDclMxelpFMkVOekxFSjhCN1ZaVkMyRVJBT2pHL1lHbWJ4Si95K2Z6YVR1bGh0blhrYUZncytmNEdJZDBISDY0MldKRnRBeUg2RS81SUVVWUVXYUk0TzA5MWgvd2EvM2EyNEJZK3R5L0ExSmIxLzM5NXpXVi84NUZETXFNemdVRDdRYkQ4TG5mcThkS1hJZDdQWmdnVnpQTFpvRHo0YXpaL3V4VG9aUkxwKy9XVjZHQy91Y2lLMmVmR1hMb09NMm1wcElDc05qVk9mT1NEM2pXS3BjQk11bDBRMjJZMGFoaXlKWDlFcnZkSEcwV0RMcXI0RXc5TGFqVVNydFovaGNqR1ZIemhZZCs1YklYSXp6ZWlmbUF6Snp4WFM4cmhjNGVoV25OYTJ3PT08L1NpZ25hdHVyZVZhbHVlPjxLZXlJbmZvPjxYNTA5RGF0YT48WDUwOUNlcnRpZmljYXRlPk1JSURvekNDQW91Z0F3SUJBZ0lKQUtOc21MOFFiZnB3TUEwR0NTcUdTSWIzRFFFQkN3VUFNR2d4Q3pBSkJnTlZCQVlUQWtoTE1SSXdFQVlEVlFRSURBbEliMjVuSUV0dmJtY3hDekFKQmdOVkJBY01Ba2hMTVJNd0VRWURWUVFLREFwdWIyUmxMWE5oYld3eU1TTXdJUVlKS29aSWh2Y05BUWtCRmhSdWIyUmxMbk5oYld3eVFHZHRZV2xzTG1OdmJUQWVGdzB4TlRBM01EVXhOelUyTkRkYUZ3MHhPREEzTURReE56VTJORGRhTUdneEN6QUpCZ05WQkFZVEFraExNUkl3RUFZRFZRUUlEQWxJYjI1bklFdHZibWN4Q3pBSkJnTlZCQWNNQWtoTE1STXdFUVlEVlFRS0RBcHViMlJsTFhOaGJXd3lNU013SVFZSktvWklodmNOQVFrQkZoUnViMlJsTG5OaGJXd3lRR2R0WVdsc0xtTnZiVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNUUpBQjhKcnNMUWJVdUphOGFrekxxTzFFWnFDbFMwdFFwK3crNXdndWZwMDdXd0duL3NobWE4ZGNRTmoxZGJqc3pJNUhCZVZGak9LSXhsZmptTkI5b3ZoUVBzdEJqUC9VUFFZcDFJcDJJb0hDWVg5SERnTXozeHlYS2JIdGhVelphRUN6K3ArN1d0Z3doY3pSa0JMRE9tMmsxNXFoUFlHUHcwdkgyemJWUkdXVUJTOWR5Mk1wM3RxbFZiUDB4WjlDRE5raENKa1Y5U01OZm9DVlcvVllQcUsyUUJvN2tpNG9ibTV4NWl4RlFTU0hzS2JWQVJWenlRSDVpTmpGZTFUZEFwM3JEd3JFNUxjMU5RbFFheFI1R25iMk5aQXBET1JSWklWbE52MldVZGk5UXZNMHlDempROTBqUDBPQW9nSGhSWWF4ZzAvdmdORXllNDZoK1BpWTBDQXdFQUFhTlFNRTR3SFFZRFZSME9CQllFRkVWa2pjTEFJVG5ka3kwOTBBeTc0UXFDbVFLSU1COEdBMVVkSXdRWU1CYUFGRVZramNMQUlUbmRreTA5MEF5NzRRcUNtUUtJTUF3R0ExVWRFd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRzRsWVgzS1FYZW5lejRMcERuWmhjRkJFWmk5WXN0VUtQRjVFS2QrV3BscFZiY1RRYzFBMy9aK3VIUm15VjhoK3BRemVGNkxpb2IzN0c4N1lwYWNQcGxKSTY2Y2YyUmo3ajhoU0JOYmRyKzY2RTJxcGNFaEFGMWlKbXpCTnloYi95ZGxFdVZwbjgvRXNvUCtIdkJlaURsNWdvbjM1NjJNelpJZ1YvcExkVGZ4SHlXNmh6QVFoakdxMlVoY3ZSK2dYTlZKdkhQMmVTNGpsSG5Ka0I5YmZvMGt2Zjg3UStENlhLWDNxNWMzbU84dHFXNlVwcUhTQyt1TEVwelppTkxldUZhNFRVSWhnQmdqRGpsUnJOREt1OG5kYW5jU24zeUJIWW5xSjJ0OWNSK2NvRm5uallBQlFwTnJ2azRtdG1YWThTWG9CellHOVkrbHFlQXVuNiswWXlFPTwvWDUwOUNlcnRpZmljYXRlPjwvWDUwOURhdGE+PC9LZXlJbmZvPjwvU2lnbmF0dXJlPjwvc2FtbHA6QXV0aG5SZXF1ZXN0Pg==';
+ const dummySignRequestSHA512 =
+ 'PHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iXzgwOTcwN2YwMDMwYTVkMDA2MjBjOWQ5ZGY5N2Y2MjdhZmU5ZGNjMjQiIFZlcnNpb249IjIuMCIgUHJvdmlkZXJOYW1lPSJTUCB0ZXN0IiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDctMTZUMjM6NTI6NDVaIiBEZXN0aW5hdGlvbj0iaHR0cDovL2lkcC5leGFtcGxlLmNvbS9TU09TZXJ2aWNlLnBocCIgUHJvdG9jb2xCaW5kaW5nPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YmluZGluZ3M6SFRUUC1QT1NUIiBBc3NlcnRpb25Db25zdW1lclNlcnZpY2VVUkw9Imh0dHBzOi8vc3AuZXhhbXBsZS5vcmcvc3Avc3NvIj48c2FtbDpJc3N1ZXIgSWQ9Il8wIj5odHRwczovL3NwLmV4YW1wbGUub3JnL21ldGFkYXRhPC9zYW1sOklzc3Vlcj48c2FtbHA6TmFtZUlEUG9saWN5IEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIiBBbGxvd0NyZWF0ZT0idHJ1ZSIvPjxzYW1scDpSZXF1ZXN0ZWRBdXRobkNvbnRleHQgQ29tcGFyaXNvbj0iZXhhY3QiPjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbHA6UmVxdWVzdGVkQXV0aG5Db250ZXh0PjxTaWduYXR1cmUgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxTaWduZWRJbmZvPjxDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PFNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGE1MTIiLz48UmVmZXJlbmNlIFVSST0iI18wIj48VHJhbnNmb3Jtcz48VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9UcmFuc2Zvcm1zPjxEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGE1MTIiLz48RGlnZXN0VmFsdWU+RWN3emlpSzZmazFNK2RETkpHNVlFeWpGY3Fjc0dzRmZNNGFDUkJKcENWTlltVWs4NWJxQk8rblRFN3RmRnd5Uk1yOUZBODBpSnN3MlFwM3R4QTE1Q2c9PTwvRGlnZXN0VmFsdWU+PC9SZWZlcmVuY2U+PC9TaWduZWRJbmZvPjxTaWduYXR1cmVWYWx1ZT5MVmFYajQ3MlZEalBvQU1hZ1BNcEswdGwvckV1c2llVXc4SXZrVVJmVVJDKzl1YXNqRXgxZjR4S1dkYUJLa09zQUhIZ1RMVlpxUnBNY1RBVnJTWDM5SnN1TmRDZnlycXBZTWlBY0w0RXhTM3dOSXdBenFCY1RiUlgxdEY2Nzk5cENYVXVOTE84NVdyN3FwZG5RTnFkTWc1L0E5a0xzUjFSc2dOeFhtandPM1dKUDhucFJ5dXYrVjJvNXhvN01FOVYyaVE4ODRhWVhnNUJodWQ5S1huSU5TZWw5YjN2NnV6T3V2VlFSM1ZCTlFWUXhRaGNUNlFpZ1BkR1hqZDl0cEU4TXV0UG5ZS1NNbHJKc1Ird2wzV2ZacmhwQ2E4U2JGS0RjNnBja1lmVUJYV3pRVVFJVkpXRm5icXBlemJsSUk2NmtlNlRvSzVseVpiajRSajFEcytjMHc9PTwvU2lnbmF0dXJlVmFsdWU+PEtleUluZm8+PFg1MDlEYXRhPjxYNTA5Q2VydGlmaWNhdGU+TUlJRG96Q0NBb3VnQXdJQkFnSUpBS05zbUw4UWJmcHdNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1HZ3hDekFKQmdOVkJBWVRBa2hMTVJJd0VBWURWUVFJREFsSWIyNW5JRXR2Ym1jeEN6QUpCZ05WQkFjTUFraExNUk13RVFZRFZRUUtEQXB1YjJSbExYTmhiV3d5TVNNd0lRWUpLb1pJaHZjTkFRa0JGaFJ1YjJSbExuTmhiV3d5UUdkdFlXbHNMbU52YlRBZUZ3MHhOVEEzTURVeE56VTJORGRhRncweE9EQTNNRFF4TnpVMk5EZGFNR2d4Q3pBSkJnTlZCQVlUQWtoTE1SSXdFQVlEVlFRSURBbEliMjVuSUV0dmJtY3hDekFKQmdOVkJBY01Ba2hMTVJNd0VRWURWUVFLREFwdWIyUmxMWE5oYld3eU1TTXdJUVlKS29aSWh2Y05BUWtCRmhSdWIyUmxMbk5oYld3eVFHZHRZV2xzTG1OdmJUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQU1RSkFCOEpyc0xRYlV1SmE4YWt6THFPMUVacUNsUzB0UXArdys1d2d1ZnAwN1d3R24vc2htYThkY1FOajFkYmpzekk1SEJlVkZqT0tJeGxmam1OQjlvdmhRUHN0QmpQL1VQUVlwMUlwMklvSENZWDlIRGdNejN4eVhLYkh0aFV6WmFFQ3orcCs3V3Rnd2hjelJrQkxET20yazE1cWhQWUdQdzB2SDJ6YlZSR1dVQlM5ZHkyTXAzdHFsVmJQMHhaOUNETmtoQ0prVjlTTU5mb0NWVy9WWVBxSzJRQm83a2k0b2JtNXg1aXhGUVNTSHNLYlZBUlZ6eVFINWlOakZlMVRkQXAzckR3ckU1TGMxTlFsUWF4UjVHbmIyTlpBcERPUlJaSVZsTnYyV1VkaTlRdk0weUN6alE5MGpQME9Bb2dIaFJZYXhnMC92Z05FeWU0NmgrUGlZMENBd0VBQWFOUU1FNHdIUVlEVlIwT0JCWUVGRVZramNMQUlUbmRreTA5MEF5NzRRcUNtUUtJTUI4R0ExVWRJd1FZTUJhQUZFVmtqY0xBSVRuZGt5MDkwQXk3NFFxQ21RS0lNQXdHQTFVZEV3UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFHNGxZWDNLUVhlbmV6NExwRG5aaGNGQkVaaTlZc3RVS1BGNUVLZCtXcGxwVmJjVFFjMUEzL1ordUhSbXlWOGgrcFF6ZUY2TGlvYjM3Rzg3WXBhY1BwbEpJNjZjZjJSajdqOGhTQk5iZHIrNjZFMnFwY0VoQUYxaUptekJOeWhiL3lkbEV1VnBuOC9Fc29QK0h2QmVpRGw1Z29uMzU2Mk16WklnVi9wTGRUZnhIeVc2aHpBUWhqR3EyVWhjdlIrZ1hOVkp2SFAyZVM0amxIbkprQjliZm8wa3ZmODdRK0Q2WEtYM3E1YzNtTzh0cVc2VXBxSFNDK3VMRXB6WmlOTGV1RmE0VFVJaGdCZ2pEamxSck5ES3U4bmRhbmNTbjN5QkhZbnFKMnQ5Y1IrY29Gbm5qWUFCUXBOcnZrNG10bVhZOFNYb0J6WUc5WStscWVBdW42KzBZeUU9PC9YNTA5Q2VydGlmaWNhdGU+PC9YNTA5RGF0YT48L0tleUluZm8+PC9TaWduYXR1cmU+PC9zYW1scDpBdXRoblJlcXVlc3Q+';
+
+ test('sign a SAML message with RSA-SHA1', (t) => {
+ t.is(
+ libsaml.constructMessageSignature(octetString, _spPrivPem, _spPrivKeyPass).toString('base64'),
+ signatureB64SHA1
+ );
+ });
+ test('sign a SAML message with RSA-SHA256', (t) => {
+ t.is(
+ libsaml
+ .constructMessageSignature(octetStringSHA256, _spPrivPem, _spPrivKeyPass, signatureAlgorithms.RSA_SHA256)
+ .toString('base64'),
+ signatureB64SHA256
+ );
+ });
+ test('sign a SAML message with RSA-SHA512', (t) => {
+ t.is(
+ libsaml
+ .constructMessageSignature(octetStringSHA512, _spPrivPem, _spPrivKeyPass, signatureAlgorithms.RSA_SHA512)
+ .toString('base64'),
+ signatureB64SHA512
+ );
+ });
+ test('verify binary SAML message signed with RSA-SHA1', (t) => {
+ const signature = libsaml.constructMessageSignature(octetString, _spPrivPem, _spPrivKeyPass);
+ t.is(libsaml.verifyMessageSignature(SPMetadata, octetString, signature), true);
+ });
+ test('verify binary SAML message signed with RSA-SHA256', (t) => {
+ const signature = libsaml.constructMessageSignature(
+ octetStringSHA256,
+ _spPrivPem,
+ _spPrivKeyPass,
+ signatureAlgorithms.RSA_SHA256
+ );
+ t.is(
+ libsaml.verifyMessageSignature(SPMetadata, octetStringSHA256, signature, signatureAlgorithms.RSA_SHA256),
+ true
+ );
+ });
+ test('verify binary SAML message signed with RSA-SHA512', (t) => {
+ const signature = libsaml.constructMessageSignature(
+ octetStringSHA512,
+ _spPrivPem,
+ _spPrivKeyPass,
+ signatureAlgorithms.RSA_SHA512
+ );
+ t.is(
+ libsaml.verifyMessageSignature(SPMetadata, octetStringSHA512, signature, signatureAlgorithms.RSA_SHA512),
+ true
+ );
+ });
+ test('verify stringified SAML message signed with RSA-SHA1', (t) => {
+ const signature = libsaml.constructMessageSignature(octetString, _spPrivPem, _spPrivKeyPass).toString('base64');
+ t.is(libsaml.verifyMessageSignature(SPMetadata, octetString, Buffer.from(signature, 'base64')), true);
+ });
+ test('verify stringified SAML message signed with RSA-SHA256', (t) => {
+ const signature = libsaml
+ .constructMessageSignature(octetStringSHA256, _spPrivPem, _spPrivKeyPass)
+ .toString('base64');
+ t.is(libsaml.verifyMessageSignature(SPMetadata, octetStringSHA256, Buffer.from(signature, 'base64')), true);
+ });
+ test('verify stringified SAML message signed with RSA-SHA512', (t) => {
+ const signature = libsaml
+ .constructMessageSignature(octetStringSHA512, _spPrivPem, _spPrivKeyPass)
+ .toString('base64');
+ t.is(libsaml.verifyMessageSignature(SPMetadata, octetStringSHA512, Buffer.from(signature, 'base64')), true);
+ });
+ test('construct signature with RSA-SHA1', (t) => {
+ t.is(
+ libsaml.constructSAMLSignature({
+ rawSamlMessage: _originRequest,
+ referenceTagXPath: libsaml.createXPath('Issuer'),
+ signingCert: SPMetadata.getX509Certificate('signing') as string,
+ privateKey: _spPrivPem,
+ privateKeyPass: _spPrivKeyPass,
+ signatureAlgorithm: signatureAlgorithms.RSA_SHA1,
+ }),
+ dummySignRequest
+ );
+ });
+ test('construct signature with RSA-SHA256', (t) => {
+ t.is(
+ libsaml.constructSAMLSignature({
+ rawSamlMessage: _originRequest,
+ referenceTagXPath: libsaml.createXPath('Issuer'),
+ signingCert: SPMetadata.getX509Certificate('signing') as string,
+ privateKey: _spPrivPem,
+ privateKeyPass: _spPrivKeyPass,
+ signatureAlgorithm: signatureAlgorithms.RSA_SHA256,
+ }),
+ dummySignRequestSHA256
+ );
+ });
+ test('construct signature with RSA-SHA512', (t) => {
+ t.is(
+ libsaml.constructSAMLSignature({
+ rawSamlMessage: _originRequest,
+ referenceTagXPath: libsaml.createXPath('Issuer'),
+ signingCert: SPMetadata.getX509Certificate('signing') as string,
+ privateKey: _spPrivPem,
+ privateKeyPass: _spPrivKeyPass,
+ signatureAlgorithm: signatureAlgorithms.RSA_SHA512,
+ }),
+ dummySignRequestSHA512
+ );
+ });
+ test('verify a XML signature signed by RSA-SHA1 with metadata', (t) => {
+ t.is(libsaml.verifySignature(_decodedResponse, { metadata: IdPMetadata })[0], true);
+ });
+ test('integrity check for request signed with RSA-SHA1', (t) => {
+ try {
+ libsaml.verifySignature(_falseDecodedRequestSHA1, {
+ metadata: SPMetadata,
+ signatureAlgorithm: signatureAlgorithms.RSA_SHA1,
+ });
+ } catch (e) {
+ t.is(isSamlifyError(e), true);
+ t.is(e.code, SamlifyErrorCode.FailedToVerifySignature);
+ }
+ });
+ test('verify a XML signature signed by RSA-SHA256 with metadata', (t) => {
+ t.is(
+ libsaml.verifySignature(_decodedRequestSHA256, {
+ metadata: SPMetadata,
+ signatureAlgorithm: signatureAlgorithms.RSA_SHA256,
+ })[0],
+ true
+ );
+ });
+ test('integrity check for request signed with RSA-SHA256', (t) => {
+ try {
+ libsaml.verifySignature(_falseDecodedRequestSHA256, {
+ metadata: SPMetadata,
+ signatureAlgorithm: signatureAlgorithms.RSA_SHA256,
+ });
+ } catch (e) {
+ t.is(isSamlifyError(e), true);
+ t.is(e.code, SamlifyErrorCode.FailedToVerifySignature);
+ }
+ });
+ test('verify a XML signature signed by RSA-SHA512 with metadata', (t) => {
+ t.is(
+ libsaml.verifySignature(_decodedRequestSHA512, {
+ metadata: SPMetadata,
+ signatureAlgorithm: signatureAlgorithms.RSA_SHA512,
+ })[0],
+ true
+ );
+ });
+ test('integrity check for request signed with RSA-SHA512', (t) => {
+ try {
+ libsaml.verifySignature(_falseDecodedRequestSHA512, {
+ metadata: SPMetadata,
+ signatureAlgorithm: signatureAlgorithms.RSA_SHA512,
+ });
+ } catch (e) {
+ t.is(isSamlifyError(e), true);
+ t.is(e.code, SamlifyErrorCode.FailedToVerifySignature);
+ }
+ });
+
+ test('verify a XML signature with metadata but with rolling certificate', (t) => {
+ const responseSignedByCert1 = String(readFileSync('./test/misc/response_signed_cert1.xml'));
+ const responseSignedByCert2 = String(readFileSync('./test/misc/response_signed_cert2.xml'));
+ t.is(
+ libsaml.verifySignature(responseSignedByCert1, {
+ metadata: idpRollingCert.getEntityMeta(),
+ signatureAlgorithm: signatureAlgorithms.RSA_SHA256,
+ })[0],
+ true
+ );
+ t.is(
+ libsaml.verifySignature(responseSignedByCert2, {
+ metadata: idpRollingCert.getEntityMeta(),
+ signatureAlgorithm: signatureAlgorithms.RSA_SHA256,
+ })[0],
+ true
+ );
+ });
+
+ test('verify a XML signature signed by RSA-SHA1 with .cer keyFile', (t) => {
+ const xml = String(readFileSync('./test/misc/signed_request_sha1.xml'));
+ t.is(libsaml.verifySignature(xml, { keyFile: './test/key/sp/cert.cer' })[0], true);
+ });
+ test('verify a XML signature signed by RSA-SHA256 with .cer keyFile', (t) => {
+ const xml = String(readFileSync('./test/misc/signed_request_sha256.xml'));
+ t.is(libsaml.verifySignature(xml, { keyFile: './test/key/sp/cert.cer' })[0], true);
+ });
+ test('verify a XML signature signed by RSA-SHA512 with .cer keyFile', (t) => {
+ const xml = String(readFileSync('./test/misc/signed_request_sha512.xml'));
+ t.is(libsaml.verifySignature(xml, { keyFile: './test/key/sp/cert.cer' })[0], true);
+ });
+ test('encrypt assertion test passes', async (t) => {
+ await t.notThrowsAsync(() => libsaml.encryptAssertion(idp, sp, sampleSignedResponse));
+ });
+ test('encrypt assertion response without assertion returns error', async (t) => {
+ const error = await t.throwsAsync(() => libsaml.encryptAssertion(idp, sp, wrongResponse));
+ t.is(isSamlifyError(error), true);
+ if (isSamlifyError(error)) t.is(error.code, SamlifyErrorCode.UndefinedAssertion);
+ });
+ test('encrypt assertion with invalid xml syntax returns error', async (t) => {
+ const error = await t.throwsAsync(() => libsaml.encryptAssertion(idp, sp, 'This is not a xml format string'));
+ t.is(isSamlifyError(error), true);
+ if (isSamlifyError(error)) t.is(error.code, SamlifyErrorCode.UndefinedAssertion);
+ });
+ test('encrypt assertion with empty string returns error', async (t) => {
+ const error = await t.throwsAsync(() => libsaml.encryptAssertion(idp, sp, ''));
+ t.is(isSamlifyError(error), true);
+ if (isSamlifyError(error)) t.is(error.code, SamlifyErrorCode.UndefinedAssertion);
+ });
+ test('encrypt assertion with undefined string returns error', async (t) => {
+ const error = await t.throwsAsync(() => libsaml.encryptAssertion(idp, sp, undefined));
+ t.is(isSamlifyError(error), true);
+ if (isSamlifyError(error)) t.is(error.code, SamlifyErrorCode.UndefinedAssertion);
+ });
+ test('building attribute statement with one attribute', (t) => {
+ const attributes = [
+ {
+ name: 'email',
+ valueTag: 'user.email',
+ nameFormat: names.attrnameFormat.basic,
+ valueXsiType: 'xs:string',
+ },
+ ];
+ const expectedStatement =
+ '{attrUserEmail}';
+
+ t.is(libsaml.attributeStatementBuilder(attributes), expectedStatement);
+ });
+ test('building attribute statement with multiple attributes', (t) => {
+ const attributes = [
+ {
+ name: 'email',
+ valueTag: 'user.email',
+ nameFormat: names.attrnameFormat.basic,
+ valueXsiType: 'xs:string',
+ },
+ {
+ name: 'firstname',
+ valueTag: 'user.firstname',
+ nameFormat: names.attrnameFormat.basic,
+ valueXsiType: 'xs:string',
+ },
+ ];
+ const expectedStatement =
+ '{attrUserEmail}{attrUserFirstname}';
+ t.is(libsaml.attributeStatementBuilder(attributes), expectedStatement);
+ });
})();
(() => {
- const baseConfig = {
- signingCert: readFileSync('./test/key/sp/cert.cer'),
- privateKey: readFileSync('./test/key/sp/privkey.pem'),
- privateKeyPass: 'VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px',
- entityID: 'http://sp',
- nameIDFormat: ['urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'],
- assertionConsumerService: [{
- Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
- Location: 'http://sp/acs',
- Index: 1,
- }],
- singleLogoutService: [{
- Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
- Location: 'http://sp/slo',
- Index: 1,
- }],
- };
- test('sp metadata with default elements order', t => {
- t.is(serviceProvider(baseConfig).getMetadata(), 'MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress');
- });
- test('sp metadata with shibboleth elements order', t => {
- const spToShib = serviceProvider(Object.assign({}, baseConfig, { elementsOrder: ref.elementsOrder.shibboleth }));
- t.is(spToShib.getMetadata(), 'MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress');
- });
-
+ const baseConfig = {
+ signingCert: readFileSync('./test/key/sp/cert.cer'),
+ privateKey: readFileSync('./test/key/sp/privkey.pem'),
+ privateKeyPass: 'VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px',
+ entityID: 'http://sp',
+ nameIDFormat: ['urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'],
+ assertionConsumerService: [
+ {
+ Binding: BindingNamespace.Post,
+ Location: 'http://sp/acs',
+ Index: 1,
+ },
+ ],
+ singleLogoutService: [
+ {
+ Binding: BindingNamespace.Redirect,
+ Location: 'http://sp/slo',
+ Index: 1,
+ },
+ ],
+ };
+ test('sp metadata with default elements order', (t) => {
+ t.is(
+ serviceProvider(baseConfig).getMetadata(),
+ 'MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
+ );
+ });
+ test('sp metadata with shibboleth elements order', (t) => {
+ const spToShib = serviceProvider(Object.assign({}, baseConfig, { elementsOrder: elementsOrder.shibboleth }));
+ t.is(
+ spToShib.getMetadata(),
+ 'MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
+ );
+ });
})();
-test('verify time with and without drift tolerance', t => {
-
- const now = new Date();
- const timeBefore10Mins = new Date(new Date().setMinutes(now.getMinutes() - 10)).toISOString();
- const timeBefore5Mins = new Date(new Date().setMinutes(now.getMinutes() - 5)).toISOString();
- const timeAfter5Mins = new Date(new Date().setMinutes(now.getMinutes() + 5)).toISOString();
- const timeAfter10Mins = new Date(new Date().setMinutes(now.getMinutes() + 5)).toISOString();
-
- // without drift tolerance
- t.true(verifyTime(timeBefore5Mins, timeAfter5Mins));
- t.true(verifyTime(timeBefore5Mins, undefined));
- t.true(verifyTime(undefined, timeAfter5Mins));
-
- t.false(verifyTime(undefined, timeBefore5Mins));
- t.false(verifyTime(timeAfter5Mins, undefined));
- t.false(verifyTime(timeBefore10Mins, timeBefore5Mins));
- t.false(verifyTime(timeAfter5Mins, timeAfter10Mins));
-
- t.true(verifyTime(undefined, undefined));
-
- // with drift tolerance 5 mins + 1 sec = 301,000 ms
- const drifts: [number, number] = [-301000, 301000];
- t.true(verifyTime(timeBefore5Mins, timeAfter5Mins, drifts));
- t.true(verifyTime(timeBefore5Mins, undefined, drifts));
- t.true(verifyTime(undefined, timeAfter5Mins, drifts));
-
- t.true(verifyTime(undefined, timeBefore5Mins, drifts));
- t.true(verifyTime(timeAfter5Mins, undefined, drifts));
- t.true(verifyTime(timeBefore10Mins, timeBefore5Mins, drifts));
- t.true(verifyTime(timeAfter5Mins, timeAfter10Mins, drifts));
-
- t.true(verifyTime(undefined, undefined, drifts));
+test('verify time with and without drift tolerance', (t) => {
+ const now = new Date();
+ const timeBefore10Mins = new Date(new Date().setMinutes(now.getMinutes() - 10)).toISOString();
+ const timeBefore5Mins = new Date(new Date().setMinutes(now.getMinutes() - 5)).toISOString();
+ const timeAfter5Mins = new Date(new Date().setMinutes(now.getMinutes() + 5)).toISOString();
+ const timeAfter10Mins = new Date(new Date().setMinutes(now.getMinutes() + 5)).toISOString();
+
+ // without drift tolerance
+ t.true(verifyTime(timeBefore5Mins, timeAfter5Mins));
+ t.true(verifyTime(timeBefore5Mins, undefined));
+ t.true(verifyTime(undefined, timeAfter5Mins));
+
+ t.false(verifyTime(undefined, timeBefore5Mins));
+ t.false(verifyTime(timeAfter5Mins, undefined));
+ t.false(verifyTime(timeBefore10Mins, timeBefore5Mins));
+ t.false(verifyTime(timeAfter5Mins, timeAfter10Mins));
+
+ t.true(verifyTime(undefined, undefined));
+
+ // with drift tolerance 5 mins + 1 sec = 301,000 ms
+ const drifts: [number, number] = [-301000, 301000];
+ t.true(verifyTime(timeBefore5Mins, timeAfter5Mins, drifts));
+ t.true(verifyTime(timeBefore5Mins, undefined, drifts));
+ t.true(verifyTime(undefined, timeAfter5Mins, drifts));
+
+ t.true(verifyTime(undefined, timeBefore5Mins, drifts));
+ t.true(verifyTime(timeAfter5Mins, undefined, drifts));
+ t.true(verifyTime(timeBefore10Mins, timeBefore5Mins, drifts));
+ t.true(verifyTime(timeAfter5Mins, timeAfter10Mins, drifts));
+
+ t.true(verifyTime(undefined, undefined, drifts));
});
-
-test('metadata with multiple entity descriptors is invalid', t => {
- try {
- identityProvider({ ...defaultIdpConfig, metadata: readFileSync('./test/misc/multiple_entitydescriptor.xml') });
- t.fail();
- } catch ({ message }) {
- t.is(message, 'ERR_MULTIPLE_METADATA_ENTITYDESCRIPTOR');
- }
+test('metadata with multiple entity descriptors is invalid', (t) => {
+ try {
+ identityProvider({ ...defaultIdpConfig, metadata: readFileSync('./test/misc/multiple_entitydescriptor.xml') });
+ t.fail();
+ } catch (e) {
+ t.is(isSamlifyError(e), true);
+ t.is(e.code, SamlifyErrorCode.MultipleMetadataEntityDescriptor);
+ }
});
-test('undefined x509 key in metadata should return null', t => {
- t.is(idp.entityMeta.getX509Certificate('undefined'), null);
- t.is(sp.entityMeta.getX509Certificate('undefined'), null);
+test('undefined x509 key in metadata should return null', (t) => {
+ t.is(idp.getEntityMeta().getX509Certificate('undefined'), null);
+ t.is(sp.getEntityMeta().getX509Certificate('undefined'), null);
});
-test('return list of x509 key in metadata when multiple keys are used', t => {
- t.is(Array.isArray(idpRollingCert.entityMeta.getX509Certificate('signing')), true);
- t.is(idpRollingCert.entityMeta.getX509Certificate('signing').length, 2);
- t.is(typeof idpRollingCert.entityMeta.getX509Certificate('encryption'), 'string');
+test('return list of x509 key in metadata when multiple keys are used', (t) => {
+ t.is(Array.isArray(idpRollingCert.getEntityMeta().getX509Certificate('signing')), true);
+ t.is(idpRollingCert.getEntityMeta().getX509Certificate('signing').length, 2);
+ t.is(typeof idpRollingCert.getEntityMeta().getX509Certificate('encryption'), 'string');
});
-test('get name id format in metadata', t => {
- t.is(sp.entityMeta.getNameIDFormat(), 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress');
- t.is(Array.isArray(idp.entityMeta.getNameIDFormat()), true);
+test('get name id format in metadata', (t) => {
+ t.is(sp.getEntityMeta().getNameIDFormat(), 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress');
+ t.is(Array.isArray(idp.getEntityMeta().getNameIDFormat()), true);
});
-test('get entity setting', t => {
- t.is(typeof idp.getEntitySetting(), 'object');
- t.is(typeof sp.getEntitySetting(), 'object');
+test('get entity setting', (t) => {
+ t.is(typeof idp.getEntitySettings(), 'object');
+ t.is(typeof sp.getEntitySettings(), 'object');
});
-test('contains shared certificate for both signing and encryption in metadata', t => {
- const metadata = idpMetadata(readFileSync('./test/misc/idpmeta_share_cert.xml'));
- const signingCertificate = metadata.getX509Certificate('signing');
- const encryptionCertificate = metadata.getX509Certificate('encryption');
- t.not(signingCertificate, null);
- t.not(encryptionCertificate, null);
- t.is(signingCertificate, encryptionCertificate);
+test('contains shared certificate for both signing and encryption in metadata', (t) => {
+ const metadata = metadataIdp(readFileSync('./test/misc/idpmeta_share_cert.xml'));
+ const signingCertificate = metadata.getX509Certificate('signing');
+ const encryptionCertificate = metadata.getX509Certificate('encryption');
+ t.not(signingCertificate, null);
+ t.not(encryptionCertificate, null);
+ t.is(signingCertificate, encryptionCertificate);
});
-test('contains explicit certificate declaration for signing and encryption in metadata', t => {
- const signingCertificate = IdPMetadata.getX509Certificate('signing');
- const encryptionCertificate = IdPMetadata.getX509Certificate('encryption');
- t.not(signingCertificate, null);
- t.not(encryptionCertificate, null);
- t.not(signingCertificate, encryptionCertificate);
-});
\ No newline at end of file
+test('contains explicit certificate declaration for signing and encryption in metadata', (t) => {
+ const signingCertificate = IdPMetadata.getX509Certificate('signing');
+ const encryptionCertificate = IdPMetadata.getX509Certificate('encryption');
+ t.not(signingCertificate, null);
+ t.not(encryptionCertificate, null);
+ t.not(signingCertificate, encryptionCertificate);
+});
diff --git a/test/issues.ts b/test/issues.ts
index f9130321..53b0fe82 100644
--- a/test/issues.ts
+++ b/test/issues.ts
@@ -1,167 +1,183 @@
-import esaml2 = require('../index');
-import { readFileSync, writeFileSync } from 'fs';
+/* eslint-disable @typescript-eslint/no-unsafe-call */
import test from 'ava';
-import * as fs from 'fs';
-import * as url from 'url';
-import { DOMParser as dom } from 'xmldom';
+import { readFileSync } from 'fs';
import { xpath as select } from 'xml-crypto';
-import { extract } from '../src/extractor';
-
-const {
- IdentityProvider: identityProvider,
- ServiceProvider: serviceProvider,
- IdPMetadata: idpMetadata,
- SPMetadata: spMetadata,
- Utility: utility,
- SamlLib: libsaml,
- Constants: ref,
-} = esaml2;
+import { DOMParser as dom } from 'xmldom';
+import { identityProvider, serviceProvider } from '../src';
+import { isSamlifyError, SamlifyErrorCode } from '../src/error';
+import { extract, isElement } from '../src/extractor';
+import libsaml from '../src/libsaml';
+import { BindingNamespace, wording } from '../src/urn';
+import { inflateString } from '../src/utility';
const getQueryParamByType = libsaml.getQueryParamByType;
-const wording = ref.wording;
-test('#31 query param for sso/slo is SamlRequest', t => {
- t.is(getQueryParamByType('SAMLRequest'), wording.urlParams.samlRequest);
- t.is(getQueryParamByType('LogoutRequest'), wording.urlParams.samlRequest);
+test('#31 query param for sso/slo is SamlRequest', (t) => {
+ t.is(getQueryParamByType('SAMLRequest'), wording.urlParams.samlRequest);
+ t.is(getQueryParamByType('LogoutRequest'), wording.urlParams.samlRequest);
});
-test('#31 query param for sso/slo is SamlResponse', t => {
- t.is(getQueryParamByType('SAMLResponse'), wording.urlParams.samlResponse);
- t.is(getQueryParamByType('LogoutResponse'), wording.urlParams.samlResponse);
+test('#31 query param for sso/slo is SamlResponse', (t) => {
+ t.is(getQueryParamByType('SAMLResponse'), wording.urlParams.samlResponse);
+ t.is(getQueryParamByType('LogoutResponse'), wording.urlParams.samlResponse);
});
-test('#31 query param for sso/slo returns error', t => {
- try {
- getQueryParamByType('samlRequest');
- t.fail();
- } catch (e) {
- t.pass();
- }
+test('#31 query param for sso/slo returns error', (t) => {
+ try {
+ getQueryParamByType('samlRequest');
+ t.fail();
+ } catch (e) {
+ t.pass();
+ }
});
(() => {
- const spcfg = {
- entityID: 'sp.example.com',
- nameIDFormat: ['urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'],
- assertionConsumerService: [{
- Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
- Location: 'sp.example.com/acs',
- }, {
- Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
- Location: 'sp.example.com/acs',
- }],
- singleLogoutService: [{
- Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
- Location: 'sp.example.com/slo',
- }, {
- Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
- Location: 'sp.example.com/slo',
- }],
- };
- const idpcfg = {
- entityID: 'idp.example.com',
- nameIDFormat: ['urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'],
- singleSignOnService: [{
- Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
- Location: 'idp.example.com/sso',
- }, {
- Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
- Location: 'idp.example.com/sso',
- }],
- singleLogoutService: [{
- Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
- Location: 'idp.example.com/sso/slo',
- }, {
- Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
- Location: 'idp.example.com/sso/slo',
- }],
- };
- const idp = identityProvider(idpcfg);
- const sp = serviceProvider(spcfg);
- const spxml = sp.getMetadata();
- const idpxml = idp.getMetadata();
- const acs = extract(spxml, [
- {
- key: 'assertionConsumerService',
- localPath: ['EntityDescriptor', 'SPSSODescriptor', 'AssertionConsumerService'],
- attributes: ['Binding', 'Location', 'isDefault', 'index'],
- }
- ]);
- const spslo = extract(spxml, [
- {
- key: 'singleLogoutService',
- localPath: ['EntityDescriptor', 'SPSSODescriptor', 'SingleLogoutService'],
- attributes: ['Binding', 'Location', 'isDefault', 'index'],
- }
- ]);
- const sso = extract(idpxml, [
- {
- key: 'singleSignOnService',
- localPath: ['EntityDescriptor', 'IDPSSODescriptor', 'SingleSignOnService'],
- attributes: ['Binding', 'Location', 'isDefault', 'index'],
- }
- ]);
- const idpslo = extract(idpxml, [
- {
- key: 'singleLogoutService',
- localPath: ['EntityDescriptor', 'IDPSSODescriptor', 'SingleLogoutService'],
- attributes: ['Binding', 'Location', 'isDefault', 'index'],
- }
- ]);
- const sp98 = serviceProvider({ metadata: fs.readFileSync('./test/misc/sp_metadata_98.xml') });
- test('#33 sp metadata acs index should be increased by 1', t => {
- t.is(acs.assertionConsumerService.length, 2);
- t.is(acs.assertionConsumerService[0].index, '0');
- t.is(acs.assertionConsumerService[1].index, '1');
- });
- test('#352 no index attribute for sp SingleLogoutService nodes', t => {
- t.is(spslo.singleLogoutService.length, 2);
- t.is(spslo.singleLogoutService[0].index, undefined);
- t.is(spslo.singleLogoutService[1].index, undefined);
- });
- test('#352 no index attribute for idp SingleSignOnService nodes', t => {
- t.is(sso.singleSignOnService.length, 2);
- t.is(sso.singleSignOnService[0].index, undefined);
- t.is(sso.singleSignOnService[1].index, undefined);
- });
- test('#352 no index attribute for idp SingleLogoutService nodes', t => {
- t.is(idpslo.singleLogoutService.length, 2);
- t.is(idpslo.singleLogoutService[0].index, undefined);
- t.is(idpslo.singleLogoutService[1].index, undefined);
- });
- test('#86 duplicate issuer throws error', t => {
- const xml = readFileSync('./test/misc/dumpes_issuer_response.xml');
- const { issuer } = extract(xml.toString(), [{
- key: 'issuer',
- localPath: [
- ['Response', 'Issuer'],
- ['Response', 'Assertion', 'Issuer']
- ],
- attributes: []
- }]);
- t.is(issuer.length, 1);
- t.is(issuer.every(i => i === 'http://www.okta.com/dummyIssuer'), true);
- });
+ const spcfg = {
+ entityID: 'sp.example.com',
+ nameIDFormat: ['urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'],
+ assertionConsumerService: [
+ {
+ Binding: BindingNamespace.Post,
+ Location: 'sp.example.com/acs',
+ },
+ {
+ Binding: BindingNamespace.Redirect,
+ Location: 'sp.example.com/acs',
+ },
+ ],
+ singleLogoutService: [
+ {
+ Binding: BindingNamespace.Post,
+ Location: 'sp.example.com/slo',
+ },
+ {
+ Binding: BindingNamespace.Redirect,
+ Location: 'sp.example.com/slo',
+ },
+ ],
+ };
+ const idpcfg = {
+ entityID: 'idp.example.com',
+ nameIDFormat: ['urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'],
+ singleSignOnService: [
+ {
+ Binding: BindingNamespace.Post,
+ Location: 'https://idp.example.com/sso',
+ },
+ {
+ Binding: BindingNamespace.Redirect,
+ Location: 'https://idp.example.com/sso',
+ },
+ ],
+ singleLogoutService: [
+ {
+ Binding: BindingNamespace.Post,
+ Location: 'https://idp.example.com/sso/slo',
+ },
+ {
+ Binding: BindingNamespace.Redirect,
+ Location: 'https://idp.example.com/sso/slo',
+ },
+ ],
+ };
+ const idp = identityProvider(idpcfg);
+ const sp = serviceProvider(spcfg);
+ const spxml = sp.getMetadata();
+ const idpxml = idp.getMetadata();
+ const acs = extract(spxml, [
+ {
+ key: 'assertionConsumerService',
+ localPath: ['EntityDescriptor', 'SPSSODescriptor', 'AssertionConsumerService'],
+ attributes: ['Binding', 'Location', 'isDefault', 'index'],
+ },
+ ]);
+ const spslo = extract(spxml, [
+ {
+ key: 'singleLogoutService',
+ localPath: ['EntityDescriptor', 'SPSSODescriptor', 'SingleLogoutService'],
+ attributes: ['Binding', 'Location', 'isDefault', 'index'],
+ },
+ ]);
+ const sso = extract(idpxml, [
+ {
+ key: 'singleSignOnService',
+ localPath: ['EntityDescriptor', 'IDPSSODescriptor', 'SingleSignOnService'],
+ attributes: ['Binding', 'Location', 'isDefault', 'index'],
+ },
+ ]);
+ const idpslo = extract(idpxml, [
+ {
+ key: 'singleLogoutService',
+ localPath: ['EntityDescriptor', 'IDPSSODescriptor', 'SingleLogoutService'],
+ attributes: ['Binding', 'Location', 'isDefault', 'index'],
+ },
+ ]);
+ const sp98 = serviceProvider({ metadata: readFileSync('./test/misc/sp_metadata_98.xml') });
+ test('#33 sp metadata acs index should be increased by 1', (t) => {
+ t.is(acs.assertionConsumerService.length, 2);
+ t.is(acs.assertionConsumerService[0].index, '0');
+ t.is(acs.assertionConsumerService[1].index, '1');
+ });
+ test('#352 no index attribute for sp SingleLogoutService nodes', (t) => {
+ t.is(spslo.singleLogoutService.length, 2);
+ t.is(spslo.singleLogoutService[0].index, undefined);
+ t.is(spslo.singleLogoutService[1].index, undefined);
+ });
+ test('#352 no index attribute for idp SingleSignOnService nodes', (t) => {
+ t.is(sso.singleSignOnService.length, 2);
+ t.is(sso.singleSignOnService[0].index, undefined);
+ t.is(sso.singleSignOnService[1].index, undefined);
+ });
+ test('#352 no index attribute for idp SingleLogoutService nodes', (t) => {
+ t.is(idpslo.singleLogoutService.length, 2);
+ t.is(idpslo.singleLogoutService[0].index, undefined);
+ t.is(idpslo.singleLogoutService[1].index, undefined);
+ });
+ test('#86 duplicate issuer throws error', (t) => {
+ const xml = readFileSync('./test/misc/dumpes_issuer_response.xml');
+ const { issuer } = extract(xml.toString(), [
+ {
+ key: 'issuer',
+ localPath: [
+ ['Response', 'Issuer'],
+ ['Response', 'Assertion', 'Issuer'],
+ ],
+ attributes: [],
+ },
+ ]);
+ t.is(issuer.length, 1);
+ t.is(
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+ issuer.every((i: any) => i === 'http://www.okta.com/dummyIssuer'),
+ true
+ );
+ });
+
+ test('#87 add existence check for signature verification', (t) => {
+ try {
+ libsaml.verifySignature(readFileSync('./test/misc/response.xml').toString(), {});
+ t.fail();
+ } catch (e) {
+ t.is(isSamlifyError(e), true);
+ t.is(e.code, SamlifyErrorCode.ZeroSignature);
+ }
+ });
- test('#87 add existence check for signature verification', t => {
- try {
- libsaml.verifySignature(readFileSync('./test/misc/response.xml').toString(), {});
- t.fail();
- } catch ({ message }) {
- t.is(message, 'ERR_ZERO_SIGNATURE');
- }
- });
+ test('#91 idp gets single sign on service from the metadata', (t) => {
+ t.is(idp.getEntityMeta().getSingleSignOnService(BindingNamespace.Post), 'https://idp.example.com/sso');
+ });
- test('#91 idp gets single sign on service from the metadata', t => {
- t.is(idp.entityMeta.getSingleSignOnService('post'), 'idp.example.com/sso');
- });
-
- test('#98 undefined AssertionConsumerServiceURL with redirect request', t => {
- const { id, context } = sp98.createLoginRequest(idp, 'redirect');
- const originalURL = url.parse(context, true);
- const request = originalURL.query.SAMLRequest as string;
- const rawRequest = utility.inflateString(decodeURIComponent(request));
- const xml = new dom().parseFromString(rawRequest);
- const authnRequest = select(xml, "/*[local-name(.)='AuthnRequest']")[0];
- const index = Object.keys(authnRequest.attributes).find((i: string) => authnRequest.attributes[i].nodeName === 'AssertionConsumerServiceURL') as any;
- t.is(authnRequest.attributes[index].nodeValue, 'https://example.org/response');
- });
-})();
\ No newline at end of file
+ test('#98 undefined AssertionConsumerServiceURL with redirect request', (t) => {
+ const { context } = sp98.createLoginRequest(idp, BindingNamespace.Redirect);
+ const url = new URL(context);
+ const request = url.searchParams.get('SAMLRequest') as string;
+ const rawRequest = inflateString(decodeURIComponent(request));
+ const xml = new dom().parseFromString(rawRequest);
+ const authnRequest = select(xml, "/*[local-name(.)='AuthnRequest']")[0];
+ if (!isElement(authnRequest)) {
+ throw new Error('Not an element!');
+ }
+ const index = Object.keys(authnRequest.attributes).find(
+ (i: any) => authnRequest.attributes[i]?.nodeName === 'AssertionConsumerServiceURL'
+ ) as any;
+ t.is(authnRequest.attributes[index]?.nodeValue, 'https://example.org/response');
+ });
+})();
diff --git a/test/key/idp/README.md b/test/key/idp/README.md
index d9b165c4..6e789d68 100644
--- a/test/key/idp/README.md
+++ b/test/key/idp/README.md
@@ -1,4 +1,4 @@
## Support the use case of rolling certificate
-* `privkey1.pem` - password protected `q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW`, it generates `cert.cer`
-* `privkey2.pem` - no password protected, it generates `cert2.cer`
\ No newline at end of file
+- `privkey1.pem` - password protected `q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW`, it generates `cert.cer`
+- `privkey2.pem` - no password protected, it generates `cert2.cer`
diff --git a/test/misc/dumpes_issuer_response.xml b/test/misc/dumpes_issuer_response.xml
index 5964bfaf..b844c8e4 100644
--- a/test/misc/dumpes_issuer_response.xml
+++ b/test/misc/dumpes_issuer_response.xml
@@ -1,20 +1,28 @@
-
-
- http://www.okta.com/dummyIssuer
+
+
+ http://www.okta.com/dummyIssuer
-
-
+
+
-
+
-
+
-
+
Spr+5HzbZxSt8I3vCY4rTBu+glE=
@@ -29,17 +37,22 @@
-
+
-
- http://www.okta.com/dummyIssuer
+
+ http://www.okta.com/dummyIssuer
email@email.com
-
+
@@ -55,8 +68,11 @@
-
+
email@email.com
diff --git a/test/misc/failed_response.xml b/test/misc/failed_response.xml
index 67b258cd..bd23d36c 100644
--- a/test/misc/failed_response.xml
+++ b/test/misc/failed_response.xml
@@ -1 +1,13 @@
-https://idp.example.com/metadata
\ No newline at end of file
+https://idp.example.com/metadata
diff --git a/test/misc/false_signed_request_sha1.xml b/test/misc/false_signed_request_sha1.xml
index c640fe36..f3f3b6f4 100644
--- a/test/misc/false_signed_request_sha1.xml
+++ b/test/misc/false_signed_request_sha1.xml
@@ -1 +1,25 @@
-https://sp.example.org/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordtQDisBXKTQ+9OXJO5r7KuJga+KI=oxRkvau7UvYgFEZ7YNAUNf3067V7Tn5C9XSIiet1aZw2FYevNW5bUy/0mxp3aj6AvfFjnmpzAb88BjdwAz2BErDTomRcuZB7Lb0fYTf31N2oZOX0MiPiQOH54I63qJW4Xo3VqdF7GBuFZZHyllfSBv7gfCtjJDwFSCzWK70B9r3cFMRJZLhCJ9oPen+4U9scSYO6g+szBZLl6AiJ06PHc8jzEKGwfQrcZk8kDKUlvNfJMULyq8dpx2VvUAx4p5ewfMOwB9W3Hl3PPa0dO77zZif3CglpcN06f+m6UYG/wnoTQEyKW9hOe+2vGM80W77eWu0dmiaPuqTok8LXPuq1A==MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=
+https://sp.example.org/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordtQDisBXKTQ+9OXJO5r7KuJga+KI=oxRkvau7UvYgFEZ7YNAUNf3067V7Tn5C9XSIiet1aZw2FYevNW5bUy/0mxp3aj6AvfFjnmpzAb88BjdwAz2BErDTomRcuZB7Lb0fYTf31N2oZOX0MiPiQOH54I63qJW4Xo3VqdF7GBuFZZHyllfSBv7gfCtjJDwFSCzWK70B9r3cFMRJZLhCJ9oPen+4U9scSYO6g+szBZLl6AiJ06PHc8jzEKGwfQrcZk8kDKUlvNfJMULyq8dpx2VvUAx4p5ewfMOwB9W3Hl3PPa0dO77zZif3CglpcN06f+m6UYG/wnoTQEyKW9hOe+2vGM80W77eWu0dmiaPuqTok8LXPuq1A==MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=
diff --git a/test/misc/false_signed_request_sha256.xml b/test/misc/false_signed_request_sha256.xml
index e385bd29..dc3a2aa6 100644
--- a/test/misc/false_signed_request_sha256.xml
+++ b/test/misc/false_signed_request_sha256.xml
@@ -1 +1,23 @@
-http://localhost:4002/sso/metadata9Ftynct5x7o+SdQM9iie2Z8VzZW95OTtXh4BD4O/HP8=EjY0qRy8tJeSANz3uINpdyFmCISiid4vl3KtszPa1mLvx1wGO2RJiFW8Sa18JOS0l8rYP2gwoUYmxU5WS/Cl1QEMlDj46fPpOjEBELGXdKW69zpAHa5jM/FtS8RCixhiMI1dmbL3+zgziEVdx5xrkaakqvpdDD601Eyn0gy1oO+VUmCMPFE6YjsPeFDhw5ZXf7MmJ/fXLeqWmH5Pn+mkyTCZWxi/L+2nG9iayZ41Z3wBl67XTdBL6rwHMcEY7oxwFSZtKTbtTOV6aW11KdAd9peLIsHeqoaMCY/VypS2bTr9FubQCbHhho2vbhX8cuUfpE21OefA7o1rA==MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=
+http://localhost:4002/sso/metadata9Ftynct5x7o+SdQM9iie2Z8VzZW95OTtXh4BD4O/HP8=EjY0qRy8tJeSANz3uINpdyFmCISiid4vl3KtszPa1mLvx1wGO2RJiFW8Sa18JOS0l8rYP2gwoUYmxU5WS/Cl1QEMlDj46fPpOjEBELGXdKW69zpAHa5jM/FtS8RCixhiMI1dmbL3+zgziEVdx5xrkaakqvpdDD601Eyn0gy1oO+VUmCMPFE6YjsPeFDhw5ZXf7MmJ/fXLeqWmH5Pn+mkyTCZWxi/L+2nG9iayZ41Z3wBl67XTdBL6rwHMcEY7oxwFSZtKTbtTOV6aW11KdAd9peLIsHeqoaMCY/VypS2bTr9FubQCbHhho2vbhX8cuUfpE21OefA7o1rA==MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=
diff --git a/test/misc/false_signed_request_sha512.xml b/test/misc/false_signed_request_sha512.xml
index 3d690ab4..f819532a 100644
--- a/test/misc/false_signed_request_sha512.xml
+++ b/test/misc/false_signed_request_sha512.xml
@@ -1 +1,23 @@
-http://localhost:4002/sso/metadata9Ftynct5x7o+SdQM9iie2Z8VzZW95OTtXh4BD4O/HP8=dk+CI6UvXgsM0cHAGAz/Y3gbvehbab92i1jEmDH0QB7d6/3l7j7TuOEvUFnmtwa0kwpigwpySwXybfiuvgdSBmhejwng5m28bYqaIA8FgCWe/BkBVL5BYeQH03gPbnqhBpC5EXUe52FtOlGAoTGNqaD0pyrshoGiOj/OzqVZC7RSBvvYt5iwpLyqj4KIFFao4yNAfIs2n7RwfcbGg3I2m2b5nuhVppRdzzukdQiLdDCuATPDxKJ3KdETbHb3yss+8L2iDPcAoqsZ+UTZ8VI5DhrQBcarcIe8Xp2FUKQnC4n0AEqCpb87l6txPz7GYDaw9yMqe2xD5LPWQ6/2guvqw==MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=
+http://localhost:4002/sso/metadata9Ftynct5x7o+SdQM9iie2Z8VzZW95OTtXh4BD4O/HP8=dk+CI6UvXgsM0cHAGAz/Y3gbvehbab92i1jEmDH0QB7d6/3l7j7TuOEvUFnmtwa0kwpigwpySwXybfiuvgdSBmhejwng5m28bYqaIA8FgCWe/BkBVL5BYeQH03gPbnqhBpC5EXUe52FtOlGAoTGNqaD0pyrshoGiOj/OzqVZC7RSBvvYt5iwpLyqj4KIFFao4yNAfIs2n7RwfcbGg3I2m2b5nuhVppRdzzukdQiLdDCuATPDxKJ3KdETbHb3yss+8L2iDPcAoqsZ+UTZ8VI5DhrQBcarcIe8Xp2FUKQnC4n0AEqCpb87l6txPz7GYDaw9yMqe2xD5LPWQ6/2guvqw==MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=
diff --git a/test/misc/idpmeta.xml b/test/misc/idpmeta.xml
index e81c9889..2ed24d7d 100644
--- a/test/misc/idpmeta.xml
+++ b/test/misc/idpmeta.xml
@@ -1,34 +1,52 @@
-
-
-
-
-
- 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
-
-
-
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
- urn:oasis:names:tc:SAML:2.0:nameid-format:entity
- urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
- urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos
- urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
-
-
-
-
-
-
+
+
+
+
+
+ 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
+
+
+
+ urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
+ urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
+ urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+ urn:oasis:names:tc:SAML:2.0:nameid-format:entity
+ urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
+ urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos
+ urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName
+ urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
+
+
+
+
+
+
diff --git a/test/misc/idpmeta_nosign.xml b/test/misc/idpmeta_nosign.xml
index a30a4284..766c3849 100644
--- a/test/misc/idpmeta_nosign.xml
+++ b/test/misc/idpmeta_nosign.xml
@@ -1,27 +1,43 @@
-
-
-
-
-
- MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=
-
-
-
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
- urn:oasis:names:tc:SAML:2.0:nameid-format:entity
- urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
- urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos
- urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
-
-
-
-
-
-
+
+
+
+
+
+ MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=
+
+
+
+ urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
+ urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
+ urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+ urn:oasis:names:tc:SAML:2.0:nameid-format:entity
+ urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
+ urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos
+ urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName
+ urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
+
+
+
+
+
+
diff --git a/test/misc/idpmeta_onelogoutservice.xml b/test/misc/idpmeta_onelogoutservice.xml
index c6d3af95..53a9e0a1 100644
--- a/test/misc/idpmeta_onelogoutservice.xml
+++ b/test/misc/idpmeta_onelogoutservice.xml
@@ -1,33 +1,48 @@
-
-
-
-
-
- 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
-
-
-
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
- urn:oasis:names:tc:SAML:2.0:nameid-format:entity
- urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
- urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos
- urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
-
-
-
-
-
+
+
+
+
+
+ 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
+
+
+
+ urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
+ urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
+ urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+ urn:oasis:names:tc:SAML:2.0:nameid-format:entity
+ urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
+ urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos
+ urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName
+ urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
+
+
+
+
+
diff --git a/test/misc/idpmeta_rollingcert.xml b/test/misc/idpmeta_rollingcert.xml
index bf8da7dc..0906b9c9 100644
--- a/test/misc/idpmeta_rollingcert.xml
+++ b/test/misc/idpmeta_rollingcert.xml
@@ -1,25 +1,33 @@
-
-
+
+
- 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
+ MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=
- 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
+ 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
- MIID6TCCAtGgAwIBAgIJAPQQPsolUypeMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxFTATBgNVBAoTDGV4cHJlc3Mtc2FtbDEMMAoGA1UECxMDZGV2MQ4wDAYDVQQDEwVlc2FtbDAeFw0xNTEwMDMwMzU3MzRaFw0xODEwMDIwMzU3MzRaMFYxCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxFTATBgNVBAoTDGV4cHJlc3Mtc2FtbDEMMAoGA1UECxMDZGV2MQ4wDAYDVQQDEwVlc2FtbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL7dF1gUNu8en0fHMSbzf192uB8m2CTeHeEeYrmq5rau6t1WzaHwbSStd9tJ/11Arm8f8zfefFqEBA0EYbp/DMqHb9ZiLGgIff08679NOYeK/d9EAs5DzvTMTR6QqG7a4vH3jKOksIbjM35h5RVitVDxo+xWDKyvOpuNE64bJlWHOEiNxvwmcHfJ2hAd1EozaRLcJOojFHg51alUqiNIZ+vpkMAM8s3lUlcYETKqTpcnsE7c1QX60cCrFN4m3SNS98HGBEdotch8+2Myzz957cBiwg9CR05PtEfjH0gGXJbL56JmpPyY+TkEiNMtMqJ7RNkK92gZfoY2i3RdjLKOHDUCAwEAAaOBuTCBtjAdBgNVHQ4EFgQUm4zK2qBtDMICekupt3LnRBdbP9UwgYYGA1UdIwR/MH2AFJuMytqgbQzCAnpLqbdy50QXWz/VoVqkWDBWMQswCQYDVQQGEwJISzESMBAGA1UECBMJSG9uZyBLb25nMRUwEwYDVQQKEwxleHByZXNzLXNhbWwxDDAKBgNVBAsTA2RldjEOMAwGA1UEAxMFZXNhbWyCCQD0ED7KJVMqXjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQA9t7VMtX93yIYIGFC20GCsMYZeZpTedxpxpjqom2dOuOUaDQgrZcGF3FVbFqTEpPtOnsKXYaCg7FJvUjxv7FIuix5H7JO6DALoJ792pfG2wwS2PvDiGFxMfGnNvb3aLnB/s6wTyWBpDYRdwlB5nj37KPk6kpFJj3N9x5BD1oTdmQqeVuacjoiemIulkc33P28tGl6Datth4WpE0LwmrwREQ1NWixi2j1Ti3mjYkyqGVY8XphWKEIIWmheqLnYCXRXhbxZ4E+FGg81ZYG8TKYC/IjzV8p0rLnAI1qS7wdwv5UJ9vQJt6KcxdHHZsUlpIfaJC6N5DvAL/qUY8DoIymgz
+ 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
@@ -31,9 +39,21 @@
urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos
urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName
urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
-
-
-
-
+
+
+
+
diff --git a/test/misc/idpmeta_share_cert.xml b/test/misc/idpmeta_share_cert.xml
index 33b0d5b3..f1e37e21 100644
--- a/test/misc/idpmeta_share_cert.xml
+++ b/test/misc/idpmeta_share_cert.xml
@@ -1,27 +1,44 @@
-
-
-
-
-
- MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=
-
-
-
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
- urn:oasis:names:tc:SAML:2.0:nameid-format:entity
- urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
- urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos
- urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
-
-
-
-
-
-
+
+
+
+
+
+ MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=
+
+
+
+ urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
+ urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
+ urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+ urn:oasis:names:tc:SAML:2.0:nameid-format:entity
+ urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
+ urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos
+ urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName
+ urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
+
+
+
+
+
+
diff --git a/test/misc/invalid_response.xml b/test/misc/invalid_response.xml
index 7caf8d0b..6bb68c54 100644
--- a/test/misc/invalid_response.xml
+++ b/test/misc/invalid_response.xml
@@ -1,6 +1,14 @@
-
+
https://idp.example.com/metadata
-
+
diff --git a/test/misc/logout_request.xml b/test/misc/logout_request.xml
index db678cc7..a20ae792 100644
--- a/test/misc/logout_request.xml
+++ b/test/misc/logout_request.xml
@@ -1,4 +1,14 @@
-
- http://sp.example.com/metadata
- f92cc1834efc0f73e9c09f482fce80037a6251e7
-
+
+ http://sp.example.com/metadata
+ f92cc1834efc0f73e9c09f482fce80037a6251e7
+
diff --git a/test/misc/request.xml b/test/misc/request.xml
index 9bafb7d8..3e0bf2df 100644
--- a/test/misc/request.xml
+++ b/test/misc/request.xml
@@ -1 +1,15 @@
-https://sp.example.org/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Password
+https://sp.example.org/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Password
diff --git a/test/misc/response.xml b/test/misc/response.xml
index fd3436ae..05a855ac 100644
--- a/test/misc/response.xml
+++ b/test/misc/response.xml
@@ -1 +1,46 @@
-https://idp.example.com/metadatahttps://idp.example.com/metadata_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7https://sp.example.com/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtesttest@example.comusersexamplerole1
+https://idp.example.com/metadatahttps://idp.example.com/metadata_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7https://sp.example.com/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtesttest@example.comusersexamplerole1
diff --git a/test/misc/response_signed.xml b/test/misc/response_signed.xml
index ebaa1d16..9736c76f 100644
--- a/test/misc/response_signed.xml
+++ b/test/misc/response_signed.xml
@@ -1 +1,56 @@
-https://idp.example.com/metadatahttps://idp.example.com/metadata_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7https://sp.example.com/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtesttest@example.comusersexamplerole1sZOR3aMpVBn1CoSmP674OQfCcyg=h7Dk6GTh4MrNNx8b8Or12SeGsAGBM/ILd7Jgz/RuqR6ixMHrmkRAotou8LvKOzH9I9BfLthqgwcNJGm4hMPHcxoiyVlkqWqnpIMxlWc/vb1E/lXjwo86mZ/hBUJdRhgIfrgIDKCMBf98ftWtUF8I1Hd5qBvY7pTMk3ErQYOtqBfvCCFGwejAfOUKwtY4itQ7AILi4Er2IgALH0zJO7alPugTOwmICd998rafB2wAHWREJkaOfCgCasRkB8tqcWjpLx2oMqiYSTVq2d6PBgAFSmoN9ltO2neTz9pqd0BA1BKIi7PjQYN+F7dB/ffG7V8VjNoPMROrHzq6sY3Ondtv7w==MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=
+https://idp.example.com/metadatahttps://idp.example.com/metadata_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7https://sp.example.com/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtesttest@example.comusersexamplerole1sZOR3aMpVBn1CoSmP674OQfCcyg=h7Dk6GTh4MrNNx8b8Or12SeGsAGBM/ILd7Jgz/RuqR6ixMHrmkRAotou8LvKOzH9I9BfLthqgwcNJGm4hMPHcxoiyVlkqWqnpIMxlWc/vb1E/lXjwo86mZ/hBUJdRhgIfrgIDKCMBf98ftWtUF8I1Hd5qBvY7pTMk3ErQYOtqBfvCCFGwejAfOUKwtY4itQ7AILi4Er2IgALH0zJO7alPugTOwmICd998rafB2wAHWREJkaOfCgCasRkB8tqcWjpLx2oMqiYSTVq2d6PBgAFSmoN9ltO2neTz9pqd0BA1BKIi7PjQYN+F7dB/ffG7V8VjNoPMROrHzq6sY3Ondtv7w==MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=
diff --git a/test/misc/response_signed_cert1.xml b/test/misc/response_signed_cert1.xml
index e5790248..1e914252 100644
--- a/test/misc/response_signed_cert1.xml
+++ b/test/misc/response_signed_cert1.xml
@@ -1 +1,60 @@
-https://idp.example.com/metadataiPlh1ZRFuCBV07ayPRWVk7xU9SB5JN8mu6xAab3lEo0=ECKTz4y6czJx+KGlZNb8E6mBnFrMQC8hL7YDlAi8dko=GZSDF9T0TMTe5nkZspOBlc6+j+lon0eHjViy765ty0tM7F47qgDVWTiC2x326Iz8One12XKKbUHxMvqABnI77aNSJ0/BADFJLoH+mgPuSsgcZygTAWmKdn1bR/3zydMtkMIbP9JXB2VEF7a7KnnnjGcM2OXmdxanhe5J2vtrBWCrxt0QZOLaEsxQmCHosKizVhOnO5JehNqqkf9M4yp7acIsIVhCg21YYqnuAWMsve8qReryF31189TdsV9KO8uB0rufBsxl/dzNnMG74Rgq4mS3QjPI7N/WpXzZZk8vPe38FYEsFA5lmeIsdMxnlbbUEPJFwzWM72xEmMgo12+y4A==MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=https://idp.example.com/metadata_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7https://sp.example.com/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtesttest@example.comusersexamplerole1
\ No newline at end of file
+https://idp.example.com/metadataiPlh1ZRFuCBV07ayPRWVk7xU9SB5JN8mu6xAab3lEo0=ECKTz4y6czJx+KGlZNb8E6mBnFrMQC8hL7YDlAi8dko=GZSDF9T0TMTe5nkZspOBlc6+j+lon0eHjViy765ty0tM7F47qgDVWTiC2x326Iz8One12XKKbUHxMvqABnI77aNSJ0/BADFJLoH+mgPuSsgcZygTAWmKdn1bR/3zydMtkMIbP9JXB2VEF7a7KnnnjGcM2OXmdxanhe5J2vtrBWCrxt0QZOLaEsxQmCHosKizVhOnO5JehNqqkf9M4yp7acIsIVhCg21YYqnuAWMsve8qReryF31189TdsV9KO8uB0rufBsxl/dzNnMG74Rgq4mS3QjPI7N/WpXzZZk8vPe38FYEsFA5lmeIsdMxnlbbUEPJFwzWM72xEmMgo12+y4A==MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=https://idp.example.com/metadata_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7https://sp.example.com/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtesttest@example.comusersexamplerole1
diff --git a/test/misc/response_signed_cert2.xml b/test/misc/response_signed_cert2.xml
index 1686dab3..295e7072 100644
--- a/test/misc/response_signed_cert2.xml
+++ b/test/misc/response_signed_cert2.xml
@@ -1 +1,60 @@
-https://idp.example.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https://idp.example.com/metadata_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7https://sp.example.com/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtesttest@example.comusersexamplerole1
\ No newline at end of file
+https://idp.example.com/metadataiPlh1ZRFuCBV07ayPRWVk7xU9SB5JN8mu6xAab3lEo0=ECKTz4y6czJx+KGlZNb8E6mBnFrMQC8hL7YDlAi8dko=JgrKOwRaj4swHYgLra3MOG92BeekCyRgbDfxAk5KLmzeRk1u0w6AmB/qW32mrlM4bn8LtwTq33PiHk6NMbkOfg5X2jQ8vjRyog+tgxDmwdiVkMMHfTWHcOqI5Gou572GayDLC0M9rOv4iHXUoDaul4ozhkeRolS9peLxydulSLzyXJiMGQ9ChnmxsR1P7y1rU/DOJ4O/zWzY2M9GoKBXWwG5C9RuoiO7FfOQn4za7InoQ+pBAtGWeh3mXwKLYpd+dhWL73vLa2sr5OmOQUlnFDSuFoAzhnT9eJEJCcedfmtjTUi724iAcYtFeXYahcCe/n4H2JjQYhE0ovG4JpEaThRA/sM3C6h3j7t9b+fX86VhH71+0f79VuX9TNeQkSiuYxqbUYvJjNgx3z8W7ixv5WGpHCjq6zd5KKbIwGk2+bWp7xs6ZD8sh9uyqWHNX/7YE096Ovxn4ki9O370MnK/3henA3m/+lwwWaL3Bn6+TmYC5DOWM7WVhy+dokSW6/2ZhgzoehpbDVZfYkKP+SH1w2PEqwtyB60fJpcTHZFANbCzfLJuyuRjiaWl9lzc3sKsRlK8W76ro/lXeIX2Jal1pQpkctZRUc0RyL+l9EnqzRnPc1K9nGUI+a8PNS54clILe0silqt339ZaE+wkslvhZ1M8oLzsWcXFWD+x2JZkvbo=MIIFLjCCAxYCCQCqGHhTssya9jANBgkqhkiG9w0BAQsFADBZMQswCQYDVQQGEwJISzESMBAGA1UECAwJSG9uZyBLb25nMRIwEAYDVQQHDAlIb25nIEtvbmcxEDAOBgNVBAoMB3NhbWxpZnkxEDAOBgNVBAMMB3NhbWxpZnkwHhcNMjAwNTEwMTUyNjIzWhcNMzAwNTA4MTUyNjIzWjBZMQswCQYDVQQGEwJISzESMBAGA1UECAwJSG9uZyBLb25nMRIwEAYDVQQHDAlIb25nIEtvbmcxEDAOBgNVBAoMB3NhbWxpZnkxEDAOBgNVBAMMB3NhbWxpZnkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDQG+abAeeWjwsOZt5SkcNcw/XSJcjSyJykEbEU2iguErRuOIyBfgj0p1UVBv33uL2igeYJT3OSXmSjvMO8KvqtYN2tJAjoFjghGr8NbIEZjYS4ukMZUbwxd2bRycD9OMI9g44AUB1sfQ0UyFwzEOseW3lcW1FnhcizA8TgI0GN4NpdVruNlpgoWdP3w+Syhtq0rWebY8g/HGFruEKn8VwbUblOZdP7jNVXsd1aUMScpuMa0khzzXPDN+Q0rwl79fO4ychSeKAAERdPXA1UfDfbh9W7pcYBP0ABXd91Bf9akplmbbVOIsNbuRIcVS7WvLwCr613JuJ+EtGDcUkrSpbuRvDW85DQRHBGuoKlcSG+imHQtHqRwMwMc8P54hIEBvaFW0RfwPfzdFNe8wARtmvIeX84iwq5Yey15Ly1rdopi7t2g7qyF7C/B9gZ3tJ/gPKp2NrdCGFBcahl93Lj56WWmI0jNHn7+7Y3x6isJ3KTRXIliSrAwiK7/7UezOlWzs1k8mGQWZTD3AGGKu1cBVwuC+rh4wkLsDeHfzxavbXxVEok9p/1P28M4GiHfS0POE3Hl4RT3Q6AiYWnmFYyZ+smY97SgPwB4tTNYFjC6+9d/BllNoQb8wsPjqp6ZDn1OeY668hp+ZAcE13AFdiTBMVrcdEECCPLxg1kFk5wZdHrGwIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQCyA/14hKTqfdeOVl+MQ2SLPWi7pC/t/Zv4kc361xP26FAVSSrxgXq9kVLZeJIAqCwjGHkl/DTUlA8hfLfuZx5z+NI/qIodsXAjCzsCe7paEbjvR6OQjYkR0UY4u/AOO7x2op2KDFKNuWT9KZNm8bh1mxwNKep1fJP2O5M0nMYAGYbPsLAOn7mzZyufQl8hsJwIV2s8sbft7s8vmEYZbuueQDOJCMTt+eC08LONrovYChyYmj3i5RIk8kcaodeSDo811F1B1gDvO/dmVxgrHEgoai7X6LUoiAiLkigP7udNEZxbXsRlOhBRv9w+rRXFurVFlUPkQ9UF+QB0BoyIcUxo+fZ8vCA4xEVBenVBadpFbwum6+XeTkvDoRc4sSCpm8v2qtprc8aU/0F82EzxSybYvstc5lDv7wuwCwNwfoAQ+/16kTpJvoYbOXUPv5yCA3mIuqYeA1woaWPXsE4jNOzTqv1qOZQTvXProEgK5B0FR5ILc4mfNrD2p9VGbiYf2GjCfeEzDFg174dvSn2MMp1yK5pvZEp7yFE8z1eduYN6W/7qdtss9BGpnyS5X7LuYfDvd1dHP6/JuqJDbfSVG9prYWcaMRd3FzSC7jBeetJgMyj4dunfqw8R16aONhwvICtzdFa93hYrDvTyo3ae80KFi0WGgApKeoqO5t3l1PAcaA==https://idp.example.com/metadata_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7https://sp.example.com/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtesttest@example.comusersexamplerole1
diff --git a/test/misc/signed_request_sha1.xml b/test/misc/signed_request_sha1.xml
index 7df1a59f..6eea3a54 100644
--- a/test/misc/signed_request_sha1.xml
+++ b/test/misc/signed_request_sha1.xml
@@ -1 +1,23 @@
-https://sp.example.org/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordtQDisBXKTQ+9OXJO5r7KuJga+KI=oxRkvau7UvYgFEZ7YNAUNf3067V7Tn5C9XSIiet1aZw2FYevNW5bUy/0mxp3aj6AvfFjnmpzAb88BjdwAz2BErDTomRcuZB7Lb0fYTf31N2oZOX0MiPiQOH54I63qJW4Xo3VqdF7GBuFZZHyllfSBv7gfCtjJDwFSCzWK70B9r3cFMRJZLhCJ9oPen+4U9scSYO6g+szBZLl6AiJ06PHc8jzEKGwfQrcZk8kDKUlvNfJMULyq8dpx2VvUAx4p5ewfMOwB9W3Hl3PPa0dO77zZif3CglpcN06f+m6UYG/wnoTQEyKW9hOe+2vGM80W77eWu0dmiaPuqT1ok8LXPuq1A==
+https://sp.example.org/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordtQDisBXKTQ+9OXJO5r7KuJga+KI=oxRkvau7UvYgFEZ7YNAUNf3067V7Tn5C9XSIiet1aZw2FYevNW5bUy/0mxp3aj6AvfFjnmpzAb88BjdwAz2BErDTomRcuZB7Lb0fYTf31N2oZOX0MiPiQOH54I63qJW4Xo3VqdF7GBuFZZHyllfSBv7gfCtjJDwFSCzWK70B9r3cFMRJZLhCJ9oPen+4U9scSYO6g+szBZLl6AiJ06PHc8jzEKGwfQrcZk8kDKUlvNfJMULyq8dpx2VvUAx4p5ewfMOwB9W3Hl3PPa0dO77zZif3CglpcN06f+m6UYG/wnoTQEyKW9hOe+2vGM80W77eWu0dmiaPuqT1ok8LXPuq1A==
diff --git a/test/misc/signed_request_sha256.xml b/test/misc/signed_request_sha256.xml
index 675b732a..99eab10c 100644
--- a/test/misc/signed_request_sha256.xml
+++ b/test/misc/signed_request_sha256.xml
@@ -1 +1,23 @@
-http://localhost:4002/sso/metadata9Ftynct5x7o+SdQM9iie2Z8VzZW95OTtXh4BD4O/HP8=EjCY0hdmiULo0qRy8tJeSANz3uINpdyFmCISiid4vl3KtszPa1mLvx1wGO2RJiFW8Sa18JOS0l8rYP2gwoUYmxU5WS/Cl1QEMlDj46fPpOjEBELGXdKW69zpAHa5jM/FtS8RCixhiMI1dmbL3+zgziEVdx5xrkaakqvpdDD601Eyn0gy1oO+VUmCMPFE6YjsPeFDhw5ZXf7MmJ/fXLeqWmH5Pn+mkyTCZWxi/L+2nG9iayZ41Z3wBl67XTdBL6rwHMcEY7oxwFSZtKTbtTOV6aW11KdAd9peLIsHeqoaMCY/VypS2bTr9FubQCbHhho2vbhX8cuUfpE21OefA7o1rA==MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=
+http://localhost:4002/sso/metadata9Ftynct5x7o+SdQM9iie2Z8VzZW95OTtXh4BD4O/HP8=EjCY0hdmiULo0qRy8tJeSANz3uINpdyFmCISiid4vl3KtszPa1mLvx1wGO2RJiFW8Sa18JOS0l8rYP2gwoUYmxU5WS/Cl1QEMlDj46fPpOjEBELGXdKW69zpAHa5jM/FtS8RCixhiMI1dmbL3+zgziEVdx5xrkaakqvpdDD601Eyn0gy1oO+VUmCMPFE6YjsPeFDhw5ZXf7MmJ/fXLeqWmH5Pn+mkyTCZWxi/L+2nG9iayZ41Z3wBl67XTdBL6rwHMcEY7oxwFSZtKTbtTOV6aW11KdAd9peLIsHeqoaMCY/VypS2bTr9FubQCbHhho2vbhX8cuUfpE21OefA7o1rA==MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=
diff --git a/test/misc/signed_request_sha512.xml b/test/misc/signed_request_sha512.xml
index 49800cb2..0c7ecbb9 100644
--- a/test/misc/signed_request_sha512.xml
+++ b/test/misc/signed_request_sha512.xml
@@ -1 +1,23 @@
-http://localhost:4002/sso/metadata9Ftynct5x7o+SdQM9iie2Z8VzZW95OTtXh4BD4O/HP8=dk+CI6UvXgsM0cHAGAz/Y3gbvehbab92i1jEUmDH0QB7d6/3l7j7TuOEvUFnmtwa0kwpigwpySwXybfiuvgdSBmhejwng5m28bYqaIA8FgCWe/BkBVL5BYeQH03gPbnqhBpC5EXUe52FtOlGAoTGNqaD0pyrshoGiOj/OzqVZC7RSBvvYt5iwpLyqj4KIFFao4yNAfIs2n7RwfcbGg3I2m2b5nuhVppRdzzukdQiLdDCuATPDxKJ3KdETbHb3yss+8L2iDPcAoqsZ+UTZ8VI5DhrQBcarcIe8Xp2FUKQnC4n0AEqCpb87l6txPz7GYDaw9yMqe2xD5LPWQ6/2guvqw==MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=
+http://localhost:4002/sso/metadata9Ftynct5x7o+SdQM9iie2Z8VzZW95OTtXh4BD4O/HP8=dk+CI6UvXgsM0cHAGAz/Y3gbvehbab92i1jEUmDH0QB7d6/3l7j7TuOEvUFnmtwa0kwpigwpySwXybfiuvgdSBmhejwng5m28bYqaIA8FgCWe/BkBVL5BYeQH03gPbnqhBpC5EXUe52FtOlGAoTGNqaD0pyrshoGiOj/OzqVZC7RSBvvYt5iwpLyqj4KIFFao4yNAfIs2n7RwfcbGg3I2m2b5nuhVppRdzzukdQiLdDCuATPDxKJ3KdETbHb3yss+8L2iDPcAoqsZ+UTZ8VI5DhrQBcarcIe8Xp2FUKQnC4n0AEqCpb87l6txPz7GYDaw9yMqe2xD5LPWQ6/2guvqw==MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=
diff --git a/test/misc/signed_response_sha1.xml b/test/misc/signed_response_sha1.xml
index 120a657d..a4aeb390 100644
--- a/test/misc/signed_response_sha1.xml
+++ b/test/misc/signed_response_sha1.xml
@@ -1 +1,52 @@
-https://idp.example.com/metadataCocGj4j5psQ0OfZ1mOlAdQkfwjTqCb95tNqpiFtt6qhTlnn+1IIp9pDpMLubomf9LWwX176PPLWFYxsRmqyEBYlhT53hgAF+z2fEgJdlxXF7FYKsnsn+ujC0ZJP3QkUlWGT9eo74i67JrkAwmiOXPHBJAAN040L/uqmYgjqdnGIFZAyTk4SwplECf1yzVxh4wkETpkf1na1VgTpFC3QDHpXVmCdTbq4FgtgNyfcZmr10d81rmSLjwfHJswV8Qg+cuxXODcn0rxDA5ZA3abpIxGwHUMtKP8ak4amY1urWQTXkhaFjZIChA6E4p870MzfmzVExG6p8/svKf2vDHTAH0w==MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=https://idp.example.com/metadata_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7https://sp.example.com/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtesttest@example.comusersexamplerole1
\ No newline at end of file
+https://idp.example.com/metadataCocGj4j5psQ0OfZ1mOlAdQkfwjTqCb95tNqpiFtt6qhTlnn+1IIp9pDpMLubomf9LWwX176PPLWFYxsRmqyEBYlhT53hgAF+z2fEgJdlxXF7FYKsnsn+ujC0ZJP3QkUlWGT9eo74i67JrkAwmiOXPHBJAAN040L/uqmYgjqdnGIFZAyTk4SwplECf1yzVxh4wkETpkf1na1VgTpFC3QDHpXVmCdTbq4FgtgNyfcZmr10d81rmSLjwfHJswV8Qg+cuxXODcn0rxDA5ZA3abpIxGwHUMtKP8ak4amY1urWQTXkhaFjZIChA6E4p870MzfmzVExG6p8/svKf2vDHTAH0w==MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=https://idp.example.com/metadata_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7https://sp.example.com/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtesttest@example.comusersexamplerole1
diff --git a/test/misc/signed_response_sha256.xml b/test/misc/signed_response_sha256.xml
index 928dcc90..45fe5af0 100644
--- a/test/misc/signed_response_sha256.xml
+++ b/test/misc/signed_response_sha256.xml
@@ -1 +1,52 @@
-https://idp.example.com/metadata0TJreH5fvSPwTL8cMGtvYkc8mDQDirHL/0KAU0PPjWXKUqyWSVi7FtDhnAuUakJpfPaYowrHBaUkX+SoJC9uQjXNCgvx5Z7DJfNq+h/vFxoSoxMT/1qeKMKWoNQFVmUErIPMCl0Wou/MfDR8qd+0ofUyLF4pEglczqNBVGi23RirDMZGSgS9M6QDlgpTx/CDnWRL6+0T1lNrTLuX6n0VaEziUeHOHY0lK5T0hmT/tVlufZ7LRO10FN7MUrxzIZvIIWVNuPVOmn0hm/4Z33JEK7rT35+MZLq8f7fbA3SS4+4InJOvZZgBRR9BcPjeEXG1n1el7uyf2AfE9+gr3vu6eg==MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=https://idp.example.com/metadata_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7https://sp.example.com/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtesttest@example.comusersexamplerole1
\ No newline at end of file
+https://idp.example.com/metadata0TJreH5fvSPwTL8cMGtvYkc8mDQDirHL/0KAU0PPjWXKUqyWSVi7FtDhnAuUakJpfPaYowrHBaUkX+SoJC9uQjXNCgvx5Z7DJfNq+h/vFxoSoxMT/1qeKMKWoNQFVmUErIPMCl0Wou/MfDR8qd+0ofUyLF4pEglczqNBVGi23RirDMZGSgS9M6QDlgpTx/CDnWRL6+0T1lNrTLuX6n0VaEziUeHOHY0lK5T0hmT/tVlufZ7LRO10FN7MUrxzIZvIIWVNuPVOmn0hm/4Z33JEK7rT35+MZLq8f7fbA3SS4+4InJOvZZgBRR9BcPjeEXG1n1el7uyf2AfE9+gr3vu6eg==MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=https://idp.example.com/metadata_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7https://sp.example.com/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtesttest@example.comusersexamplerole1
diff --git a/test/misc/signed_response_sha512.xml b/test/misc/signed_response_sha512.xml
index 8f268a99..79b07d18 100644
--- a/test/misc/signed_response_sha512.xml
+++ b/test/misc/signed_response_sha512.xml
@@ -1 +1,52 @@
-https://idp.example.com/metadataRkgzPlU7snHmrHTA6tCt0DRqQFyQeNypGIFzaY+2/6OLsNNH0B4gdBWYUWrwMpOGNqHr9Wo+th248ABVoUBtbdQ2pT8M49D0JDGwvl6L8CscTK0xzLGaqaAhHwszmk61WGVOxrbkiZQqOQA8VDiua5bDoXOqiCqEIB6TlSuJ+HH4Lc6u10WSXChI5iC9YwsHoWS8tqFLw6rsx4qPx4hFkZfBUh6JFZNT8hsWiSr4y6d359SIkRgkPUd85+I/3Od//al4HLnIjXaDsahO/YZ9AlvOnBxjUEuk/7kuxZ91LDeI6I8ekno83+ndhk34tnaBc3l1uGeHNhJhpY3eK+LiCw==MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=https://idp.example.com/metadata_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7https://sp.example.com/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtesttest@example.comusersexamplerole1
\ No newline at end of file
+https://idp.example.com/metadataRkgzPlU7snHmrHTA6tCt0DRqQFyQeNypGIFzaY+2/6OLsNNH0B4gdBWYUWrwMpOGNqHr9Wo+th248ABVoUBtbdQ2pT8M49D0JDGwvl6L8CscTK0xzLGaqaAhHwszmk61WGVOxrbkiZQqOQA8VDiua5bDoXOqiCqEIB6TlSuJ+HH4Lc6u10WSXChI5iC9YwsHoWS8tqFLw6rsx4qPx4hFkZfBUh6JFZNT8hsWiSr4y6d359SIkRgkPUd85+I/3Od//al4HLnIjXaDsahO/YZ9AlvOnBxjUEuk/7kuxZ91LDeI6I8ekno83+ndhk34tnaBc3l1uGeHNhJhpY3eK+LiCw==MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0xNTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZUtWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRAvr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsbVUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0OBBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwdwK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPNmHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUwi1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0azkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWWk1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4yyoWAJDUHiAmvFA=https://idp.example.com/metadata_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7https://sp.example.com/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordtesttest@example.comusersexamplerole1
diff --git a/test/misc/sp_metadata_98.xml b/test/misc/sp_metadata_98.xml
index 40814588..1aff573a 100644
--- a/test/misc/sp_metadata_98.xml
+++ b/test/misc/sp_metadata_98.xml
@@ -1,6 +1,16 @@
-
+
urn:oasis:names:tc:SAML:2.0:nameid-format:transient
-
+
-
\ No newline at end of file
+
diff --git a/test/misc/spmeta.xml b/test/misc/spmeta.xml
index 81068842..e063a004 100644
--- a/test/misc/spmeta.xml
+++ b/test/misc/spmeta.xml
@@ -1,27 +1,49 @@
-
-
-
-
-
- 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
-
-
-
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
-
-
-
-
-
-
+
+
+
+
+
+ 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
+
+
+
+ urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
+
+
+
+
+
+
diff --git a/test/misc/spmeta_noassertsign.xml b/test/misc/spmeta_noassertsign.xml
index 984917ed..2d34e763 100644
--- a/test/misc/spmeta_noassertsign.xml
+++ b/test/misc/spmeta_noassertsign.xml
@@ -1,32 +1,41 @@
-
-
-
-
-
- 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
-
-
-
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
-
-
-
-
+
+
+
+
+
+ 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
+
+
+
+ urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
+
+
+
+
diff --git a/test/misc/spmeta_noauthnsign.xml b/test/misc/spmeta_noauthnsign.xml
index 0fc8d2c6..2b6e760e 100644
--- a/test/misc/spmeta_noauthnsign.xml
+++ b/test/misc/spmeta_noauthnsign.xml
@@ -1,25 +1,33 @@
-
-
-
-
-
- MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=
-
-
-
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
-
-
-
-
+
+
+
+
+
+ MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=
+
+
+
+ urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
+
+
+
+
diff --git a/test/tsconfig.json b/test/tsconfig.json
new file mode 100644
index 00000000..d53588ea
--- /dev/null
+++ b/test/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "noEmit": true,
+ "rootDir": "./",
+ "tsBuildInfoFile": "./.build/caches/test/tsconfig.tsbuildinfo",
+ "types": ["node", "ava"]
+ },
+ "include": ["./**/*.ts"],
+ "references": [
+ {
+ "path": "../tsconfig.json"
+ }
+ ]
+}
diff --git a/tools/eslint-config/base.js b/tools/eslint-config/base.js
new file mode 100644
index 00000000..439c8784
--- /dev/null
+++ b/tools/eslint-config/base.js
@@ -0,0 +1,13 @@
+// @ts-check
+const config = {
+ env: {
+ es6: true,
+ },
+ extends: ['eslint:recommended'],
+ parserOptions: {
+ ecmaVersion: 2019,
+ sourceType: 'module',
+ },
+};
+
+module.exports = config;
diff --git a/tools/eslint-config/javascript.js b/tools/eslint-config/javascript.js
new file mode 100644
index 00000000..4275c3a8
--- /dev/null
+++ b/tools/eslint-config/javascript.js
@@ -0,0 +1,138 @@
+// @ts-check
+const config = {
+ rules: {
+ // possible errors
+ 'no-cond-assign': [2],
+ 'no-constant-condition': [2],
+ 'no-control-regex': [2],
+ 'no-debugger': [2],
+ 'no-dupe-args': [2],
+ 'no-dupe-keys': [2],
+ 'no-duplicate-case': [2],
+ 'no-empty': [2],
+ 'no-empty-character-class': [2],
+ 'no-ex-assign': [2],
+ 'no-extra-boolean-cast': [2],
+ 'no-extra-semi': [2],
+ 'no-func-assign': [2],
+ 'no-invalid-regexp': [2],
+ 'no-irregular-whitespace': [2],
+ 'no-negated-in-lhs': [2],
+ 'no-reserved-keys': [0],
+ 'no-regex-spaces': [2],
+ 'no-sparse-arrays': [2],
+ 'no-unreachable': [2],
+ 'use-isnan': [2],
+ 'valid-typeof': [2],
+ 'valid-jsdoc': [
+ 2,
+ {
+ requireReturnDescription: false,
+ },
+ ],
+
+ // best practices
+ 'array-callback-return': [2],
+ 'block-scoped-var': [2],
+ complexity: [1],
+ 'consistent-return': [2],
+ curly: [2],
+ 'default-case': [2],
+ 'dot-notation': [2, { allowKeywords: true }],
+ eqeqeq: [2],
+ 'guard-for-in': [2],
+ 'no-alert': [2],
+ 'no-caller': [2],
+ 'no-case-declarations': [2],
+ 'no-div-regex': [2],
+ 'no-empty-function': [2],
+ 'no-empty-pattern': [2],
+ 'no-eq-null': [2],
+ 'no-eval': [2],
+ 'no-extend-native': [2],
+ 'no-extra-bind': [2],
+ 'no-extra-label': [2],
+ 'no-fallthrough': [2],
+ 'no-floating-decimal': [2],
+ 'no-implicit-coercion': [2],
+ 'no-implied-eval': [2],
+ 'no-iterator': [2],
+ 'no-labels': [2],
+ 'no-lone-blocks': [2],
+ 'no-loop-func': [2],
+ 'no-magic-numbers': [0],
+ 'no-multi-spaces': [0],
+ 'no-native-reassign': [2],
+ 'no-new': [2],
+ 'no-new-func': [2],
+ 'no-new-wrappers': [2],
+ 'no-octal': [2],
+ 'no-octal-escape': [2],
+ 'no-param-reassign': [2],
+ 'no-proto': [2],
+ 'no-redeclare': [2],
+ 'no-return-assign': [2],
+ 'no-script-url': [2],
+ 'no-self-assign': [2],
+ 'no-self-compare': [2],
+ 'no-sequences': [2],
+ 'no-throw-literal': [2],
+ 'no-unmodified-loop-condition': [2],
+ 'no-unused-expressions': [2],
+ 'no-unused-labels': [2],
+ 'no-useless-call': [2],
+ 'no-useless-concat': [2],
+ 'no-void': [2],
+ 'no-warning-comments': [1],
+ 'no-with': [2],
+ 'wrap-iife': [2],
+ yoda: [2, 'never'],
+
+ // strict mode
+ strict: [2, 'global'],
+
+ // variables
+ 'no-catch-shadow': [2],
+ 'no-delete-var': [2],
+ 'no-shadow': [2],
+ 'no-shadow-restricted-names': [2],
+ 'no-undef': [2],
+ 'no-undef-init': [2],
+ 'no-undefined': [2],
+ 'no-unused-vars': [
+ 'warn',
+ {
+ argsIgnorePattern: '^_|^[iI]gnore',
+ caughtErrorsIgnorePattern: '^_|^[iI]gnore',
+ varsIgnorePattern: '^_|^[iI]gnore|^React$|^jsx$|^css$',
+ },
+ ],
+ 'no-use-before-define': [2, 'nofunc'],
+
+ // node.js
+ 'callback-return': [2, ['callback', 'cb', 'cb1', 'cb2', 'cb3', 'next', 'innerCb', 'done']],
+ 'global-require': [2],
+ 'handle-callback-err': [2, '^.*(e|E)rr'],
+ 'no-mixed-requires': [2],
+ 'no-new-require': [2],
+ 'no-path-concat': [2],
+ 'no-process-exit': [0],
+
+ // stylistic
+ // turn on the few on that aren't handled by JSCS.
+ 'consistent-this': [2, 'self'],
+ 'no-array-constructor': [2],
+ 'no-nested-ternary': [2],
+ 'no-new-object': [2],
+
+ // es6
+ 'no-class-assign': [2],
+ 'no-dupe-class-members': [2],
+ 'no-new-symbol': [2],
+ 'no-const-assign': [2],
+ 'no-var': [2],
+ 'prefer-const': [2],
+ },
+};
+
+module.exports = config;
diff --git a/tools/eslint-config/node.js b/tools/eslint-config/node.js
new file mode 100644
index 00000000..2620807a
--- /dev/null
+++ b/tools/eslint-config/node.js
@@ -0,0 +1,91 @@
+// @ts-check
+const config = {
+ env: {
+ node: true,
+ },
+ plugins: ['security'],
+ extends: ['plugin:security/recommended'],
+ rules: {
+ 'default-case': ['warn', { commentPattern: '^no default$' }],
+ eqeqeq: ['warn', 'smart'],
+ 'no-array-constructor': 'warn',
+ 'no-caller': 'warn',
+ 'no-cond-assign': ['warn', 'except-parens'],
+ 'no-const-assign': 'warn',
+ 'no-control-regex': 'warn',
+ 'no-dupe-args': 'warn',
+ 'no-dupe-class-members': 'warn',
+ 'no-dupe-keys': 'warn',
+ 'no-duplicate-case': 'warn',
+ 'no-empty-character-class': 'warn',
+ 'no-empty-pattern': 'warn',
+ 'no-eval': 'warn',
+ 'no-ex-assign': 'warn',
+ 'no-extend-native': 'warn',
+ 'no-extra-bind': 'warn',
+ 'no-extra-label': 'warn',
+ 'no-fallthrough': 'warn',
+ 'no-func-assign': 'warn',
+ 'no-implied-eval': 'warn',
+ 'no-invalid-regexp': 'warn',
+ 'no-iterator': 'warn',
+ 'no-label-var': 'warn',
+ 'no-labels': ['warn', { allowLoop: true, allowSwitch: false }],
+ 'no-loop-func': 'warn',
+ 'no-mixed-operators': [
+ 'warn',
+ {
+ groups: [
+ ['&', '|', '^', '~', '<<', '>>', '>>>'],
+ ['==', '!=', '===', '!==', '>', '>=', '<', '<='],
+ ['&&', '||'],
+ ['in', 'instanceof'],
+ ],
+ allowSamePrecedence: false,
+ },
+ ],
+ 'no-global-assign': 'warn',
+ 'no-unsafe-negation': 'warn',
+ 'no-new-func': 'warn',
+ 'no-new-symbol': 'warn',
+ 'no-new-wrappers': 'warn',
+ 'no-obj-calls': 'warn',
+ 'no-octal': 'warn',
+ 'no-octal-escape': 'warn',
+ 'no-redeclare': 'warn',
+ 'no-restricted-syntax': ['warn', 'WithStatement'],
+ 'no-script-url': 'warn',
+ 'no-sequences': 'warn',
+ 'no-shadow-restricted-names': 'warn',
+ 'no-sparse-arrays': 'warn',
+ 'no-template-curly-in-string': 'warn',
+ 'no-this-before-super': 'warn',
+ 'no-unexpected-multiline': 'warn',
+ 'no-unreachable': 'warn',
+ 'no-unused-expressions': [
+ 'error',
+ {
+ allowShortCircuit: true,
+ allowTernary: true,
+ },
+ ],
+ 'no-useless-escape': 'warn',
+ 'no-useless-rename': [
+ 'warn',
+ {
+ ignoreDestructuring: false,
+ ignoreImport: false,
+ ignoreExport: false,
+ },
+ ],
+ 'no-with': 'warn',
+ 'no-whitespace-before-property': 'warn',
+ 'require-yield': 'warn',
+ 'rest-spread-spacing': ['warn', 'never'],
+ 'unicode-bom': ['warn', 'never'],
+ 'use-isnan': 'warn',
+ 'valid-typeof': 'warn',
+ },
+};
+
+module.exports = config;
diff --git a/tools/eslint-config/prettier.js b/tools/eslint-config/prettier.js
new file mode 100644
index 00000000..324a35c0
--- /dev/null
+++ b/tools/eslint-config/prettier.js
@@ -0,0 +1,7 @@
+// @ts-check
+const config = {
+ plugins: ['prettier'],
+ extends: ['prettier', 'plugin:prettier/recommended'],
+};
+
+module.exports = config;
diff --git a/tools/eslint-config/typescript.js b/tools/eslint-config/typescript.js
new file mode 100644
index 00000000..b01b3d81
--- /dev/null
+++ b/tools/eslint-config/typescript.js
@@ -0,0 +1,33 @@
+// @ts-check
+const config = {
+ parser: '@typescript-eslint/parser',
+ plugins: ['@typescript-eslint'],
+ extends: [
+ 'plugin:@typescript-eslint/eslint-recommended',
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:@typescript-eslint/recommended-requiring-type-checking',
+ ],
+ rules: {
+ '@typescript-eslint/no-unused-vars': [
+ 'warn',
+ {
+ argsIgnorePattern: '^_|^[iI]gnore',
+ caughtErrorsIgnorePattern: '^_|^[iI]gnore',
+ varsIgnorePattern: '^_|^[iI]gnore|^React$|^jsx$|^css$',
+ },
+ ],
+ '@typescript-eslint/explicit-function-return-type': 'off',
+ '@typescript-eslint/no-use-before-define': 'off',
+ '@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/no-unsafe-assignment': 'off',
+ // '@typescript-eslint/no-unsafe-call': 'off',
+ '@typescript-eslint/no-unsafe-member-access': 'off',
+ // '@typescript-eslint/no-unsafe-return': 'off',
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
+ '@typescript-eslint/restrict-template-expressions': 'off',
+ // '@typescript-eslint/restrict-plus-operands': 'off',
+ '@typescript-eslint/require-await': 'off',
+ },
+};
+
+module.exports = config;
diff --git a/tools/eslintrc.js b/tools/eslintrc.js
new file mode 100644
index 00000000..451d6d8d
--- /dev/null
+++ b/tools/eslintrc.js
@@ -0,0 +1,27 @@
+const { resolve } = require('path');
+
+// @ts-check
+const config = {
+ overrides: [
+ {
+ files: ['*.{js,ts}'],
+ extends: ['./eslint-config/base'],
+ },
+ {
+ files: ['*.js'],
+ extends: ['./eslint-config/javascript', './eslint-config/node', './eslint-config/prettier'],
+ },
+ {
+ files: ['src/**/*.ts'],
+ extends: ['./eslint-config/typescript', './eslint-config/node', './eslint-config/prettier'],
+ parserOptions: { project: resolve(__dirname, '../tsconfig.json') },
+ },
+ {
+ files: ['test/**/*.ts'],
+ extends: ['./eslint-config/typescript', './eslint-config/node', './eslint-config/prettier'],
+ parserOptions: { project: resolve(__dirname, '../test/tsconfig.json') },
+ },
+ ],
+};
+
+module.exports = config;
diff --git a/tsconfig.json b/tsconfig.json
index 946e97a7..6f57aaeb 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,33 +1,36 @@
{
- "compilerOptions": {
- "target": "es5",
- "module": "commonjs",
- "moduleResolution": "node",
- "declaration": true,
- "declarationDir": "types",
- "emitDecoratorMetadata": true,
- "experimentalDecorators": true,
- "downlevelIteration": true,
- "sourceMap": true,
- "outDir": "./build",
- "baseUrl": "./",
- "removeComments": false,
- "strictNullChecks": true,
- "paths": {},
- "lib": [
- "dom",
- "es2015.core",
- "es2015.promise",
- "es2015.iterable",
- "es5"
- ]
- },
- "atom": { "rewriteTsconfig": false },
- "exclude": [
- "node_modules",
- "types/**/*.ts",
- "test/**/*.ts"
- ],
- "compileOnSave": false,
- "buildOnSave": false
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "compilerOptions": {
+ "allowJs": false,
+ "allowUnreachableCode": true,
+ "allowSyntheticDefaultImports": true,
+ "assumeChangesOnlyAffectDirectDependencies": true,
+ "composite": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ // "importHelpers": true,
+ "importsNotUsedAsValues": "error",
+ "incremental": true,
+ "isolatedModules": true,
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "noImplicitAny": true,
+ "noUncheckedIndexedAccess": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "preserveSymlinks": true,
+ "removeComments": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "sourceMap": false,
+ "strict": true,
+ "target": "ES2019", // using node 14! (or babel, in which packages opt-in to ESNEXT)
+ "tsBuildInfoFile": "./.build/caches/tsconfig.tsbuildinfo",
+ "typeRoots": ["@types"], // force to opt-in
+ "types": ["node"], // force to opt-in
+ "declaration": true,
+ "rootDir": "./src",
+ "outDir": "./lib"
+ },
+ "include": ["./src/**/*.ts"]
}
diff --git a/tslint.json b/tslint.json
deleted file mode 100644
index e2b2917c..00000000
--- a/tslint.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "extends": "tslint:recommended",
- "rulesDirectory": [],
- "linterOptions": {
- "exclude": [
- "node_modules/**"
- ]
- },
- "rules": {
- "arrow-parens": [true, "ban-single-arg-parens"],
- "comment-format": false,
- "interface-name": [true, "never-prefix"],
- "jsdoc-format": false,
- "max-line-length": false,
- "member-access": false,
- "no-console": [false],
- "no-consecutive-blank-lines": [true, 3],
- "no-empty-interface": false,
- "no-string-literal": false,
- "object-literal-sort-keys": false,
- "object-literal-key-quotes": false,
- "object-literal-shorthand": false,
- "trailing-comma": false,
- "eofline": false,
- "no-empty": false,
- "align": false,
- "no-trailing-whitespace": false,
- "ordered-imports": false,
- "quotemark": [true, "single", "avoid-escape", "avoid-template"],
- "variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore", "allow-pascal-case"],
- "interface-over-type-literal": false,
- "no-var-requires": false
- },
- "jsRules": {}
-}
diff --git a/types.d.ts b/types.d.ts
deleted file mode 100644
index 8f37c925..00000000
--- a/types.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './index'
-export * from './src/types'
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 3c5341c6..49db4e8b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,15 +2,17 @@
# yarn lockfile v1
-"@authenio/xml-encryption@^1.2.2":
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/@authenio/xml-encryption/-/xml-encryption-1.2.2.tgz#87cbfe7a9efa9e283ba4c743f9ba5b4f6e06d36c"
- integrity sha512-DARJx+HwQ/jgVF+rOFwYFDWqFIZGueIKmwSRsTBPlEw9tkCFWWv53MdwScKfVX0tiZFNJshRR99fdKnzXnxyvg==
+"@authenio/samlify-xsd-schema-validator@^1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@authenio/samlify-xsd-schema-validator/-/samlify-xsd-schema-validator-1.0.3.tgz#7e30a5a3a4af302f43095fec1eb9d3fd29778142"
+ integrity sha512-J4yC4S6Jn8bd9AXIdytzuFprrpDrRoV5TUoH135fFirx8CE1wUzy5xKYPXi8juDRylsIKOGpXEY3voCPIWYxGg==
dependencies:
- escape-html "^1.0.3"
- node-forge "^0.10.0"
- xmldom "~0.1.15"
- xpath "0.0.27"
+ "@authenio/xsd-schema-validator" "^0.7.1"
+
+"@authenio/xsd-schema-validator@^0.7.1":
+ version "0.7.1"
+ resolved "https://registry.yarnpkg.com/@authenio/xsd-schema-validator/-/xsd-schema-validator-0.7.1.tgz#3264c1b781d5192fe46cb5207d6ce51a706eed07"
+ integrity sha512-LfxalSt34Z6V2MKBRExfDBXHwLO3PycKaI0+k4nM1ei5rLJZHqU3C/vYm+LaA1nba6cu+cVXQY8zez3R0ZKl+w==
"@ava/typescript@^1.1.1":
version "1.1.1"
@@ -19,179 +21,206 @@
dependencies:
escape-string-regexp "^2.0.0"
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
- integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==
+"@babel/code-frame@7.12.11":
+ version "7.12.11"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
+ integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==
dependencies:
- "@babel/highlight" "^7.8.3"
+ "@babel/highlight" "^7.10.4"
+
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13":
+ version "7.12.13"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658"
+ integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==
+ dependencies:
+ "@babel/highlight" "^7.12.13"
+
+"@babel/compat-data@^7.13.8":
+ version "7.13.8"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.8.tgz#5b783b9808f15cef71547f1b691f34f8ff6003a6"
+ integrity sha512-EaI33z19T4qN3xLXsGf48M2cDqa6ei9tPZlfLdb2HC+e/cFtREiRd8hdSqDbwdLB0/+gLwqJmCYASH0z2bUdog==
"@babel/core@^7.7.5":
- version "7.9.6"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.6.tgz#d9aa1f580abf3b2286ef40b6904d390904c63376"
- integrity sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg==
- dependencies:
- "@babel/code-frame" "^7.8.3"
- "@babel/generator" "^7.9.6"
- "@babel/helper-module-transforms" "^7.9.0"
- "@babel/helpers" "^7.9.6"
- "@babel/parser" "^7.9.6"
- "@babel/template" "^7.8.6"
- "@babel/traverse" "^7.9.6"
- "@babel/types" "^7.9.6"
+ version "7.13.8"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.8.tgz#c191d9c5871788a591d69ea1dc03e5843a3680fb"
+ integrity sha512-oYapIySGw1zGhEFRd6lzWNLWFX2s5dA/jm+Pw/+59ZdXtjyIuwlXbrId22Md0rgZVop+aVoqow2riXhBLNyuQg==
+ dependencies:
+ "@babel/code-frame" "^7.12.13"
+ "@babel/generator" "^7.13.0"
+ "@babel/helper-compilation-targets" "^7.13.8"
+ "@babel/helper-module-transforms" "^7.13.0"
+ "@babel/helpers" "^7.13.0"
+ "@babel/parser" "^7.13.4"
+ "@babel/template" "^7.12.13"
+ "@babel/traverse" "^7.13.0"
+ "@babel/types" "^7.13.0"
convert-source-map "^1.7.0"
debug "^4.1.0"
- gensync "^1.0.0-beta.1"
+ gensync "^1.0.0-beta.2"
json5 "^2.1.2"
- lodash "^4.17.13"
- resolve "^1.3.2"
- semver "^5.4.1"
+ lodash "^4.17.19"
+ semver "^6.3.0"
source-map "^0.5.0"
-"@babel/generator@^7.9.6":
- version "7.9.6"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.6.tgz#5408c82ac5de98cda0d77d8124e99fa1f2170a43"
- integrity sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ==
+"@babel/generator@^7.13.0":
+ version "7.13.0"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.0.tgz#bd00d4394ca22f220390c56a0b5b85568ec1ec0c"
+ integrity sha512-zBZfgvBB/ywjx0Rgc2+BwoH/3H+lDtlgD4hBOpEv5LxRnYsm/753iRuLepqnYlynpjC3AdQxtxsoeHJoEEwOAw==
dependencies:
- "@babel/types" "^7.9.6"
+ "@babel/types" "^7.13.0"
jsesc "^2.5.1"
- lodash "^4.17.13"
source-map "^0.5.0"
-"@babel/helper-function-name@^7.9.5":
- version "7.9.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c"
- integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==
- dependencies:
- "@babel/helper-get-function-arity" "^7.8.3"
- "@babel/template" "^7.8.3"
- "@babel/types" "^7.9.5"
-
-"@babel/helper-get-function-arity@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5"
- integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==
- dependencies:
- "@babel/types" "^7.8.3"
-
-"@babel/helper-member-expression-to-functions@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c"
- integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==
- dependencies:
- "@babel/types" "^7.8.3"
-
-"@babel/helper-module-imports@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498"
- integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==
- dependencies:
- "@babel/types" "^7.8.3"
-
-"@babel/helper-module-transforms@^7.9.0":
- version "7.9.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5"
- integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==
- dependencies:
- "@babel/helper-module-imports" "^7.8.3"
- "@babel/helper-replace-supers" "^7.8.6"
- "@babel/helper-simple-access" "^7.8.3"
- "@babel/helper-split-export-declaration" "^7.8.3"
- "@babel/template" "^7.8.6"
- "@babel/types" "^7.9.0"
- lodash "^4.17.13"
-
-"@babel/helper-optimise-call-expression@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9"
- integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==
- dependencies:
- "@babel/types" "^7.8.3"
-
-"@babel/helper-replace-supers@^7.8.6":
- version "7.9.6"
- resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz#03149d7e6a5586ab6764996cd31d6981a17e1444"
- integrity sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA==
- dependencies:
- "@babel/helper-member-expression-to-functions" "^7.8.3"
- "@babel/helper-optimise-call-expression" "^7.8.3"
- "@babel/traverse" "^7.9.6"
- "@babel/types" "^7.9.6"
-
-"@babel/helper-simple-access@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae"
- integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==
- dependencies:
- "@babel/template" "^7.8.3"
- "@babel/types" "^7.8.3"
-
-"@babel/helper-split-export-declaration@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9"
- integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==
- dependencies:
- "@babel/types" "^7.8.3"
-
-"@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5":
- version "7.9.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80"
- integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==
-
-"@babel/helpers@^7.9.6":
- version "7.9.6"
- resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.6.tgz#092c774743471d0bb6c7de3ad465ab3d3486d580"
- integrity sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw==
- dependencies:
- "@babel/template" "^7.8.3"
- "@babel/traverse" "^7.9.6"
- "@babel/types" "^7.9.6"
-
-"@babel/highlight@^7.8.3":
- version "7.9.0"
- resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079"
- integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==
- dependencies:
- "@babel/helper-validator-identifier" "^7.9.0"
+"@babel/helper-compilation-targets@^7.13.8":
+ version "7.13.8"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.8.tgz#02bdb22783439afb11b2f009814bdd88384bd468"
+ integrity sha512-pBljUGC1y3xKLn1nrx2eAhurLMA8OqBtBP/JwG4U8skN7kf8/aqwwxpV1N6T0e7r6+7uNitIa/fUxPFagSXp3A==
+ dependencies:
+ "@babel/compat-data" "^7.13.8"
+ "@babel/helper-validator-option" "^7.12.17"
+ browserslist "^4.14.5"
+ semver "^6.3.0"
+
+"@babel/helper-function-name@^7.12.13":
+ version "7.12.13"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz#93ad656db3c3c2232559fd7b2c3dbdcbe0eb377a"
+ integrity sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==
+ dependencies:
+ "@babel/helper-get-function-arity" "^7.12.13"
+ "@babel/template" "^7.12.13"
+ "@babel/types" "^7.12.13"
+
+"@babel/helper-get-function-arity@^7.12.13":
+ version "7.12.13"
+ resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583"
+ integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==
+ dependencies:
+ "@babel/types" "^7.12.13"
+
+"@babel/helper-member-expression-to-functions@^7.13.0":
+ version "7.13.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.0.tgz#6aa4bb678e0f8c22f58cdb79451d30494461b091"
+ integrity sha512-yvRf8Ivk62JwisqV1rFRMxiSMDGnN6KH1/mDMmIrij4jztpQNRoHqqMG3U6apYbGRPJpgPalhva9Yd06HlUxJQ==
+ dependencies:
+ "@babel/types" "^7.13.0"
+
+"@babel/helper-module-imports@^7.12.13":
+ version "7.12.13"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz#ec67e4404f41750463e455cc3203f6a32e93fcb0"
+ integrity sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==
+ dependencies:
+ "@babel/types" "^7.12.13"
+
+"@babel/helper-module-transforms@^7.13.0":
+ version "7.13.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.0.tgz#42eb4bd8eea68bab46751212c357bfed8b40f6f1"
+ integrity sha512-Ls8/VBwH577+pw7Ku1QkUWIyRRNHpYlts7+qSqBBFCW3I8QteB9DxfcZ5YJpOwH6Ihe/wn8ch7fMGOP1OhEIvw==
+ dependencies:
+ "@babel/helper-module-imports" "^7.12.13"
+ "@babel/helper-replace-supers" "^7.13.0"
+ "@babel/helper-simple-access" "^7.12.13"
+ "@babel/helper-split-export-declaration" "^7.12.13"
+ "@babel/helper-validator-identifier" "^7.12.11"
+ "@babel/template" "^7.12.13"
+ "@babel/traverse" "^7.13.0"
+ "@babel/types" "^7.13.0"
+ lodash "^4.17.19"
+
+"@babel/helper-optimise-call-expression@^7.12.13":
+ version "7.12.13"
+ resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea"
+ integrity sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==
+ dependencies:
+ "@babel/types" "^7.12.13"
+
+"@babel/helper-replace-supers@^7.13.0":
+ version "7.13.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.0.tgz#6034b7b51943094cb41627848cb219cb02be1d24"
+ integrity sha512-Segd5me1+Pz+rmN/NFBOplMbZG3SqRJOBlY+mA0SxAv6rjj7zJqr1AVr3SfzUVTLCv7ZLU5FycOM/SBGuLPbZw==
+ dependencies:
+ "@babel/helper-member-expression-to-functions" "^7.13.0"
+ "@babel/helper-optimise-call-expression" "^7.12.13"
+ "@babel/traverse" "^7.13.0"
+ "@babel/types" "^7.13.0"
+
+"@babel/helper-simple-access@^7.12.13":
+ version "7.12.13"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz#8478bcc5cacf6aa1672b251c1d2dde5ccd61a6c4"
+ integrity sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==
+ dependencies:
+ "@babel/types" "^7.12.13"
+
+"@babel/helper-split-export-declaration@^7.12.13":
+ version "7.12.13"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05"
+ integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==
+ dependencies:
+ "@babel/types" "^7.12.13"
+
+"@babel/helper-validator-identifier@^7.12.11":
+ version "7.12.11"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed"
+ integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==
+
+"@babel/helper-validator-option@^7.12.17":
+ version "7.12.17"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831"
+ integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==
+
+"@babel/helpers@^7.13.0":
+ version "7.13.0"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.0.tgz#7647ae57377b4f0408bf4f8a7af01c42e41badc0"
+ integrity sha512-aan1MeFPxFacZeSz6Ld7YZo5aPuqnKlD7+HZY75xQsueczFccP9A7V05+oe0XpLwHK3oLorPe9eaAUljL7WEaQ==
+ dependencies:
+ "@babel/template" "^7.12.13"
+ "@babel/traverse" "^7.13.0"
+ "@babel/types" "^7.13.0"
+
+"@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13":
+ version "7.13.8"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.8.tgz#10b2dac78526424dfc1f47650d0e415dfd9dc481"
+ integrity sha512-4vrIhfJyfNf+lCtXC2ck1rKSzDwciqF7IWFhXXrSOUC2O5DrVp+w4c6ed4AllTxhTkUP5x2tYj41VaxdVMMRDw==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.12.11"
chalk "^2.0.0"
js-tokens "^4.0.0"
-"@babel/parser@^7.8.6", "@babel/parser@^7.9.6":
- version "7.9.6"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.6.tgz#3b1bbb30dabe600cd72db58720998376ff653bc7"
- integrity sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==
-
-"@babel/template@^7.8.3", "@babel/template@^7.8.6":
- version "7.8.6"
- resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
- integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==
- dependencies:
- "@babel/code-frame" "^7.8.3"
- "@babel/parser" "^7.8.6"
- "@babel/types" "^7.8.6"
-
-"@babel/traverse@^7.9.6":
- version "7.9.6"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.6.tgz#5540d7577697bf619cc57b92aa0f1c231a94f442"
- integrity sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg==
- dependencies:
- "@babel/code-frame" "^7.8.3"
- "@babel/generator" "^7.9.6"
- "@babel/helper-function-name" "^7.9.5"
- "@babel/helper-split-export-declaration" "^7.8.3"
- "@babel/parser" "^7.9.6"
- "@babel/types" "^7.9.6"
+"@babel/parser@^7.12.13", "@babel/parser@^7.13.0", "@babel/parser@^7.13.4":
+ version "7.13.4"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.4.tgz#340211b0da94a351a6f10e63671fa727333d13ab"
+ integrity sha512-uvoOulWHhI+0+1f9L4BoozY7U5cIkZ9PgJqvb041d6vypgUmtVPG4vmGm4pSggjl8BELzvHyUeJSUyEMY6b+qA==
+
+"@babel/template@^7.12.13":
+ version "7.12.13"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327"
+ integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==
+ dependencies:
+ "@babel/code-frame" "^7.12.13"
+ "@babel/parser" "^7.12.13"
+ "@babel/types" "^7.12.13"
+
+"@babel/traverse@^7.13.0":
+ version "7.13.0"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.0.tgz#6d95752475f86ee7ded06536de309a65fc8966cc"
+ integrity sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ==
+ dependencies:
+ "@babel/code-frame" "^7.12.13"
+ "@babel/generator" "^7.13.0"
+ "@babel/helper-function-name" "^7.12.13"
+ "@babel/helper-split-export-declaration" "^7.12.13"
+ "@babel/parser" "^7.13.0"
+ "@babel/types" "^7.13.0"
debug "^4.1.0"
globals "^11.1.0"
- lodash "^4.17.13"
+ lodash "^4.17.19"
-"@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5", "@babel/types@^7.9.6":
- version "7.9.6"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7"
- integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==
+"@babel/types@^7.12.13", "@babel/types@^7.13.0":
+ version "7.13.0"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80"
+ integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA==
dependencies:
- "@babel/helper-validator-identifier" "^7.9.5"
- lodash "^4.17.13"
+ "@babel/helper-validator-identifier" "^7.12.11"
+ lodash "^4.17.19"
to-fast-properties "^2.0.0"
"@concordance/react@^2.0.0":
@@ -201,42 +230,66 @@
dependencies:
arrify "^1.0.1"
+"@eslint/eslintrc@^0.4.0":
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.0.tgz#99cc0a0584d72f1df38b900fb062ba995f395547"
+ integrity sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==
+ dependencies:
+ ajv "^6.12.4"
+ debug "^4.1.1"
+ espree "^7.3.0"
+ globals "^12.1.0"
+ ignore "^4.0.6"
+ import-fresh "^3.2.1"
+ js-yaml "^3.13.1"
+ minimatch "^3.0.4"
+ strip-json-comments "^3.1.1"
+
"@istanbuljs/load-nyc-config@^1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz#10602de5570baea82f8afbfa2630b24e7a8cfe5b"
- integrity sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
+ integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==
dependencies:
camelcase "^5.3.1"
find-up "^4.1.0"
+ get-package-type "^0.1.0"
js-yaml "^3.13.1"
resolve-from "^5.0.0"
"@istanbuljs/schema@^0.1.2":
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd"
- integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
+ integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
-"@nodelib/fs.scandir@2.1.3":
- version "2.1.3"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
- integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==
+"@nodelib/fs.scandir@2.1.4":
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69"
+ integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==
dependencies:
- "@nodelib/fs.stat" "2.0.3"
+ "@nodelib/fs.stat" "2.0.4"
run-parallel "^1.1.9"
-"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2":
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3"
- integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==
+"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2":
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655"
+ integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==
"@nodelib/fs.walk@^1.2.3":
- version "1.2.4"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976"
- integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063"
+ integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==
dependencies:
- "@nodelib/fs.scandir" "2.1.3"
+ "@nodelib/fs.scandir" "2.1.4"
fastq "^1.6.0"
+"@prettier/plugin-xml@^0.13.0":
+ version "0.13.0"
+ resolved "https://registry.yarnpkg.com/@prettier/plugin-xml/-/plugin-xml-0.13.0.tgz#0927ab554668de0a9c6c5d15c5f94a9346c79a01"
+ integrity sha512-kjC8ZoqrNdSGyUfGS0KGxmA2YxI/mpO2bQtOsg8663DISyfvkN/1LoR8nO+0BaAKkGWhP2rzgk6rqiSEQz8vQw==
+ dependencies:
+ "@xml-tools/parser" "^1.0.2"
+ prettier ">=1.10"
+
"@sindresorhus/is@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
@@ -249,46 +302,29 @@
dependencies:
defer-to-connect "^1.0.1"
-"@types/color-name@^1.1.1":
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
- integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
+"@types/json-schema@^7.0.3":
+ version "7.0.7"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
+ integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
-"@types/events@*":
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
- integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
-
-"@types/glob@^7.1.1":
- version "7.1.1"
- resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
- integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
+"@types/node-forge@^0.9.7":
+ version "0.9.7"
+ resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-0.9.7.tgz#948f7b52d352a6c4ab22d3328c206f0870672db5"
+ integrity sha512-AX7Mqk5ztzSvxqsA/q8y0k0/znJpW6QAqnoLQMEi7A3tio+laRmC/8Q3gbATHT4kZnvhnDmGAy1CpWFzlzCfeA==
dependencies:
- "@types/events" "*"
- "@types/minimatch" "*"
"@types/node" "*"
-"@types/minimatch@*":
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
- integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
-
-"@types/node-forge@^0.9.5":
- version "0.9.5"
- resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-0.9.5.tgz#648231d79da197216290429020698d4e767365a0"
- integrity sha512-rrN3xfA/oZIzwOnO3d2wRQz7UdeVkmMMPjWUCfpPTPuKFVb3D6G10LuiVHYYmvrivBBLMx4m0P/FICoDbNZUMA==
+"@types/node-rsa@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@types/node-rsa/-/node-rsa-1.1.0.tgz#b0864186c1c468179645517c56d83622595e8813"
+ integrity sha512-MDFFz1Fra3Xb4S7ZoWHPyFqq8WxRSI5c1Y/UKq/mD9HUAzGw9fu0LSgSvCzrqndtNPPw3DQS3petBVgQtPGfMA==
dependencies:
"@types/node" "*"
-"@types/node@*":
- version "13.13.5"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.5.tgz#96ec3b0afafd64a4ccea9107b75bf8489f0e5765"
- integrity sha512-3ySmiBYJPqgjiHA7oEaIo2Rzz0HrOZ7yrNO5HWyaE5q0lQ3BppDZ3N53Miz8bw2I7gh1/zir2MGVZBvpb1zq9g==
-
-"@types/node@^11.11.3":
- version "11.15.12"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-11.15.12.tgz#bf5d348c4d37c026029ad81e874946fa6ad100ba"
- integrity sha512-iefeBfpmhoYaZfj+gJM5z9H9eiTwhuzhPsJgH/flx4HP2SBI2FNDra1D3vKljqPLGDr9Cazvh9gP9Xszc30ncA==
+"@types/node@*", "@types/node@^14.14.31":
+ version "14.14.31"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055"
+ integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
@@ -300,46 +336,163 @@
resolved "https://registry.yarnpkg.com/@types/pako/-/pako-1.0.1.tgz#33b237f3c9aff44d0f82fe63acffa4a365ef4a61"
integrity sha512-GdZbRSJ3Cv5fiwT6I0SQ3ckeN2PWNqxd26W9Z2fCK1tGrrasGy4puvNFtnddqH9UJFMQYXxEuuB7B8UK+LLwSg==
-"@types/uuid@3.0.0":
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.0.0.tgz#be93b14bcf97f59c079a9e58754960b5efd946c3"
- integrity sha512-CGaBAcUB4o+YTaUBZ2VfBc7PcIZXwHflZs4FDtr5Kk7MpAl0PVfH7W/VqoHH+OAG41d9YSCV8eWDfz/KKqVm+A==
+"@types/uuid@^8.3.0":
+ version "8.3.0"
+ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f"
+ integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==
+
+"@types/xml-crypto@^1.4.1":
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/@types/xml-crypto/-/xml-crypto-1.4.1.tgz#30824290c0b867302269e7ff31fc62fb6494ce18"
+ integrity sha512-w7pI4Gq1buWinzLsDopd4du0sUzlSltT7QfHqknLu+hVuFWTXLzJnAOmYKuD20ncx3XCkYNwSRr2sKeYiwCvZw==
dependencies:
"@types/node" "*"
+ xpath "0.0.27"
-"@types/xmldom@^0.1.28":
- version "0.1.29"
- resolved "https://registry.yarnpkg.com/@types/xmldom/-/xmldom-0.1.29.tgz#c4428b0ca86d3b881475726fd94980b38a27c381"
- integrity sha1-xEKLDKhtO4gUdXJv2UmAs4onw4E=
+"@types/xml-encryption@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@types/xml-encryption/-/xml-encryption-1.2.0.tgz#5615c4931c8260365718003fc456708ba5495f41"
+ integrity sha512-+/QeXG3B0Z7aC6Dm+TvGkhWUuMVJczhA1+6gSwp0Rf14G+miy9IBLxC2s/o13271aHVo3DPqPOCI1YG8+0DnCg==
+ dependencies:
+ "@types/node" "*"
-acorn-walk@^7.1.1:
- version "7.1.1"
- resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e"
- integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==
+"@types/xml@^1.0.5":
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/@types/xml/-/xml-1.0.5.tgz#5f647b6719cdbcfd026d7e73f295035e49cae0bd"
+ integrity sha512-h3PVM7waRi2UeoaY2BhpLGvettU/3vfCbsjXMV/9Ex5WjvIy82J8Qfp1xiPxM4kTSOLdFFpjRwQ7YY7XJeKBvg==
+ dependencies:
+ "@types/node" "*"
-acorn@^7.1.1:
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe"
- integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==
+"@types/xmldom@^0.1.30":
+ version "0.1.30"
+ resolved "https://registry.yarnpkg.com/@types/xmldom/-/xmldom-0.1.30.tgz#d36d9a7d64af4693d3b18d5dc02ce432a95be12e"
+ integrity sha512-edqgAFXMEtVvaBZ3YnhamvmrHjoYpuxETmnb0lbTZmf/dXpAsO9ZKotUO4K2rn2SIZBDFCMOuA7fOe0H6dRZcA==
+
+"@typescript-eslint/eslint-plugin@^4.15.2":
+ version "4.15.2"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.2.tgz#981b26b4076c62a5a55873fbef3fe98f83360c61"
+ integrity sha512-uiQQeu9tWl3f1+oK0yoAv9lt/KXO24iafxgQTkIYO/kitruILGx3uH+QtIAHqxFV+yIsdnJH+alel9KuE3J15Q==
+ dependencies:
+ "@typescript-eslint/experimental-utils" "4.15.2"
+ "@typescript-eslint/scope-manager" "4.15.2"
+ debug "^4.1.1"
+ functional-red-black-tree "^1.0.1"
+ lodash "^4.17.15"
+ regexpp "^3.0.0"
+ semver "^7.3.2"
+ tsutils "^3.17.1"
+
+"@typescript-eslint/experimental-utils@4.15.2":
+ version "4.15.2"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.2.tgz#5efd12355bd5b535e1831282e6cf465b9a71cf36"
+ integrity sha512-Fxoshw8+R5X3/Vmqwsjc8nRO/7iTysRtDqx6rlfLZ7HbT8TZhPeQqbPjTyk2RheH3L8afumecTQnUc9EeXxohQ==
+ dependencies:
+ "@types/json-schema" "^7.0.3"
+ "@typescript-eslint/scope-manager" "4.15.2"
+ "@typescript-eslint/types" "4.15.2"
+ "@typescript-eslint/typescript-estree" "4.15.2"
+ eslint-scope "^5.0.0"
+ eslint-utils "^2.0.0"
+
+"@typescript-eslint/parser@^4.15.2":
+ version "4.15.2"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.15.2.tgz#c804474321ef76a3955aec03664808f0d6e7872e"
+ integrity sha512-SHeF8xbsC6z2FKXsaTb1tBCf0QZsjJ94H6Bo51Y1aVEZ4XAefaw5ZAilMoDPlGghe+qtq7XdTiDlGfVTOmvA+Q==
+ dependencies:
+ "@typescript-eslint/scope-manager" "4.15.2"
+ "@typescript-eslint/types" "4.15.2"
+ "@typescript-eslint/typescript-estree" "4.15.2"
+ debug "^4.1.1"
+
+"@typescript-eslint/scope-manager@4.15.2":
+ version "4.15.2"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.15.2.tgz#5725bda656995960ae1d004bfd1cd70320f37f4f"
+ integrity sha512-Zm0tf/MSKuX6aeJmuXexgdVyxT9/oJJhaCkijv0DvJVT3ui4zY6XYd6iwIo/8GEZGy43cd7w1rFMiCLHbRzAPQ==
+ dependencies:
+ "@typescript-eslint/types" "4.15.2"
+ "@typescript-eslint/visitor-keys" "4.15.2"
+
+"@typescript-eslint/types@4.15.2":
+ version "4.15.2"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.15.2.tgz#04acf3a2dc8001a88985291744241e732ef22c60"
+ integrity sha512-r7lW7HFkAarfUylJ2tKndyO9njwSyoy6cpfDKWPX6/ctZA+QyaYscAHXVAfJqtnY6aaTwDYrOhp+ginlbc7HfQ==
+
+"@typescript-eslint/typescript-estree@4.15.2":
+ version "4.15.2"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.2.tgz#c2f7a1e94f3428d229d5ecff3ead6581ee9b62fa"
+ integrity sha512-cGR8C2g5SPtHTQvAymEODeqx90pJHadWsgTtx6GbnTWKqsg7yp6Eaya9nFzUd4KrKhxdYTTFBiYeTPQaz/l8bw==
+ dependencies:
+ "@typescript-eslint/types" "4.15.2"
+ "@typescript-eslint/visitor-keys" "4.15.2"
+ debug "^4.1.1"
+ globby "^11.0.1"
+ is-glob "^4.0.1"
+ semver "^7.3.2"
+ tsutils "^3.17.1"
+
+"@typescript-eslint/visitor-keys@4.15.2":
+ version "4.15.2"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.2.tgz#3d1c7979ce75bf6acf9691109bd0d6b5706192b9"
+ integrity sha512-TME1VgSb7wTwgENN5KVj4Nqg25hP8DisXxNBojM4Nn31rYaNDIocNm5cmjOFfh42n7NVERxWrDFoETO/76ePyg==
+ dependencies:
+ "@typescript-eslint/types" "4.15.2"
+ eslint-visitor-keys "^2.0.0"
+
+"@xml-tools/parser@^1.0.2":
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/@xml-tools/parser/-/parser-1.0.10.tgz#d1dd3a4323d713fcca5279005c96b37058ccbc33"
+ integrity sha512-9oRb68wEKT+MRB7e2GwTiKicRKVXKzquBDGgH6YcGafvnSYXorWi2oaTVtbv2109RlGiQSnoXaQFUXCnHwFS7Q==
+ dependencies:
+ chevrotain "7.1.1"
+
+acorn-jsx@^5.3.1:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
+ integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
+
+acorn-walk@^8.0.0:
+ version "8.0.2"
+ resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.0.2.tgz#d4632bfc63fd93d0f15fd05ea0e984ffd3f5a8c3"
+ integrity sha512-+bpA9MJsHdZ4bgfDcpk0ozQyhhVct7rzOmO0s1IIr0AGGgKBljss8n2zp11rRP2wid5VGeh04CgeKzgat5/25A==
+
+acorn@^7.4.0:
+ version "7.4.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
+ integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
+
+acorn@^8.0.4:
+ version "8.0.5"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.0.5.tgz#a3bfb872a74a6a7f661bc81b9849d9cac12601b7"
+ integrity sha512-v+DieK/HJkJOpFBETDJioequtc3PfxsWMaxIdIwujtF7FEV/MAyDQLlm6/zPvr7Mix07mLh6ccVwIsloceodlg==
aggregate-error@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0"
- integrity sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
+ integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==
dependencies:
clean-stack "^2.0.0"
indent-string "^4.0.0"
-ajv@^6.5.5:
- version "6.12.2"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd"
- integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==
+ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4:
+ version "6.12.6"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
+ajv@^7.0.2:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.1.1.tgz#1e6b37a454021fa9941713f38b952fc1c8d32a84"
+ integrity sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ json-schema-traverse "^1.0.0"
+ require-from-string "^2.0.2"
+ uri-js "^4.2.2"
+
ansi-align@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb"
@@ -347,10 +500,10 @@ ansi-align@^3.0.0:
dependencies:
string-width "^3.0.0"
-ansi-regex@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
- integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
+ansi-colors@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
+ integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
ansi-regex@^4.1.0:
version "4.1.0"
@@ -369,14 +522,18 @@ ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
-ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.2.1:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
- integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
dependencies:
- "@types/color-name" "^1.1.1"
color-convert "^2.0.1"
+ansi-styles@^5.0.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.1.0.tgz#a436bcc51d81f4fab5080b2ba94242e377c41573"
+ integrity sha512-osxifZo3ar56+e8tdYreU6p8FZGciBHo5O0JoDAxMUqZuyNUb+yHEwYtJZ+Z32R459jEgtwVf1u8D7qYwU0l6w==
+
anymatch@~3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142"
@@ -457,65 +614,66 @@ asynckit@^0.4.0:
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
ava@^3.8.2:
- version "3.8.2"
- resolved "https://registry.yarnpkg.com/ava/-/ava-3.8.2.tgz#877c9eb861763a185bbabd54f359e1fbe57d1754"
- integrity sha512-sph3oUsVTGsq4qbgeWys03QKCmXjkZUO3oPnFWXEW6g1SReCY9vuONGghMgw1G6VOzkg1k+niqJsOzwfO8h9Ng==
+ version "3.15.0"
+ resolved "https://registry.yarnpkg.com/ava/-/ava-3.15.0.tgz#a239658ab1de8a29a243cc902e6b42e4574de2f0"
+ integrity sha512-HGAnk1SHPk4Sx6plFAUkzV/XC1j9+iQhOzt4vBly18/yo0AV8Oytx7mtJd/CR8igCJ5p160N/Oo/cNJi2uSeWA==
dependencies:
"@concordance/react" "^2.0.0"
- acorn "^7.1.1"
- acorn-walk "^7.1.1"
- ansi-styles "^4.2.1"
+ acorn "^8.0.4"
+ acorn-walk "^8.0.0"
+ ansi-styles "^5.0.0"
arrgv "^1.0.2"
arrify "^2.0.1"
callsites "^3.1.0"
- chalk "^4.0.0"
- chokidar "^3.4.0"
+ chalk "^4.1.0"
+ chokidar "^3.4.3"
chunkd "^2.0.1"
ci-info "^2.0.0"
- ci-parallel-vars "^1.0.0"
+ ci-parallel-vars "^1.0.1"
clean-yaml-object "^0.1.0"
cli-cursor "^3.1.0"
cli-truncate "^2.1.0"
- code-excerpt "^2.1.1"
+ code-excerpt "^3.0.0"
common-path-prefix "^3.0.0"
- concordance "^4.0.0"
+ concordance "^5.0.1"
convert-source-map "^1.7.0"
currently-unhandled "^0.4.1"
- debug "^4.1.1"
- del "^5.1.0"
- emittery "^0.6.0"
+ debug "^4.3.1"
+ del "^6.0.0"
+ emittery "^0.8.0"
equal-length "^1.0.0"
figures "^3.2.0"
- globby "^11.0.0"
- ignore-by-default "^1.0.0"
+ globby "^11.0.1"
+ ignore-by-default "^2.0.0"
import-local "^3.0.2"
indent-string "^4.0.0"
is-error "^2.2.2"
- is-plain-object "^3.0.0"
+ is-plain-object "^5.0.0"
is-promise "^4.0.0"
- lodash "^4.17.15"
+ lodash "^4.17.20"
matcher "^3.0.0"
md5-hex "^3.0.1"
- mem "^6.1.0"
- ms "^2.1.2"
- ora "^4.0.4"
+ mem "^8.0.0"
+ ms "^2.1.3"
+ ora "^5.2.0"
+ p-event "^4.2.0"
p-map "^4.0.0"
picomatch "^2.2.2"
pkg-conf "^3.1.0"
plur "^4.0.0"
- pretty-ms "^7.0.0"
+ pretty-ms "^7.0.1"
read-pkg "^5.2.0"
resolve-cwd "^3.0.0"
slash "^3.0.0"
source-map-support "^0.5.19"
- stack-utils "^2.0.2"
+ stack-utils "^2.0.3"
strip-ansi "^6.0.0"
- supertap "^1.0.0"
+ supertap "^2.0.0"
temp-dir "^2.0.0"
trim-off-newlines "^1.0.1"
- update-notifier "^4.1.0"
+ update-notifier "^5.0.1"
write-file-atomic "^3.0.3"
- yargs "^15.3.1"
+ yargs "^16.2.0"
aws-sign2@~0.7.0:
version "0.7.0"
@@ -523,15 +681,20 @@ aws-sign2@~0.7.0:
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
aws4@^1.8.0:
- version "1.9.1"
- resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
- integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
+ integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+base64-js@^1.3.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+ integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
@@ -540,28 +703,37 @@ bcrypt-pbkdf@^1.0.0:
tweetnacl "^0.14.3"
binary-extensions@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
- integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
+ integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
+
+bl@^4.0.3:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
+ integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
+ dependencies:
+ buffer "^5.5.0"
+ inherits "^2.0.4"
+ readable-stream "^3.4.0"
blueimp-md5@^2.10.0:
- version "2.15.0"
- resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.15.0.tgz#ae945f87ca6c2c11e2562983e11450b0545f9bb3"
- integrity sha512-Zc6sowqlCWu3+V0bocZwdaPPXlRv14EHtYcQDCOghj9EdyKLMkAOODBh3HHAx5r7QRylDYCOaXa/b/edgBLDpA==
+ version "2.18.0"
+ resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.18.0.tgz#1152be1335f0c6b3911ed9e36db54f3e6ac52935"
+ integrity sha512-vE52okJvzsVWhcgUHOv+69OG3Mdg151xyn41aVQN/5W5S+S43qZhxECtYLAEHMSFWX6Mv5IZrzj3T5+JqXfj5Q==
-boxen@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64"
- integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==
+boxen@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.0.0.tgz#64fe9b16066af815f51057adcc800c3730120854"
+ integrity sha512-5bvsqw+hhgUi3oYGK0Vf4WpIkyemp60WBInn7+WNfoISzAqk/HX4L7WNROq38E6UR/y3YADpv6pEm4BfkeEAdA==
dependencies:
ansi-align "^3.0.0"
- camelcase "^5.3.1"
- chalk "^3.0.0"
- cli-boxes "^2.2.0"
- string-width "^4.1.0"
- term-size "^2.1.0"
- type-fest "^0.8.1"
+ camelcase "^6.2.0"
+ chalk "^4.1.0"
+ cli-boxes "^2.2.1"
+ string-width "^4.2.0"
+ type-fest "^0.20.2"
widest-line "^3.1.0"
+ wrap-ansi "^7.0.0"
brace-expansion@^1.1.7:
version "1.1.11"
@@ -578,15 +750,29 @@ braces@^3.0.1, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
+browserslist@^4.14.5:
+ version "4.16.3"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717"
+ integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==
+ dependencies:
+ caniuse-lite "^1.0.30001181"
+ colorette "^1.2.1"
+ electron-to-chromium "^1.3.649"
+ escalade "^3.1.1"
+ node-releases "^1.1.70"
+
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
-builtin-modules@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
- integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=
+buffer@^5.5.0:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
+ integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
+ dependencies:
+ base64-js "^1.3.1"
+ ieee754 "^1.1.13"
cacheable-request@^6.0.0:
version "6.1.0"
@@ -611,7 +797,7 @@ caching-transform@^4.0.0:
package-hash "^4.0.0"
write-file-atomic "^3.0.0"
-callsites@^3.1.0:
+callsites@^3.0.0, callsites@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
@@ -621,12 +807,22 @@ camelcase@^5.0.0, camelcase@^5.3.1:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+camelcase@^6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
+ integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
+
+caniuse-lite@^1.0.30001181:
+ version "1.0.30001192"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001192.tgz#b848ebc0ab230cf313d194a4775a30155d50ae40"
+ integrity sha512-63OrUnwJj5T1rUmoyqYTdRWBqFFxZFlyZnRRjDR8NSUQFB6A+j/uBORU/SyJ5WzDLg4SPiZH40hQCBNdZ/jmAw==
+
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
-chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.2:
+chalk@^2.0.0:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -635,26 +831,25 @@ chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.2:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
-chalk@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
- integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
+chalk@^4.0.0, chalk@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
+ integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
-chalk@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72"
- integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==
+chevrotain@7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/chevrotain/-/chevrotain-7.1.1.tgz#5122814eafd1585a9601f9180a7be9c42d5699c6"
+ integrity sha512-wy3mC1x4ye+O+QkEinVJkPf5u2vsrDIYW9G7ZuwFl6v/Yu0LwUuT2POsb+NUWApebyxfkQq6+yDfRExbnI5rcw==
dependencies:
- ansi-styles "^4.1.0"
- supports-color "^7.1.0"
+ regexp-to-ast "0.5.0"
-chokidar@^3.4.0:
- version "3.4.0"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.0.tgz#b30611423ce376357c765b9b8f904b9fba3c0be8"
- integrity sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==
+chokidar@^3.4.3:
+ version "3.5.1"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a"
+ integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==
dependencies:
anymatch "~3.1.1"
braces "~3.0.2"
@@ -662,9 +857,9 @@ chokidar@^3.4.0:
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
- readdirp "~3.4.0"
+ readdirp "~3.5.0"
optionalDependencies:
- fsevents "~2.1.2"
+ fsevents "~2.3.1"
chunkd@^2.0.1:
version "2.0.1"
@@ -676,10 +871,10 @@ ci-info@^2.0.0:
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
-ci-parallel-vars@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/ci-parallel-vars/-/ci-parallel-vars-1.0.0.tgz#af97729ed1c7381911ca37bcea263d62638701b3"
- integrity sha512-u6dx20FBXm+apMi+5x7UVm6EH7BL1gc4XrcnQewjcB7HWRcor/V5qWc3RG2HwpgDJ26gIi2DSEu3B7sXynAw/g==
+ci-parallel-vars@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz#e87ff0625ccf9d286985b29b4ada8485ca9ffbc2"
+ integrity sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==
clean-stack@^2.0.0:
version "2.2.0"
@@ -691,10 +886,10 @@ clean-yaml-object@^0.1.0:
resolved "https://registry.yarnpkg.com/clean-yaml-object/-/clean-yaml-object-0.1.0.tgz#63fb110dc2ce1a84dc21f6d9334876d010ae8b68"
integrity sha1-Y/sRDcLOGoTcIfbZM0h20BCui2g=
-cli-boxes@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d"
- integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==
+cli-boxes@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
+ integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
cli-cursor@^3.1.0:
version "3.1.0"
@@ -703,10 +898,10 @@ cli-cursor@^3.1.0:
dependencies:
restore-cursor "^3.1.0"
-cli-spinners@^2.2.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.3.0.tgz#0632239a4b5aa4c958610142c34bb7a651fc8df5"
- integrity sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w==
+cli-spinners@^2.5.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.5.0.tgz#12763e47251bf951cb75c201dfa58ff1bcb2d047"
+ integrity sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ==
cli-truncate@^2.1.0:
version "2.1.0"
@@ -725,6 +920,15 @@ cliui@^6.0.0:
strip-ansi "^6.0.0"
wrap-ansi "^6.2.0"
+cliui@^7.0.2:
+ version "7.0.4"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
+ integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.0"
+ wrap-ansi "^7.0.0"
+
clone-response@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
@@ -737,10 +941,10 @@ clone@^1.0.2:
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
-code-excerpt@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/code-excerpt/-/code-excerpt-2.1.1.tgz#5fe3057bfbb71a5f300f659ef2cc0a47651ba77c"
- integrity sha512-tJLhH3EpFm/1x7heIW0hemXJTUU5EWl2V0EIX558jp05Mt1U6DVryCgkp3l37cxqs+DNbNgxG43SkwJXpQ14Jw==
+code-excerpt@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/code-excerpt/-/code-excerpt-3.0.0.tgz#fcfb6748c03dba8431c19f5474747fad3f250f10"
+ integrity sha512-VHNTVhd7KsLGOqfX3SyeO8RyYPMp1GJOg194VITk04WMYCv4plV68YWe6TJZxd9MhobjtpMRnVky01gqZsalaw==
dependencies:
convert-to-spaces "^1.0.1"
@@ -768,6 +972,11 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+colorette@^1.2.1:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
+ integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
+
combined-stream@^1.0.6, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
@@ -775,11 +984,6 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
dependencies:
delayed-stream "~1.0.0"
-commander@^2.12.1:
- version "2.20.3"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
- integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
-
common-path-prefix@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0"
@@ -795,21 +999,18 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
-concordance@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/concordance/-/concordance-4.0.0.tgz#5932fdee397d129bdbc3a1885fbe69839b1b7e15"
- integrity sha512-l0RFuB8RLfCS0Pt2Id39/oCPykE01pyxgAFypWTlaGRgvLkZrtczZ8atEHpTeEIW+zYWXTBuA9cCSeEOScxReQ==
+concordance@^5.0.1:
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/concordance/-/concordance-5.0.2.tgz#c242e59b7ee507491e16e9bea85967f7afccec0a"
+ integrity sha512-hC63FKdGM9tBcd4VQIa+LQjmrgorrnxESb8B3J21Qe/FzL0blBv0pb8iNyymt+bmsvGSUqO0uhPi2ZSLgLtLdg==
dependencies:
- date-time "^2.1.0"
- esutils "^2.0.2"
- fast-diff "^1.1.2"
+ date-time "^3.1.0"
+ esutils "^2.0.3"
+ fast-diff "^1.2.0"
js-string-escape "^1.0.1"
- lodash.clonedeep "^4.5.0"
- lodash.flattendeep "^4.4.0"
- lodash.islength "^4.0.1"
- lodash.merge "^4.6.1"
- md5-hex "^2.0.0"
- semver "^5.5.1"
+ lodash "^4.17.15"
+ md5-hex "^3.0.1"
+ semver "^7.3.2"
well-known-symbols "^2.0.0"
configstore@^5.0.1:
@@ -852,10 +1053,15 @@ coveralls@^3.1.0:
minimist "^1.2.5"
request "^2.88.2"
-cross-spawn@^7.0.0:
- version "7.0.2"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.2.tgz#d0d7dcfa74e89115c7619f4f721a94e1fdb716d6"
- integrity sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==
+create-require@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
+ integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
+
+cross-spawn@^7.0.0, cross-spawn@^7.0.2:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+ integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
dependencies:
path-key "^3.1.0"
shebang-command "^2.0.0"
@@ -880,19 +1086,19 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"
-date-time@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/date-time/-/date-time-2.1.0.tgz#0286d1b4c769633b3ca13e1e62558d2dbdc2eba2"
- integrity sha512-/9+C44X7lot0IeiyfgJmETtRMhBidBYM2QFFIkGa0U1k+hSyY87Nw7PY3eDqpvCBm7I3WCSfPeZskW/YYq6m4g==
+date-time@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/date-time/-/date-time-3.1.0.tgz#0d1e934d170579f481ed8df1e2b8ff70ee845e1e"
+ integrity sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==
dependencies:
time-zone "^1.0.0"
-debug@^4.1.0, debug@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
- integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
+debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
+ integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
dependencies:
- ms "^2.1.1"
+ ms "2.1.2"
decamelize@^1.2.0:
version "1.2.0"
@@ -911,6 +1117,11 @@ deep-extend@^0.6.0:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
+deep-is@^0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+ integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
+
default-require-extensions@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-3.0.0.tgz#e03f93aac9b2b6443fc52e5e4a37b3ad9ad8df96"
@@ -930,18 +1141,18 @@ defer-to-connect@^1.0.1:
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==
-del@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/del/-/del-5.1.0.tgz#d9487c94e367410e6eff2925ee58c0c84a75b3a7"
- integrity sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==
+del@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952"
+ integrity sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==
dependencies:
- globby "^10.0.1"
- graceful-fs "^4.2.2"
+ globby "^11.0.1"
+ graceful-fs "^4.2.4"
is-glob "^4.0.1"
is-path-cwd "^2.2.0"
- is-path-inside "^3.0.1"
- p-map "^3.0.0"
- rimraf "^3.0.0"
+ is-path-inside "^3.0.2"
+ p-map "^4.0.0"
+ rimraf "^3.0.2"
slash "^3.0.0"
delayed-stream@~1.0.0:
@@ -961,10 +1172,17 @@ dir-glob@^3.0.1:
dependencies:
path-type "^4.0.0"
+doctrine@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+ integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+ dependencies:
+ esutils "^2.0.2"
+
dot-prop@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
- integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
+ integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==
dependencies:
is-obj "^2.0.0"
@@ -981,10 +1199,15 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
-emittery@^0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.6.0.tgz#e85312468d77c3ed9a6adf43bb57d34849e0c95a"
- integrity sha512-6EMRGr9KzYWp8DzHFZsKVZBsMO6QhAeHMeHND8rhyBNCHKMLpgW9tZv40bwN3rAIKRS5CxcK8oLRKUJSB9h7yQ==
+electron-to-chromium@^1.3.649:
+ version "1.3.675"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.675.tgz#7ad29f98d7b48da581554eb28bb9a71fd5fd4956"
+ integrity sha512-GEQw+6dNWjueXGkGfjgm7dAMtXfEqrfDG3uWcZdeaD4cZ3dKYdPRQVruVXQRXtPLtOr5GNVVlNLRMChOZ611pQ==
+
+emittery@^0.8.0:
+ version "0.8.1"
+ resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860"
+ integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==
emoji-regex@^7.0.1:
version "7.0.3"
@@ -1003,6 +1226,13 @@ end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
+enquirer@^2.3.5:
+ version "2.3.6"
+ resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
+ integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
+ dependencies:
+ ansi-colors "^4.1.1"
+
equal-length@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/equal-length/-/equal-length-1.0.1.tgz#21ca112d48ab24b4e1e7ffc0e5339d31fdfc274c"
@@ -1020,6 +1250,11 @@ es6-error@^4.0.1:
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
+escalade@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+ integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+
escape-goat@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
@@ -1045,12 +1280,132 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+eslint-config-prettier@^8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz#4ef1eaf97afe5176e6a75ddfb57c335121abc5a6"
+ integrity sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==
+
+eslint-plugin-prettier@^3.3.1:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz#7079cfa2497078905011e6f82e8dd8453d1371b7"
+ integrity sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==
+ dependencies:
+ prettier-linter-helpers "^1.0.0"
+
+eslint-plugin-security@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-security/-/eslint-plugin-security-1.4.0.tgz#d4f314484a80b1b613b8c8886e84f52efe1526c2"
+ integrity sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA==
+ dependencies:
+ safe-regex "^1.1.0"
+
+eslint-scope@^5.0.0, eslint-scope@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
+ integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^4.1.1"
+
+eslint-utils@^2.0.0, eslint-utils@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
+ integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
+ dependencies:
+ eslint-visitor-keys "^1.1.0"
+
+eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
+ integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
+
+eslint-visitor-keys@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
+ integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
+
+eslint@^7.20.0:
+ version "7.21.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.21.0.tgz#4ecd5b8c5b44f5dedc9b8a110b01bbfeb15d1c83"
+ integrity sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==
+ dependencies:
+ "@babel/code-frame" "7.12.11"
+ "@eslint/eslintrc" "^0.4.0"
+ ajv "^6.10.0"
+ chalk "^4.0.0"
+ cross-spawn "^7.0.2"
+ debug "^4.0.1"
+ doctrine "^3.0.0"
+ enquirer "^2.3.5"
+ eslint-scope "^5.1.1"
+ eslint-utils "^2.1.0"
+ eslint-visitor-keys "^2.0.0"
+ espree "^7.3.1"
+ esquery "^1.4.0"
+ esutils "^2.0.2"
+ file-entry-cache "^6.0.1"
+ functional-red-black-tree "^1.0.1"
+ glob-parent "^5.0.0"
+ globals "^12.1.0"
+ ignore "^4.0.6"
+ import-fresh "^3.0.0"
+ imurmurhash "^0.1.4"
+ is-glob "^4.0.0"
+ js-yaml "^3.13.1"
+ json-stable-stringify-without-jsonify "^1.0.1"
+ levn "^0.4.1"
+ lodash "^4.17.20"
+ minimatch "^3.0.4"
+ natural-compare "^1.4.0"
+ optionator "^0.9.1"
+ progress "^2.0.0"
+ regexpp "^3.1.0"
+ semver "^7.2.1"
+ strip-ansi "^6.0.0"
+ strip-json-comments "^3.1.0"
+ table "^6.0.4"
+ text-table "^0.2.0"
+ v8-compile-cache "^2.0.3"
+
+espree@^7.3.0, espree@^7.3.1:
+ version "7.3.1"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6"
+ integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==
+ dependencies:
+ acorn "^7.4.0"
+ acorn-jsx "^5.3.1"
+ eslint-visitor-keys "^1.3.0"
+
esprima@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
-esutils@^2.0.2:
+esquery@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5"
+ integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==
+ dependencies:
+ estraverse "^5.1.0"
+
+esrecurse@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+ integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+ dependencies:
+ estraverse "^5.2.0"
+
+estraverse@^4.1.1:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+ integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+estraverse@^5.1.0, estraverse@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
+ integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==
+
+esutils@^2.0.2, esutils@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
@@ -1071,19 +1426,19 @@ extsprintf@^1.2.0:
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
fast-deep-equal@^3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
- integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
-fast-diff@^1.1.2:
+fast-diff@^1.1.2, fast-diff@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
-fast-glob@^3.0.3, fast-glob@^3.1.1:
- version "3.2.2"
- resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.2.tgz#ade1a9d91148965d4bf7c51f72e1ca662d32e63d"
- integrity sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==
+fast-glob@^3.1.1:
+ version "3.2.5"
+ resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661"
+ integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3"
@@ -1097,10 +1452,15 @@ fast-json-stable-stringify@^2.0.0:
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+fast-levenshtein@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
fastq@^1.6.0:
- version "1.7.0"
- resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.7.0.tgz#fcd79a08c5bd7ec5b55cd3f5c4720db551929801"
- integrity sha512-YOadQRnHd5q6PogvAR/x62BGituF2ufiEA6s8aavQANw5YKHERI4AREboX6KotzP8oX2klxYF2wcV/7bn1clfQ==
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858"
+ integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==
dependencies:
reusify "^1.0.4"
@@ -1111,6 +1471,13 @@ figures@^3.2.0:
dependencies:
escape-string-regexp "^1.0.5"
+file-entry-cache@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+ integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+ dependencies:
+ flat-cache "^3.0.4"
+
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@@ -1142,6 +1509,19 @@ find-up@^4.0.0, find-up@^4.1.0:
locate-path "^5.0.0"
path-exists "^4.0.0"
+flat-cache@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
+ integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
+ dependencies:
+ flatted "^3.1.0"
+ rimraf "^3.0.2"
+
+flatted@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469"
+ integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==
+
foreground-child@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53"
@@ -1165,30 +1545,45 @@ form-data@~2.3.2:
mime-types "^2.1.12"
fromentries@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.2.0.tgz#e6aa06f240d6267f913cea422075ef88b63e7897"
- integrity sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ==
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a"
+ integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
-fsevents@~2.1.2:
- version "2.1.3"
- resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e"
- integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
+fsevents@~2.3.1:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
+ integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
+
+function-bind@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+ integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
-gensync@^1.0.0-beta.1:
- version "1.0.0-beta.1"
- resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
- integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==
+functional-red-black-tree@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+ integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
+
+gensync@^1.0.0-beta.2:
+ version "1.0.0-beta.2"
+ resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
+ integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
-get-caller-file@^2.0.1:
+get-caller-file@^2.0.1, get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+get-package-type@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
+ integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
+
get-stream@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
@@ -1197,9 +1592,9 @@ get-stream@^4.1.0:
pump "^3.0.0"
get-stream@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9"
- integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
+ integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
dependencies:
pump "^3.0.0"
@@ -1210,14 +1605,14 @@ getpass@^0.1.1:
dependencies:
assert-plus "^1.0.0"
-glob-parent@^5.1.0, glob-parent@~5.1.0:
+glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
dependencies:
is-glob "^4.0.1"
-glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
+glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@@ -1229,36 +1624,29 @@ glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
once "^1.3.0"
path-is-absolute "^1.0.0"
-global-dirs@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201"
- integrity sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==
+global-dirs@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686"
+ integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==
dependencies:
- ini "^1.3.5"
+ ini "2.0.0"
globals@^11.1.0:
version "11.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
-globby@^10.0.1:
- version "10.0.2"
- resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543"
- integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==
+globals@^12.1.0:
+ version "12.4.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8"
+ integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==
dependencies:
- "@types/glob" "^7.1.1"
- array-union "^2.1.0"
- dir-glob "^3.0.1"
- fast-glob "^3.0.3"
- glob "^7.1.3"
- ignore "^5.1.1"
- merge2 "^1.2.3"
- slash "^3.0.0"
+ type-fest "^0.8.1"
-globby@^11.0.0:
- version "11.0.0"
- resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.0.tgz#56fd0e9f0d4f8fb0c456f1ab0dee96e1380bc154"
- integrity sha512-iuehFnR3xu5wBBtm4xi0dMe92Ob87ufyu/dHwpDYfbcpYpIbrO5OnS8M1vWvrBhSGEJ3/Ecj7gnX76P8YxpPEg==
+globby@^11.0.1:
+ version "11.0.2"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.2.tgz#1af538b766a3b540ebfb58a32b2e2d5897321d83"
+ integrity sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==
dependencies:
array-union "^2.1.0"
dir-glob "^3.0.1"
@@ -1284,10 +1672,10 @@ got@^9.6.0:
to-readable-stream "^1.0.0"
url-parse-lax "^3.0.0"
-graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.2:
- version "4.2.4"
- resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
- integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
+graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.4:
+ version "4.2.6"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
+ integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
har-schema@^2.0.0:
version "2.0.0"
@@ -1295,11 +1683,11 @@ har-schema@^2.0.0:
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
har-validator@~5.1.3:
- version "5.1.3"
- resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
- integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
+ version "5.1.5"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd"
+ integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==
dependencies:
- ajv "^6.5.5"
+ ajv "^6.12.3"
har-schema "^2.0.0"
has-flag@^3.0.0:
@@ -1317,10 +1705,17 @@ has-yarn@^2.1.0:
resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77"
integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==
+has@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+ integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+ dependencies:
+ function-bind "^1.1.1"
+
hasha@^5.0.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.0.tgz#33094d1f69c40a4a6ac7be53d5fe3ff95a269e0c"
- integrity sha512-2W+jKdQbAdSIrggA8Q35Br8qKadTrqCTC8+XZvBWepKDK6m9XkX6Iz1a2yh2KP01kzAR/dpuMeUnocoLYDcskw==
+ version "5.2.2"
+ resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1"
+ integrity sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==
dependencies:
is-stream "^2.0.0"
type-fest "^0.8.0"
@@ -1349,15 +1744,33 @@ http-signature@~1.2.0:
jsprim "^1.2.2"
sshpk "^1.7.0"
-ignore-by-default@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
- integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk=
+ieee754@^1.1.13:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+ integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+
+ignore-by-default@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-2.0.0.tgz#537092018540640459569fe7c8c7a408af581146"
+ integrity sha512-+mQSgMRiFD3L3AOxLYOCxjIq4OnAmo5CIuC+lj5ehCJcPtV++QacEV7FdpzvYxH6DaOySWzQU6RR0lPLy37ckA==
+
+ignore@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
+ integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
-ignore@^5.1.1, ignore@^5.1.4:
- version "5.1.4"
- resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf"
- integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==
+ignore@^5.1.4:
+ version "5.1.8"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
+ integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
+
+import-fresh@^3.0.0, import-fresh@^3.2.1:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+ integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+ dependencies:
+ parent-module "^1.0.0"
+ resolve-from "^4.0.0"
import-lazy@^2.1.0:
version "2.1.0"
@@ -1377,11 +1790,6 @@ imurmurhash@^0.1.4:
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
-indent-string@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289"
- integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=
-
indent-string@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
@@ -1395,15 +1803,20 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@2:
+inherits@2, inherits@^2.0.3, inherits@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
-ini@^1.3.5, ini@~1.3.0:
- version "1.3.7"
- resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
- integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==
+ini@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
+ integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
+
+ini@~1.3.0:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
+ integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
irregular-plurals@^3.2.0:
version "3.2.0"
@@ -1429,6 +1842,13 @@ is-ci@^2.0.0:
dependencies:
ci-info "^2.0.0"
+is-core-module@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a"
+ integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==
+ dependencies:
+ has "^1.0.3"
+
is-error@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/is-error/-/is-error-2.2.2.tgz#c10ade187b3c93510c5470a5567833ee25649843"
@@ -1449,30 +1869,30 @@ is-fullwidth-code-point@^3.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
-is-glob@^4.0.1, is-glob@~4.0.1:
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
dependencies:
is-extglob "^2.1.1"
-is-installed-globally@^0.3.1:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141"
- integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==
+is-installed-globally@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520"
+ integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==
dependencies:
- global-dirs "^2.0.1"
- is-path-inside "^3.0.1"
+ global-dirs "^3.0.0"
+ is-path-inside "^3.0.2"
is-interactive@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
-is-npm@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d"
- integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==
+is-npm@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8"
+ integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==
is-number@^7.0.0:
version "7.0.0"
@@ -1489,17 +1909,15 @@ is-path-cwd@^2.2.0:
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb"
integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==
-is-path-inside@^3.0.1:
+is-path-inside@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017"
integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==
-is-plain-object@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.0.tgz#47bfc5da1b5d50d64110806c199359482e75a928"
- integrity sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==
- dependencies:
- isobject "^4.0.0"
+is-plain-object@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
+ integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
is-promise@^4.0.0:
version "4.0.0"
@@ -1531,11 +1949,6 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
-isobject@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0"
- integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==
-
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@@ -1612,10 +2025,10 @@ js-tokens@^4.0.0:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
-js-yaml@^3.10.0, js-yaml@^3.13.1:
- version "3.13.1"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
- integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
+js-yaml@^3.13.1, js-yaml@^3.14.0:
+ version "3.14.1"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
+ integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
dependencies:
argparse "^1.0.7"
esprima "^4.0.0"
@@ -1640,25 +2053,40 @@ json-parse-better-errors@^1.0.1:
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
+json-parse-even-better-errors@^2.3.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+ integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+json-schema-traverse@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
+ integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
+
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
+json-stable-stringify-without-jsonify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+ integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
+
json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json5@^2.1.2:
- version "2.1.3"
- resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
- integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
+ integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
dependencies:
minimist "^1.2.5"
@@ -1679,7 +2107,7 @@ keyv@^3.0.0:
dependencies:
json-buffer "3.0.0"
-latest-version@^5.0.0:
+latest-version@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face"
integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==
@@ -1691,6 +2119,14 @@ lcov-parse@^1.0.0:
resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-1.0.0.tgz#eb0d46b54111ebc561acb4c408ef9363bdc8f7e0"
integrity sha1-6w1GtUER68VhrLTECO+TY73I9+A=
+levn@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+ integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+ dependencies:
+ prelude-ls "^1.2.1"
+ type-check "~0.4.0"
+
lines-and-columns@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
@@ -1722,42 +2158,27 @@ locate-path@^5.0.0:
dependencies:
p-locate "^4.1.0"
-lodash.clonedeep@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
- integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
-
lodash.flattendeep@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=
-lodash.islength@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/lodash.islength/-/lodash.islength-4.0.1.tgz#4e9868d452575d750affd358c979543dc20ed577"
- integrity sha1-Tpho1FJXXXUK/9NYyXlUPcIO1Xc=
-
-lodash.merge@^4.6.1:
- version "4.6.2"
- resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
- integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
-
-lodash@^4.17.13, lodash@^4.17.15:
- version "4.17.19"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
- integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
+lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log-driver@^1.2.7:
version "1.2.7"
resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8"
integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==
-log-symbols@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4"
- integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==
+log-symbols@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920"
+ integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==
dependencies:
- chalk "^2.4.2"
+ chalk "^4.0.0"
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
version "1.0.1"
@@ -1769,6 +2190,13 @@ lowercase-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
+lru-cache@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+ integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+ dependencies:
+ yallist "^4.0.0"
+
make-dir@^3.0.0, make-dir@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
@@ -1795,13 +2223,6 @@ matcher@^3.0.0:
dependencies:
escape-string-regexp "^4.0.0"
-md5-hex@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/md5-hex/-/md5-hex-2.0.0.tgz#d0588e9f1c74954492ecd24ac0ac6ce997d92e33"
- integrity sha1-0FiOnxx0lUSS7NJKwKxs6ZfZLjM=
- dependencies:
- md5-o-matic "^0.1.1"
-
md5-hex@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/md5-hex/-/md5-hex-3.0.1.tgz#be3741b510591434b2784d79e556eefc2c9a8e5c"
@@ -1809,23 +2230,18 @@ md5-hex@^3.0.1:
dependencies:
blueimp-md5 "^2.10.0"
-md5-o-matic@^0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/md5-o-matic/-/md5-o-matic-0.1.1.tgz#822bccd65e117c514fab176b25945d54100a03c3"
- integrity sha1-givM1l4RfFFPqxdrJZRdVBAKA8M=
-
-mem@^6.1.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/mem/-/mem-6.1.0.tgz#846eca0bd4708a8f04b9c3f3cd769e194ae63c5c"
- integrity sha512-RlbnLQgRHk5lwqTtpEkBTQ2ll/CG/iB+J4Hy2Wh97PjgZgXgWJWrFF+XXujh3UUVLvR4OOTgZzcWMMwnehlEUg==
+mem@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/mem/-/mem-8.0.0.tgz#b5e4b6d2d241c6296da05436173b4d0c7ae1f9ac"
+ integrity sha512-qrcJOe6uD+EW8Wrci1Vdiua/15Xw3n/QnaNXE7varnB6InxSk7nu3/i5jfy3S6kWxr8WYJ6R1o0afMUtvorTsA==
dependencies:
map-age-cleaner "^0.1.3"
- mimic-fn "^3.0.0"
+ mimic-fn "^3.1.0"
-merge2@^1.2.3, merge2@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81"
- integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==
+merge2@^1.3.0:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
+ integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
micromatch@^4.0.2:
version "4.0.2"
@@ -1835,27 +2251,27 @@ micromatch@^4.0.2:
braces "^3.0.1"
picomatch "^2.0.5"
-mime-db@1.44.0:
- version "1.44.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
- integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
+mime-db@1.46.0:
+ version "1.46.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee"
+ integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==
mime-types@^2.1.12, mime-types@~2.1.19:
- version "2.1.27"
- resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
- integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
+ version "2.1.29"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.29.tgz#1d4ab77da64b91f5f72489df29236563754bb1b2"
+ integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==
dependencies:
- mime-db "1.44.0"
+ mime-db "1.46.0"
mimic-fn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
-mimic-fn@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.0.0.tgz#76044cfa8818bbf6999c5c9acadf2d3649b14b4b"
- integrity sha512-PiVO95TKvhiwgSwg1IdLYlCTdul38yZxZMIcnDSFIBUm4BNZha2qpQ4GpJ++15bHoKDtrW2D69lMfFwdFYtNZQ==
+mimic-fn@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74"
+ integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==
mimic-response@^1.0.0, mimic-response@^1.0.1:
version "1.0.1"
@@ -1874,22 +2290,20 @@ minimist@^1.2.0, minimist@^1.2.5:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
-mkdirp@^0.5.3:
- version "0.5.5"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
- integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
- dependencies:
- minimist "^1.2.5"
-
-ms@^2.1.1, ms@^2.1.2:
+ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-mute-stream@0.0.8:
- version "0.0.8"
- resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
- integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
+ms@^2.1.3:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+natural-compare@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+ integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
node-forge@^0.10.0:
version "0.10.0"
@@ -1903,10 +2317,15 @@ node-preload@^0.2.1:
dependencies:
process-on-spawn "^1.0.0"
-node-rsa@^1.0.5:
- version "1.0.8"
- resolved "https://registry.yarnpkg.com/node-rsa/-/node-rsa-1.0.8.tgz#29a4517380f3272cd2073ff4d4c1ca44127ea4ad"
- integrity sha512-q8knkMHEqViIX/fshOltCHTtlt4Nw5wpBpu0//LB1tkxqYZB/001dYMwbPvTPiENwKvPqVDkhxK6J4fV09oa7w==
+node-releases@^1.1.70:
+ version "1.1.71"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb"
+ integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==
+
+node-rsa@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/node-rsa/-/node-rsa-1.1.1.tgz#efd9ad382097782f506153398496f79e4464434d"
+ integrity sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw==
dependencies:
asn1 "^0.2.4"
@@ -1931,9 +2350,9 @@ normalize-url@^4.1.0:
integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
nyc@^15.0.1:
- version "15.0.1"
- resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.0.1.tgz#bd4d5c2b17f2ec04370365a5ca1fc0ed26f9f93d"
- integrity sha512-n0MBXYBYRqa67IVt62qW1r/d9UH/Qtr7SF1w/nQLJ9KxvWF6b2xCHImRAixHN9tnMMYHC2P14uo6KddNGwMgGg==
+ version "15.1.0"
+ resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.1.0.tgz#1335dae12ddc87b6e249d5a1994ca4bdaea75f02"
+ integrity sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==
dependencies:
"@istanbuljs/load-nyc-config" "^1.0.0"
"@istanbuljs/schema" "^0.1.2"
@@ -1943,6 +2362,7 @@ nyc@^15.0.1:
find-cache-dir "^3.2.0"
find-up "^4.1.0"
foreground-child "^2.0.0"
+ get-package-type "^0.1.0"
glob "^7.1.6"
istanbul-lib-coverage "^3.0.0"
istanbul-lib-hook "^3.0.0"
@@ -1975,23 +2395,35 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
wrappy "1"
onetime@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5"
- integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+ integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
dependencies:
mimic-fn "^2.1.0"
-ora@^4.0.4:
- version "4.0.4"
- resolved "https://registry.yarnpkg.com/ora/-/ora-4.0.4.tgz#e8da697cc5b6a47266655bf68e0fb588d29a545d"
- integrity sha512-77iGeVU1cIdRhgFzCK8aw1fbtT1B/iZAvWjS+l/o1x0RShMgxHUZaD2yDpWsNCPwXg9z1ZA78Kbdvr8kBmG/Ww==
+optionator@^0.9.1:
+ version "0.9.1"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
+ integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
dependencies:
- chalk "^3.0.0"
+ deep-is "^0.1.3"
+ fast-levenshtein "^2.0.6"
+ levn "^0.4.1"
+ prelude-ls "^1.2.1"
+ type-check "^0.4.0"
+ word-wrap "^1.2.3"
+
+ora@^5.2.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/ora/-/ora-5.3.0.tgz#fb832899d3a1372fe71c8b2c534bbfe74961bb6f"
+ integrity sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==
+ dependencies:
+ bl "^4.0.3"
+ chalk "^4.1.0"
cli-cursor "^3.1.0"
- cli-spinners "^2.2.0"
+ cli-spinners "^2.5.0"
is-interactive "^1.0.0"
- log-symbols "^3.0.0"
- mute-stream "0.0.8"
+ log-symbols "^4.0.0"
strip-ansi "^6.0.0"
wcwidth "^1.0.1"
@@ -2005,6 +2437,18 @@ p-defer@^1.0.0:
resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=
+p-event@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5"
+ integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==
+ dependencies:
+ p-timeout "^3.1.0"
+
+p-finally@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+ integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
+
p-limit@^2.0.0, p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
@@ -2040,6 +2484,13 @@ p-map@^4.0.0:
dependencies:
aggregate-error "^3.0.0"
+p-timeout@^3.1.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe"
+ integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==
+ dependencies:
+ p-finally "^1.0.0"
+
p-try@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
@@ -2065,11 +2516,18 @@ package-json@^6.3.0:
registry-url "^5.0.0"
semver "^6.2.0"
-pako@^1.0.10:
+pako@^1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
+parent-module@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+ integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+ dependencies:
+ callsites "^3.0.0"
+
parse-json@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
@@ -2079,13 +2537,13 @@ parse-json@^4.0.0:
json-parse-better-errors "^1.0.1"
parse-json@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.0.0.tgz#73e5114c986d143efa3712d4ea24db9a4266f60f"
- integrity sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
+ integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
dependencies:
"@babel/code-frame" "^7.0.0"
error-ex "^1.3.1"
- json-parse-better-errors "^1.0.1"
+ json-parse-even-better-errors "^2.3.0"
lines-and-columns "^1.1.6"
parse-ms@^2.1.0:
@@ -2160,15 +2618,32 @@ plur@^4.0.0:
dependencies:
irregular-plurals "^3.2.0"
+prelude-ls@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+ integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
prepend-http@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
-pretty-ms@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.0.tgz#45781273110caf35f55cab21a8a9bd403a233dc0"
- integrity sha512-J3aPWiC5e9ZeZFuSeBraGxSkGMOvulSWsxDByOcbD1Pr75YL3LSNIKIb52WXbCLE1sS5s4inBBbryjF4Y05Ceg==
+prettier-linter-helpers@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
+ integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
+ dependencies:
+ fast-diff "^1.1.2"
+
+prettier@>=1.10, prettier@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5"
+ integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==
+
+pretty-ms@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8"
+ integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==
dependencies:
parse-ms "^2.1.0"
@@ -2179,6 +2654,11 @@ process-on-spawn@^1.0.0:
dependencies:
fromentries "^1.2.0"
+progress@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
+ integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
+
psl@^1.1.28:
version "1.8.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
@@ -2197,10 +2677,10 @@ punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
-pupa@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726"
- integrity sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==
+pupa@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62"
+ integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==
dependencies:
escape-goat "^2.0.0"
@@ -2209,6 +2689,11 @@ qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
+queue-microtask@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.2.tgz#abf64491e6ecf0f38a6502403d4cda04f372dfd3"
+ integrity sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg==
+
rc@^1.2.8:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
@@ -2229,17 +2714,36 @@ read-pkg@^5.2.0:
parse-json "^5.0.0"
type-fest "^0.6.0"
-readdirp@~3.4.0:
- version "3.4.0"
- resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada"
- integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==
+readable-stream@^3.4.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
+ integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
+ dependencies:
+ inherits "^2.0.3"
+ string_decoder "^1.1.1"
+ util-deprecate "^1.0.1"
+
+readdirp@~3.5.0:
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e"
+ integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==
dependencies:
picomatch "^2.2.1"
+regexp-to-ast@0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz#56c73856bee5e1fef7f73a00f1473452ab712a24"
+ integrity sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==
+
+regexpp@^3.0.0, regexpp@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
+ integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==
+
registry-auth-token@^4.0.0:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.1.1.tgz#40a33be1e82539460f94328b0f7f0f84c16d9479"
- integrity sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250"
+ integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==
dependencies:
rc "^1.2.8"
@@ -2288,6 +2792,11 @@ require-directory@^2.1.1:
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
+require-from-string@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
+ integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+
require-main-filename@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
@@ -2300,16 +2809,22 @@ resolve-cwd@^3.0.0:
dependencies:
resolve-from "^5.0.0"
+resolve-from@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+ integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
resolve-from@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
-resolve@^1.10.0, resolve@^1.3.2:
- version "1.17.0"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
- integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
+resolve@^1.10.0:
+ version "1.20.0"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
+ integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
dependencies:
+ is-core-module "^2.2.0"
path-parse "^1.0.6"
responselike@^1.0.2:
@@ -2327,12 +2842,17 @@ restore-cursor@^3.1.0:
onetime "^5.1.0"
signal-exit "^3.0.2"
+ret@~0.1.10:
+ version "0.1.15"
+ resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
+ integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
+
reusify@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
-rimraf@^3.0.0:
+rimraf@^3.0.0, rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
@@ -2340,11 +2860,13 @@ rimraf@^3.0.0:
glob "^7.1.3"
run-parallel@^1.1.9:
- version "1.1.9"
- resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
- integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+ integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+ dependencies:
+ queue-microtask "^1.2.2"
-safe-buffer@^5.0.1, safe-buffer@^5.1.2:
+safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@@ -2354,6 +2876,13 @@ safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+safe-regex@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
+ integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
+ dependencies:
+ ret "~0.1.10"
+
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
@@ -2366,7 +2895,7 @@ semver-diff@^3.1.1:
dependencies:
semver "^6.3.0"
-"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.1:
+"semver@2 || 3 || 4 || 5":
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@@ -2376,10 +2905,19 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
-serialize-error@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a"
- integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go=
+semver@^7.2.1, semver@^7.3.2, semver@^7.3.4:
+ version "7.3.4"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
+ integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
+ dependencies:
+ lru-cache "^6.0.0"
+
+serialize-error@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18"
+ integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==
+ dependencies:
+ type-fest "^0.13.1"
set-blocking@^2.0.0:
version "2.0.0"
@@ -2417,6 +2955,15 @@ slice-ansi@^3.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
+slice-ansi@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
+ integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
+ dependencies:
+ ansi-styles "^4.0.0"
+ astral-regex "^2.0.0"
+ is-fullwidth-code-point "^3.0.0"
+
source-map-support@^0.5.17, source-map-support@^0.5.19:
version "0.5.19"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
@@ -2448,9 +2995,9 @@ spawn-wrap@^2.0.0:
which "^2.0.1"
spdx-correct@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
- integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
+ integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==
dependencies:
spdx-expression-parse "^3.0.0"
spdx-license-ids "^3.0.0"
@@ -2461,17 +3008,17 @@ spdx-exceptions@^2.1.0:
integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==
spdx-expression-parse@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0"
- integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
+ integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==
dependencies:
spdx-exceptions "^2.1.0"
spdx-license-ids "^3.0.0"
spdx-license-ids@^3.0.0:
- version "3.0.5"
- resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
- integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65"
+ integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==
sprintf-js@~1.0.2:
version "1.0.3"
@@ -2493,10 +3040,10 @@ sshpk@^1.7.0:
safer-buffer "^2.0.2"
tweetnacl "~0.14.0"
-stack-utils@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.2.tgz#5cf48b4557becb4638d0bc4f21d23f5d19586593"
- integrity sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==
+stack-utils@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277"
+ integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==
dependencies:
escape-string-regexp "^2.0.0"
@@ -2510,20 +3057,20 @@ string-width@^3.0.0:
strip-ansi "^5.1.0"
string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5"
- integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5"
+ integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
-strip-ansi@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
- integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
+string_decoder@^1.1.1:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+ integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
dependencies:
- ansi-regex "^3.0.0"
+ safe-buffer "~5.2.0"
strip-ansi@^5.1.0:
version "5.2.0"
@@ -2549,21 +3096,26 @@ strip-bom@^4.0.0:
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878"
integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==
+strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+ integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
-supertap@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/supertap/-/supertap-1.0.0.tgz#bd9751c7fafd68c68cf8222a29892206a119fa9e"
- integrity sha512-HZJ3geIMPgVwKk2VsmO5YHqnnJYl6bV5A9JW2uzqV43WmpgliNEYbuvukfor7URpaqpxuw3CfZ3ONdVbZjCgIA==
+supertap@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/supertap/-/supertap-2.0.0.tgz#8b587d6e14b8e885fa5183a9c45abf429feb9f7f"
+ integrity sha512-jRzcXlCeDYvKoZGA5oRhYyR3jUIYu0enkSxtmAgHRlD7HwrovTpH4bDSi0py9FtuA8si9cW/fKommJHuaoDHJA==
dependencies:
- arrify "^1.0.1"
- indent-string "^3.2.0"
- js-yaml "^3.10.0"
- serialize-error "^2.1.0"
- strip-ansi "^4.0.0"
+ arrify "^2.0.1"
+ indent-string "^4.0.0"
+ js-yaml "^3.14.0"
+ serialize-error "^7.0.1"
+ strip-ansi "^6.0.0"
supports-color@^5.3.0:
version "5.5.0"
@@ -2573,22 +3125,27 @@ supports-color@^5.3.0:
has-flag "^3.0.0"
supports-color@^7.1.0:
- version "7.1.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1"
- integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
+table@^6.0.4:
+ version "6.0.7"
+ resolved "https://registry.yarnpkg.com/table/-/table-6.0.7.tgz#e45897ffbcc1bcf9e8a87bf420f2c9e5a7a52a34"
+ integrity sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==
+ dependencies:
+ ajv "^7.0.2"
+ lodash "^4.17.20"
+ slice-ansi "^4.0.0"
+ string-width "^4.2.0"
+
temp-dir@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e"
integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==
-term-size@^2.1.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.0.tgz#1f16adedfe9bdc18800e1776821734086fcc6753"
- integrity sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==
-
test-exclude@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
@@ -2598,6 +3155,11 @@ test-exclude@^6.0.0:
glob "^7.1.4"
minimatch "^3.0.4"
+text-table@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+ integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
+
time-zone@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/time-zone/-/time-zone-1.0.0.tgz#99c5bf55958966af6d06d83bdf3800dc82faec5d"
@@ -2638,45 +3200,27 @@ trim-off-newlines@^1.0.1:
resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3"
integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM=
-ts-node@^8.3.0:
- version "8.10.1"
- resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.1.tgz#77da0366ff8afbe733596361d2df9a60fc9c9bd3"
- integrity sha512-bdNz1L4ekHiJul6SHtZWs1ujEKERJnHs4HxN7rjTyyVOFf3HaJ6sLqe6aPG62XTzAB/63pKRh5jTSWL0D7bsvw==
+ts-node@^9.1.1:
+ version "9.1.1"
+ resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d"
+ integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==
dependencies:
arg "^4.1.0"
+ create-require "^1.1.0"
diff "^4.0.1"
make-error "^1.1.1"
source-map-support "^0.5.17"
yn "3.1.1"
-tslib@^1.10.0, tslib@^1.8.1:
- version "1.11.2"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.2.tgz#9c79d83272c9a7aaf166f73915c9667ecdde3cc9"
- integrity sha512-tTSkux6IGPnUGUd1XAZHcpu85MOkIl5zX49pO+jfsie3eP0B6pyhOlLXm3cAC6T7s+euSDDUUV+Acop5WmtkVg==
-
-tslint@^6.1.2:
- version "6.1.2"
- resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.2.tgz#2433c248512cc5a7b2ab88ad44a6b1b34c6911cf"
- integrity sha512-UyNrLdK3E0fQG/xWNqAFAC5ugtFyPO4JJR1KyyfQAyzR8W0fTRrC91A8Wej4BntFzcvETdCSDa/4PnNYJQLYiA==
- dependencies:
- "@babel/code-frame" "^7.0.0"
- builtin-modules "^1.1.1"
- chalk "^2.3.0"
- commander "^2.12.1"
- diff "^4.0.1"
- glob "^7.1.1"
- js-yaml "^3.13.1"
- minimatch "^3.0.4"
- mkdirp "^0.5.3"
- resolve "^1.3.2"
- semver "^5.3.0"
- tslib "^1.10.0"
- tsutils "^2.29.0"
+tslib@^1.8.1:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
+ integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
-tsutils@^2.29.0:
- version "2.29.0"
- resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99"
- integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==
+tsutils@^3.17.1:
+ version "3.20.0"
+ resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.20.0.tgz#ea03ea45462e146b53d70ce0893de453ff24f698"
+ integrity sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg==
dependencies:
tslib "^1.8.1"
@@ -2692,6 +3236,23 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
+type-check@^0.4.0, type-check@~0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+ integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+ dependencies:
+ prelude-ls "^1.2.1"
+
+type-fest@^0.13.1:
+ version "0.13.1"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934"
+ integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==
+
+type-fest@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+ integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
type-fest@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1"
@@ -2714,10 +3275,10 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
-typescript@^3.8.3:
- version "3.8.3"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061"
- integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==
+typescript@4.1.x:
+ version "4.1.5"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72"
+ integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==
unique-string@^2.0.0:
version "2.0.0"
@@ -2726,29 +3287,30 @@ unique-string@^2.0.0:
dependencies:
crypto-random-string "^2.0.0"
-update-notifier@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.0.tgz#4866b98c3bc5b5473c020b1250583628f9a328f3"
- integrity sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==
+update-notifier@^5.0.1:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9"
+ integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==
dependencies:
- boxen "^4.2.0"
- chalk "^3.0.0"
+ boxen "^5.0.0"
+ chalk "^4.1.0"
configstore "^5.0.1"
has-yarn "^2.1.0"
import-lazy "^2.1.0"
is-ci "^2.0.0"
- is-installed-globally "^0.3.1"
- is-npm "^4.0.0"
+ is-installed-globally "^0.4.0"
+ is-npm "^5.0.0"
is-yarn-global "^0.3.0"
- latest-version "^5.0.0"
- pupa "^2.0.1"
+ latest-version "^5.1.0"
+ pupa "^2.1.1"
+ semver "^7.3.4"
semver-diff "^3.1.1"
xdg-basedir "^4.0.0"
uri-js@^4.2.2:
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
- integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
dependencies:
punycode "^2.1.0"
@@ -2759,11 +3321,26 @@ url-parse-lax@^3.0.0:
dependencies:
prepend-http "^2.0.0"
+util-deprecate@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+ integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
uuid@^3.3.2, uuid@^3.3.3:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
+uuid@^8.3.2:
+ version "8.3.2"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
+ integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
+
+v8-compile-cache@^2.0.3:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132"
+ integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==
+
validate-npm-package-license@^3.0.1:
version "3.0.4"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
@@ -2812,6 +3389,11 @@ widest-line@^3.1.0:
dependencies:
string-width "^4.0.0"
+word-wrap@^1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+ integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
wrap-ansi@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
@@ -2821,6 +3403,15 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
@@ -2849,6 +3440,16 @@ xml-crypto@^2.0.0:
xmldom "0.1.27"
xpath "0.0.27"
+xml-encryption@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/xml-encryption/-/xml-encryption-1.2.2.tgz#352f2d133ae697977894f54bd75e65150657a869"
+ integrity sha512-s0Fax5BpCZqLzYGtlmilUoi/kyhj8dHqaMOvTAQLifkDS4QVfIuBqhEj9POtk3YbZ0tSxp/hvbGj3iCOM1ej8w==
+ dependencies:
+ escape-html "^1.0.3"
+ node-forge "^0.10.0"
+ xmldom "~0.1.15"
+ xpath "0.0.27"
+
xml@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"
@@ -2870,11 +3471,21 @@ xpath@0.0.27, xpath@^0.0.27:
integrity sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==
y18n@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4"
+ integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==
+
+y18n@^5.0.5:
+ version "5.0.5"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18"
+ integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==
+
+yallist@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
- integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+ integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
-yargs-parser@^18.1.1:
+yargs-parser@^18.1.2:
version "18.1.3"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
@@ -2882,10 +3493,15 @@ yargs-parser@^18.1.1:
camelcase "^5.0.0"
decamelize "^1.2.0"
-yargs@^15.0.2, yargs@^15.3.1:
- version "15.3.1"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b"
- integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==
+yargs-parser@^20.2.2:
+ version "20.2.6"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.6.tgz#69f920addf61aafc0b8b89002f5d66e28f2d8b20"
+ integrity sha512-AP1+fQIWSM/sMiET8fyayjx/J+JmTPt2Mr0FkrgqB4todtfa53sOsrSAcIrJRD5XS20bKUwaDIuMkWKCEiQLKA==
+
+yargs@^15.0.2:
+ version "15.4.1"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
+ integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
dependencies:
cliui "^6.0.0"
decamelize "^1.2.0"
@@ -2897,7 +3513,20 @@ yargs@^15.0.2, yargs@^15.3.1:
string-width "^4.2.0"
which-module "^2.0.0"
y18n "^4.0.0"
- yargs-parser "^18.1.1"
+ yargs-parser "^18.1.2"
+
+yargs@^16.2.0:
+ version "16.2.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
+ integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
+ dependencies:
+ cliui "^7.0.2"
+ escalade "^3.1.1"
+ get-caller-file "^2.0.5"
+ require-directory "^2.1.1"
+ string-width "^4.2.0"
+ y18n "^5.0.5"
+ yargs-parser "^20.2.2"
yn@3.1.1:
version "3.1.1"