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}`; +// } +// } + 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}`; - }; - 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}`); - 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}`; + }, + 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}` + ); + 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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 @@ -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.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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 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"