diff options
author | 2020-10-02 15:01:38 -0400 | |
---|---|---|
committer | 2020-10-02 15:01:38 -0400 | |
commit | 30182d5bb3887c68648a069679abdbdc3a59f191 (patch) | |
tree | aba120486e186bb9dba8a5457300468d426f7a65 | |
parent | Update CommentStreams to 1.35 (diff) | |
download | extensions-30182d5bb3887c68648a069679abdbdc3a59f191.tar.gz extensions-30182d5bb3887c68648a069679abdbdc3a59f191.tar.bz2 extensions-30182d5bb3887c68648a069679abdbdc3a59f191.zip |
Update OpenIDConnect to 1.35
Signed-off-by: Brian Evans <grknight@gentoo.org>
24 files changed, 1308 insertions, 151 deletions
diff --git a/OpenIDConnect/.gitignore b/OpenIDConnect/.gitignore index 6574e829..47e36d52 100644 --- a/OpenIDConnect/.gitignore +++ b/OpenIDConnect/.gitignore @@ -1,4 +1,3 @@ /node_modules/ /vendor/ /composer.lock -/package-lock.json diff --git a/OpenIDConnect/.phpcs.xml b/OpenIDConnect/.phpcs.xml index 95b1ff23..35799936 100644 --- a/OpenIDConnect/.phpcs.xml +++ b/OpenIDConnect/.phpcs.xml @@ -1,8 +1,10 @@ <?xml version="1.0"?> <ruleset> - <rule ref="./vendor/mediawiki/mediawiki-codesniffer/MediaWiki" /> + <rule ref="./vendor/mediawiki/mediawiki-codesniffer/MediaWiki"> + <exclude name="MediaWiki.Commenting.FunctionComment.MissingDocumentationPrivate" /> + <exclude name="PSR12.Properties.ConstantVisibility.NotFound" /> + </rule> <file>.</file> <arg name="extensions" value="php,php5,inc" /> <arg name="encoding" value="utf8" /> - <exclude-pattern>vendor</exclude-pattern> </ruleset> diff --git a/OpenIDConnect/CODE_OF_CONDUCT.md b/OpenIDConnect/CODE_OF_CONDUCT.md index d8e5d087..498acf76 100644 --- a/OpenIDConnect/CODE_OF_CONDUCT.md +++ b/OpenIDConnect/CODE_OF_CONDUCT.md @@ -1 +1 @@ -The development of this software is covered by a [Code of Conduct](https://www.mediawiki.org/wiki/Code_of_Conduct). +The development of this software is covered by a [Code of Conduct](https://www.mediawiki.org/wiki/Special:MyLanguage/Code_of_Conduct). diff --git a/OpenIDConnect/composer.json b/OpenIDConnect/composer.json index 692e24e4..f971f014 100644 --- a/OpenIDConnect/composer.json +++ b/OpenIDConnect/composer.json @@ -1,5 +1,6 @@ { - "name": "mediawiki/OpenIDConnect", + "name": "mediawiki/openidconnect", + "type": "mediawiki-extension", "description": "Provides authentication using OpenID Connect in conjunction with PluggableAuth", "license": "MIT", "authors": [ @@ -8,20 +9,15 @@ "email": "cicalese@mitre.org" } ], - "repositories": [ - { - "url": "https://github.com/jumbojett/OpenID-Connect-PHP.git", - "type": "git" - } - ], "require": { - "jumbojett/openid-connect-php": "0.5.0" + "jumbojett/openid-connect-php": "0.5.0", + "composer/installers": "~1.0" }, "require-dev": { - "jakub-onderka/php-parallel-lint": "1.0.0", - "mediawiki/mediawiki-codesniffer": "18.0.0", - "jakub-onderka/php-console-highlighter": "0.3.2", - "mediawiki/minus-x": "0.3.1" + "mediawiki/mediawiki-codesniffer": "31.0.0", + "mediawiki/minus-x": "1.1.0", + "php-parallel-lint/php-console-highlighter": "0.5.0", + "php-parallel-lint/php-parallel-lint": "1.2.0" }, "scripts": { "test": [ @@ -30,8 +26,11 @@ "minus-x check ." ], "fix": [ - "phpcbf", - "minus-x fix ." + "minus-x fix .", + "phpcbf" ] + }, + "extra": { + "installer-name": "OpenIDConnect" } } diff --git a/OpenIDConnect/extension.json b/OpenIDConnect/extension.json index de2bac84..1aa32359 100644 --- a/OpenIDConnect/extension.json +++ b/OpenIDConnect/extension.json @@ -1,6 +1,6 @@ { "name": "OpenID Connect", - "version": "4.1", + "version": "5.3", "author": [ "[https://www.mediawiki.org/wiki/User:Cindy.cicalese Cindy Cicalese]" ], @@ -25,7 +25,9 @@ }, "AutoloadClasses": { "OpenIDConnect": "src/OpenIDConnect.php", - "SelectOpenIDConnectIssuer": "src/SelectOpenIDConnectIssuer.php" + "SelectOpenIDConnectIssuer": "src/SelectOpenIDConnectIssuer.php", + "MigrateOIDCSubjectAndIssuerFromUserTable": + "maintenance/MigrateOIDCSubjectAndIssuerFromUserTable.php" }, "Hooks": { "LoadExtensionSchemaUpdates": "OpenIDConnect::loadExtensionSchemaUpdates" diff --git a/OpenIDConnect/gitinfo.json b/OpenIDConnect/gitinfo.json deleted file mode 100644 index ad2b408c..00000000 --- a/OpenIDConnect/gitinfo.json +++ /dev/null @@ -1 +0,0 @@ -{"branch": "baea47f52e4b86ee09a01a6e26d1699bd31b6ea8\n", "headSHA1": "baea47f52e4b86ee09a01a6e26d1699bd31b6ea8\n", "remoteURL": "https://gerrit-replica.wikimedia.org/r/mediawiki/extensions/OpenIDConnect", "headCommitDate": "1523681260", "head": "baea47f52e4b86ee09a01a6e26d1699bd31b6ea8\n"}
\ No newline at end of file diff --git a/OpenIDConnect/i18n/SelectOpenIDConnectIssuer.alias.php b/OpenIDConnect/i18n/SelectOpenIDConnectIssuer.alias.php index 353ed4d5..e30385bb 100644 --- a/OpenIDConnect/i18n/SelectOpenIDConnectIssuer.alias.php +++ b/OpenIDConnect/i18n/SelectOpenIDConnectIssuer.alias.php @@ -21,7 +21,6 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -// @codingStandardsIgnoreFile $specialPageAliases = []; diff --git a/OpenIDConnect/i18n/ar.json b/OpenIDConnect/i18n/ar.json new file mode 100644 index 00000000..f1bad33d --- /dev/null +++ b/OpenIDConnect/i18n/ar.json @@ -0,0 +1,9 @@ +{ + "@metadata": { + "authors": [ + "ديفيد" + ] + }, + "openidconnect-desc": "يوفر المصادقة باستخدام OpenID Connect بالاقتران مع PluggableAuth", + "selectopenidconnectissuer": "حدد مصدر المصادقة" +} diff --git a/OpenIDConnect/i18n/el.json b/OpenIDConnect/i18n/el.json new file mode 100644 index 00000000..25d98138 --- /dev/null +++ b/OpenIDConnect/i18n/el.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Nikosgranturismogt" + ] + }, + "selectopenidconnectissuer": "Επιλογή εκδότη επαλήθευσης" +} diff --git a/OpenIDConnect/i18n/sh.json b/OpenIDConnect/i18n/sh.json new file mode 100644 index 00000000..38e5f1f2 --- /dev/null +++ b/OpenIDConnect/i18n/sh.json @@ -0,0 +1,9 @@ +{ + "@metadata": { + "authors": [ + "Vlad5250" + ] + }, + "openidconnect-desc": "Omogućuje potvdu identiteta s OpenID Connect koja radi zajedno s PluggableAuth", + "selectopenidconnectissuer": "Izaberite izdatelja potvrde identiteta" +} diff --git a/OpenIDConnect/i18n/tr.json b/OpenIDConnect/i18n/tr.json new file mode 100644 index 00000000..0d20717f --- /dev/null +++ b/OpenIDConnect/i18n/tr.json @@ -0,0 +1,9 @@ +{ + "@metadata": { + "authors": [ + "BaRaN6161 TURK" + ] + }, + "openidconnect-desc": "PluIDableAuth ile birlikte OpenID Connect kullanarak kimlik doğrulama sağlar", + "selectopenidconnectissuer": "Kimlik Doğrulayıcıyı Seçin" +} diff --git a/OpenIDConnect/i18n/zh-hk.json b/OpenIDConnect/i18n/zh-hk.json new file mode 100644 index 00000000..c226a2ea --- /dev/null +++ b/OpenIDConnect/i18n/zh-hk.json @@ -0,0 +1,9 @@ +{ + "@metadata": { + "authors": [ + "C933103" + ] + }, + "openidconnect-desc": "與PluggableAuth共同提供OpenID Connect認證", + "selectopenidconnectissuer": "選擇認證發行者" +} diff --git a/OpenIDConnect/maintenance/MigrateOIDCSubjectAndIssuerFromUserTable.php b/OpenIDConnect/maintenance/MigrateOIDCSubjectAndIssuerFromUserTable.php new file mode 100644 index 00000000..d7b77b93 --- /dev/null +++ b/OpenIDConnect/maintenance/MigrateOIDCSubjectAndIssuerFromUserTable.php @@ -0,0 +1,96 @@ +<?php +/** + * Migrate subject and issuer columns from the user table to the openid_connect + * table. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance + */ + +if ( getenv( 'MW_INSTALL_PATH' ) ) { + require_once getenv( 'MW_INSTALL_PATH' ) . '/maintenance/Maintenance.php'; +} else { + require_once __DIR__ . '/../../../maintenance/Maintenance.php'; +} + +/** + * Maintenance script that migrates data in the subject and issuer columns + * from the user table to the openid_connect table. + * + * @ingroup Maintenance + */ +class MigrateOIDCSubjectAndIssuerFromUserTable extends LoggedUpdateMaintenance { + public function __construct() { + parent::__construct(); + $this->addDescription( 'Migrates data from user table (for upgrade to 5.X+' ); + } + + /** + * @inheritDoc + */ + public function getUpdateKey() { + return __CLASS__; + } + + /** + * @inheritDoc + */ + public function doDBUpdates() { + $dbw = $this->getDB( DB_MASTER ); + + if ( !$dbw->fieldExists( + 'user', + 'subject', + __METHOD__ + ) ) { + $this->output( 'No subject column found in user table.' . PHP_EOL ); + return true; + } + + if ( !$dbw->fieldExists( + 'user', + 'issuer', + __METHOD__ + ) ) { + $this->output( 'No issuer column found in user table.' . PHP_EOL ); + return true; + } + + $dbw->insertSelect( + 'openid_connect', + 'user', + [ + 'oidc_user' => 'user_id', + 'oidc_subject' => 'subject', + 'oidc_issuer' => 'issuer' + ], + [ + 'subject IS NOT NULL', + 'issuer IS NOT NULL' + ], + __METHOD__ + ); + $this->output( 'Migrated data from user table to openid_connect table.' . + PHP_EOL ); + + return true; + } +} + +$maintClass = MigrateOIDCSubjectAndIssuerFromUserTable::class; +require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/OpenIDConnect/package-lock.json b/OpenIDConnect/package-lock.json new file mode 100644 index 00000000..16a67e43 --- /dev/null +++ b/OpenIDConnect/package-lock.json @@ -0,0 +1,891 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "JSV": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz", + "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c=", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + } + } + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "coffeescript": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz", + "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4=", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", + "dev": true + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "findup-sync": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", + "dev": true, + "requires": { + "glob": "~5.0.0" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "getobject": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", + "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=", + "dev": true + }, + "glob": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "grunt": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.1.0.tgz", + "integrity": "sha512-+NGod0grmviZ7Nzdi9am7vuRS/h76PcWDsV635mEXF0PEQMUV6Kb+OjTdsVxbi0PZmfQOjCMKb3w8CVZcqsn1g==", + "dev": true, + "requires": { + "coffeescript": "~1.10.0", + "dateformat": "~1.0.12", + "eventemitter2": "~0.4.13", + "exit": "~0.1.1", + "findup-sync": "~0.3.0", + "glob": "~7.0.0", + "grunt-cli": "~1.2.0", + "grunt-known-options": "~1.1.0", + "grunt-legacy-log": "~2.0.0", + "grunt-legacy-util": "~1.1.1", + "iconv-lite": "~0.4.13", + "js-yaml": "~3.13.1", + "minimatch": "~3.0.2", + "mkdirp": "~1.0.3", + "nopt": "~3.0.6", + "path-is-absolute": "~1.0.0", + "rimraf": "~2.6.2" + }, + "dependencies": { + "grunt-cli": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", + "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", + "dev": true, + "requires": { + "findup-sync": "~0.3.0", + "grunt-known-options": "~1.1.0", + "nopt": "~3.0.6", + "resolve": "~1.1.0" + } + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "grunt-banana-checker": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/grunt-banana-checker/-/grunt-banana-checker-0.9.0.tgz", + "integrity": "sha512-SqPiB6OazWqR8USL0NymtuT5Br3mD9WBBsM1rHC/3wIi2SrZNM6/+j9CIeuEM5oCn+AtO2Y0+rzzFyOdC9afAg==", + "dev": true + }, + "grunt-jsonlint": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/grunt-jsonlint/-/grunt-jsonlint-1.0.7.tgz", + "integrity": "sha1-BDPzpYVSy1twkgW/1uJ11ECoCWU=", + "dev": true, + "requires": { + "jsonlint": "1.6.2", + "strip-json-comments": "^2.0.0" + } + }, + "grunt-known-options": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz", + "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==", + "dev": true + }, + "grunt-legacy-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-2.0.0.tgz", + "integrity": "sha512-1m3+5QvDYfR1ltr8hjiaiNjddxGdQWcH0rw1iKKiQnF0+xtgTazirSTGu68RchPyh1OBng1bBUjLmX8q9NpoCw==", + "dev": true, + "requires": { + "colors": "~1.1.2", + "grunt-legacy-log-utils": "~2.0.0", + "hooker": "~0.2.3", + "lodash": "~4.17.5" + } + }, + "grunt-legacy-log-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.0.1.tgz", + "integrity": "sha512-o7uHyO/J+i2tXG8r2bZNlVk20vlIFJ9IEYyHMCQGfWYru8Jv3wTqKZzvV30YW9rWEjq0eP3cflQ1qWojIe9VFA==", + "dev": true, + "requires": { + "chalk": "~2.4.1", + "lodash": "~4.17.10" + } + }, + "grunt-legacy-util": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.1.1.tgz", + "integrity": "sha512-9zyA29w/fBe6BIfjGENndwoe1Uy31BIXxTH3s8mga0Z5Bz2Sp4UCjkeyv2tI449ymkx3x26B+46FV4fXEddl5A==", + "dev": true, + "requires": { + "async": "~1.5.2", + "exit": "~0.1.1", + "getobject": "~0.1.0", + "hooker": "~0.2.3", + "lodash": "~4.17.10", + "underscore.string": "~3.3.4", + "which": "~1.3.0" + } + }, + "has-color": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", + "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsonlint": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.2.tgz", + "integrity": "sha1-VzcEUIX1XrRVxosf9OvAG9UOiDA=", + "dev": true, + "requires": { + "JSV": ">= 4.0.x", + "nomnom": ">= 1.5.x" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "dev": true + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz", + "integrity": "sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==", + "dev": true + }, + "nomnom": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz", + "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=", + "dev": true, + "requires": { + "chalk": "~0.4.0", + "underscore": "~1.6.0" + }, + "dependencies": { + "ansi-styles": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", + "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=", + "dev": true + }, + "chalk": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", + "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", + "dev": true, + "requires": { + "ansi-styles": "~1.0.0", + "has-color": "~0.1.0", + "strip-ansi": "~0.1.0" + } + } + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + }, + "strip-ansi": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", + "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", + "dev": true + }, + "underscore.string": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz", + "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==", + "dev": true, + "requires": { + "sprintf-js": "^1.0.3", + "util-deprecate": "^1.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/OpenIDConnect/package.json b/OpenIDConnect/package.json index 3b4b5f0c..1bfa2182 100644 --- a/OpenIDConnect/package.json +++ b/OpenIDConnect/package.json @@ -1,11 +1,11 @@ { - "private": true, - "scripts": { - "test": "grunt test" - }, - "devDependencies": { - "grunt": "1.0.1", - "grunt-banana-checker": "0.4.0", - "grunt-jsonlint": "1.0.7" - } + "private": true, + "scripts": { + "test": "grunt test" + }, + "devDependencies": { + "grunt": "1.1.0", + "grunt-banana-checker": "0.9.0", + "grunt-jsonlint": "1.0.7" + } } diff --git a/OpenIDConnect/sql/AddIssuer.sql b/OpenIDConnect/sql/AddIssuer.sql deleted file mode 100644 index 4b18a376..00000000 --- a/OpenIDConnect/sql/AddIssuer.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE /*_*/user ADD issuer TINYBLOB; diff --git a/OpenIDConnect/sql/AddSubject.sql b/OpenIDConnect/sql/AddSubject.sql deleted file mode 100644 index 76ff53d8..00000000 --- a/OpenIDConnect/sql/AddSubject.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE /*_*/user ADD subject TINYBLOB; diff --git a/OpenIDConnect/sql/mysql/AddTable.sql b/OpenIDConnect/sql/mysql/AddTable.sql new file mode 100644 index 00000000..873cbef4 --- /dev/null +++ b/OpenIDConnect/sql/mysql/AddTable.sql @@ -0,0 +1,6 @@ +CREATE TABLE /*_*/openid_connect ( + oidc_user int unsigned PRIMARY KEY NOT NULL, + oidc_subject TINYBLOB NOT NULL, + oidc_issuer TINYBLOB NOT NULL +) /*$wgDBTableOptions*/; +CREATE INDEX /*i*/openid_connect_subject ON /*_*/openid_connect (oidc_subject(50),oidc_issuer(50)); diff --git a/OpenIDConnect/sql/mysql/DropColumnsFromUserTable.sql b/OpenIDConnect/sql/mysql/DropColumnsFromUserTable.sql new file mode 100644 index 00000000..43f18fce --- /dev/null +++ b/OpenIDConnect/sql/mysql/DropColumnsFromUserTable.sql @@ -0,0 +1 @@ +ALTER TABLE /*_*/user DROP COLUMN subject, DROP COLUMN issuer; diff --git a/OpenIDConnect/sql/postgres/AddTable.sql b/OpenIDConnect/sql/postgres/AddTable.sql new file mode 100644 index 00000000..209794fe --- /dev/null +++ b/OpenIDConnect/sql/postgres/AddTable.sql @@ -0,0 +1,6 @@ +CREATE TABLE openid_connect ( + oidc_user INTEGER NOT NULL PRIMARY KEY, + oidc_subject VARCHAR(255) NOT NULL, + oidc_issuer VARCHAR(255) NOT NULL +); +CREATE INDEX openid_connect_subject ON openid_connect (oidc_subject,oidc_issuer); diff --git a/OpenIDConnect/sql/postgres/DropColumnsFromUserTable.sql b/OpenIDConnect/sql/postgres/DropColumnsFromUserTable.sql new file mode 100644 index 00000000..8db8403e --- /dev/null +++ b/OpenIDConnect/sql/postgres/DropColumnsFromUserTable.sql @@ -0,0 +1 @@ +ALTER TABLE mwuser DROP COLUMN subject, DROP COLUMN issuer; diff --git a/OpenIDConnect/src/OpenIDConnect.php b/OpenIDConnect/src/OpenIDConnect.php index 869c1555..481f5c26 100644 --- a/OpenIDConnect/src/OpenIDConnect.php +++ b/OpenIDConnect/src/OpenIDConnect.php @@ -22,9 +22,10 @@ * DEALINGS IN THE SOFTWARE. */ -use \MediaWiki\Session\SessionManager; -use \MediaWiki\Auth\AuthManager; -use \Jumbojett\OpenIDConnectClient; +use Jumbojett\OpenIDConnectClient; +use MediaWiki\Auth\AuthManager; +use MediaWiki\MediaWikiServices; +use MediaWiki\Session\SessionManager; class OpenIDConnect extends PluggableAuth { @@ -47,12 +48,14 @@ class OpenIDConnect extends PluggableAuth { public function authenticate( &$id, &$username, &$realname, &$email, &$errorMessage ) { if ( !array_key_exists( 'SERVER_PORT', $_SERVER ) ) { - wfDebug( "in authenticate, server port not set" . PHP_EOL ); + wfDebugLog( 'OpenID Connect', 'in authenticate, server port not set' . + PHP_EOL ); return false; } if ( !isset( $GLOBALS['wgOpenIDConnect_Config'] ) ) { - wfDebug( "wgOpenIDConnect_Config not set" . PHP_EOL ); + wfDebugLog( 'OpenID Connect', 'wgOpenIDConnect_Config not set' . + PHP_EOL ); return false; } @@ -62,7 +65,7 @@ class OpenIDConnect extends PluggableAuth { $iss = $session->get( 'iss' ); - if ( !is_null( $iss ) ) { + if ( $iss !== null ) { if ( isset( $_REQUEST['code'] ) && isset( $_REQUEST['status'] ) ) { $session->remove( 'iss' ); @@ -74,19 +77,21 @@ class OpenIDConnect extends PluggableAuth { if ( !isset( $config['clientID'] ) || !isset( $config['clientsecret'] ) ) { - wfDebug( "OpenID Connect: clientID or clientsecret not set for " . $iss ); + wfDebugLog( 'OpenID Connect', + 'OpenID Connect: clientID or clientsecret not set for ' . $iss . + '.' . PHP_EOL ); $params = [ - "uri" => urlencode( $_SERVER['REQUEST_URI'] ), - "query" => urlencode( $_SERVER['QUERY_STRING'] ) + 'uri' => urlencode( $_SERVER['REQUEST_URI'] ), + 'query' => urlencode( $_SERVER['QUERY_STRING'] ) ]; - self::redirect( "Special:SelectOpenIDConnectIssuer", + self::redirect( 'Special:SelectOpenIDConnectIssuer', $params, true ); return false; } } else { - wfDebug( 'Issuer ' . $iss . - ' does not exist in wgOpeIDConnect_Config'. PHP_EOL ); + wfDebugLog( 'OpenID Connect', 'Issuer ' . $iss . + ' does not exist in wgOpeIDConnect_Config.' . PHP_EOL ); return false; } @@ -108,18 +113,19 @@ class OpenIDConnect extends PluggableAuth { if ( !isset( $config['clientID'] ) || !isset( $config['clientsecret'] ) ) { - wfDebug( "OpenID Connect: clientID or clientsecret not set for " . - $iss ); + wfDebugLog( 'OpenID Connect', + 'OpenID Connect: clientID or clientsecret not set for ' . + $iss . '.' . PHP_EOL ); return false; } } else { $params = [ - "uri" => urlencode( $_SERVER['REQUEST_URI'] ), - "query" => urlencode( $_SERVER['QUERY_STRING'] ) + 'uri' => urlencode( $_SERVER['REQUEST_URI'] ), + 'query' => urlencode( $_SERVER['QUERY_STRING'] ) ]; - self::redirect( "Special:SelectOpenIDConnectIssuer", + self::redirect( 'Special:SelectOpenIDConnectIssuer', $params, true ); return false; } @@ -149,57 +155,89 @@ class OpenIDConnect extends PluggableAuth { if ( isset( $config['proxy'] ) ) { $oidc->setHttpProxy( $config['proxy'] ); } + if ( isset( $config['verifyHost'] ) ) { + $oidc->setVerifyHost( $config['verifyHost'] ); + } + if ( isset( $config['verifyPeer'] ) ) { + $oidc->setVerifyPeer( $config['verifyPeer'] ); + } + $redirectURL = + SpecialPage::getTitleFor( 'PluggableAuthLogin' )->getFullURL(); + $oidc->setRedirectURL( $redirectURL ); + wfDebugLog( 'OpenID Connect', 'Redirect URL: ' . $redirectURL ); if ( $oidc->authenticate() ) { - $preferred_username = - $oidc->requestUserInfo( - isset( $config['preferred_username'] ) ? - $config['preferred_username'] : - "preferred_username" - ); - $realname = $oidc->requestUserInfo( "name" ); - $email = $oidc->requestUserInfo( "email" ); + $realname = $oidc->requestUserInfo( 'name' ); + $email = $oidc->requestUserInfo( 'email' ); $this->subject = $oidc->requestUserInfo( 'sub' ); $this->issuer = $oidc->getProviderURL(); - - $username = $this->getName( $this->subject, $this->issuer ); - if ( !is_null( $username ) ) { + wfDebugLog( 'OpenID Connect', 'Real name: ' . $realname . + ', Email: ' . $email . ', Subject: ' . $this->subject . + ', Issuer: ' . $this->issuer ); + + list( $id, $username ) = + $this->findUser( $this->subject, $this->issuer ); + if ( $id !== null ) { + wfDebugLog( 'OpenID Connect', + 'Found user with matching subject and issuer.' . PHP_EOL ); return true; } + wfDebugLog( 'OpenID Connect', + 'No user found with matching subject and issuer.' . PHP_EOL ); + if ( $GLOBALS['wgOpenIDConnect_MigrateUsersByEmail'] === true ) { + wfDebugLog( 'OpenID Connect', 'Checking for email migration.' . + PHP_EOL ); list( $id, $username ) = $this->getMigratedIdByEmail( $email ); - if ( !is_null( $id ) ) { + if ( $id !== null ) { $this->saveExtraAttributes( $id ); - wfDebug( "Migrated user " . $username . " by email: " . $email ); + wfDebugLog( 'OpenID Connect', 'Migrated user ' . $username . + ' by email: ' . $email . '.' . PHP_EOL ); return true; } - } elseif ( $GLOBALS['wgOpenIDConnect_MigrateUsersByUserName'] === true ) { + } + + $preferred_username = $this->getPreferredUsername( $config, $oidc, + $realname, $email ); + wfDebugLog( 'OpenID Connect', 'Preferred username: ' . + $preferred_username . PHP_EOL ); + + if ( $GLOBALS['wgOpenIDConnect_MigrateUsersByUserName'] === true ) { + wfDebugLog( 'OpenID Connect', 'Checking for username migration.' . + PHP_EOL ); $id = $this->getMigratedIdByUserName( $preferred_username ); - if ( !is_null( $id ) ) { + if ( $id !== null ) { $this->saveExtraAttributes( $id ); - wfDebug( "Migrated user by username: " . $preferred_username ); + wfDebugLog( 'OpenID Connect', 'Migrated user by username: ' . + $preferred_username . '.' . PHP_EOL ); $username = $preferred_username; return true; } } $username = self::getAvailableUsername( $preferred_username, - $realname, $email, $this->subject ); + $realname, $email ); + + wfDebugLog( 'OpenID Connect', 'Available username: ' . + $username . PHP_EOL ); - $authManager = Authmanager::singleton(); + if ( method_exists( MediaWikiServices::class, 'getAuthManager' ) ) { + // MediaWiki 1.35+ + $authManager = MediaWikiServices::getInstance()->getAuthManager(); + } else { + $authManager = AuthManager::singleton(); + } $authManager->setAuthenticationSessionData( self::OIDC_SUBJECT_SESSION_KEY, $this->subject ); $authManager->setAuthenticationSessionData( self::OIDC_ISSUER_SESSION_KEY, $this->issuer ); return true; - - } else { - $session->clear(); - return false; } + } catch ( Exception $e ) { - wfDebug( $e->__toString() . PHP_EOL ); + wfDebugLog( 'OpenID Connect', $e->__toString() . PHP_EOL ); + $errorMessage = $e->__toString(); $session->clear(); return false; } @@ -224,138 +262,192 @@ class OpenIDConnect extends PluggableAuth { * @param int $id user id */ public function saveExtraAttributes( $id ) { - $authManager = Authmanager::singleton(); - if ( is_null( $this->subject ) ) { + if ( method_exists( MediaWikiServices::class, 'getAuthManager' ) ) { + // MediaWiki 1.35+ + $authManager = MediaWikiServices::getInstance()->getAuthManager(); + } else { + $authManager = AuthManager::singleton(); + } + if ( $this->subject === null ) { $this->subject = $authManager->getAuthenticationSessionData( self::OIDC_SUBJECT_SESSION_KEY ); $authManager->removeAuthenticationSessionData( self::OIDC_SUBJECT_SESSION_KEY ); } - if ( is_null( $this->issuer ) ) { + if ( $this->issuer === null ) { $this->issuer = $authManager->getAuthenticationSessionData( self::OIDC_ISSUER_SESSION_KEY ); $authManager->removeAuthenticationSessionData( self::OIDC_ISSUER_SESSION_KEY ); } $dbw = wfGetDB( DB_MASTER ); - $dbw->update( 'user', + $dbw->upsert( + 'openid_connect', + [ + 'oidc_user' => $id, + 'oidc_subject' => $this->subject, + 'oidc_issuer' => $this->issuer + ], + [ + [ 'oidc_user' ] + ], [ - 'subject' => $this->subject, - 'issuer' => $this->issuer - ], [ - 'user_id' => $id - ], __METHOD__ + 'oidc_subject' => $this->subject, + 'oidc_issuer' => $this->issuer + ], + __METHOD__ ); } - private static function getName( $subject, $issuer ) { + private static function findUser( $subject, $issuer ) { $dbr = wfGetDB( DB_REPLICA ); - $row = $dbr->selectRow( 'user', - [ 'user_name' ], + $row = $dbr->selectRow( [ - 'subject' => $subject, - 'issuer' => $issuer - ], __METHOD__ + 'user', + 'openid_connect' + ], + [ + 'user_id', + 'user_name' + ], + [ + 'oidc_subject' => $subject, + 'oidc_issuer' => $issuer + ], + __METHOD__, + [], + [ + 'openid_connect' => [ 'JOIN', [ 'user_id=oidc_user' ] ] + ] ); if ( $row === false ) { - return null; + return [ null, null ]; + } else { + return [ $row->user_id, $row->user_name ]; + } + } + + private static function getPreferredUsername( $config, $oidc, $realname, + $email ) { + if ( isset( $config['preferred_username'] ) ) { + wfDebugLog( 'OpenID Connect', 'Using ' . $config['preferred_username'] . + ' attribute for preferred username.' . PHP_EOL ); + $preferred_username = + $oidc->requestUserInfo( $config['preferred_username'] ); + } else { + $preferred_username = $oidc->requestUserInfo( 'preferred_username' ); + } + if ( strlen( $preferred_username ) > 0 ) { + // do nothing + } elseif ( strlen( $realname ) > 0 && + $GLOBALS['wgOpenIDConnect_UseRealNameAsUserName'] === true ) { + $preferred_username = $realname; + } elseif ( strlen( $email ) > 0 && + $GLOBALS['wgOpenIDConnect_UseEmailNameAsUserName'] === true ) { + $pos = strpos( $email, '@' ); + if ( $pos !== false && $pos > 0 ) { + $preferred_username = substr( $email, 0, $pos ); + } else { + $preferred_username = $email; + } } else { - return $row->user_name; + return null; } + $nt = Title::makeTitleSafe( NS_USER, $preferred_username ); + if ( $nt === null ) { + return null; + } + return $nt->getText(); } private static function getMigratedIdByUserName( $username ) { $nt = Title::makeTitleSafe( NS_USER, $username ); - if ( is_null( $nt ) ) { + if ( $nt === null ) { + wfDebugLog( 'OpenID Connect', + 'Invalid preferred username for migration: ' . $username . '.' . + PHP_EOL ); return null; } $username = $nt->getText(); $dbr = wfGetDB( DB_REPLICA ); - $row = $dbr->selectRow( 'user', - [ 'user_id' ], + $row = $dbr->selectRow( + [ + 'user', + 'openid_connect' + ], + [ + 'user_id' + ], [ 'user_name' => $username, - 'subject' => null, - 'issuer' => null + 'oidc_user' => null ], - __METHOD__ + __METHOD__, + [], + [ + 'openid_connect' => [ 'LEFT JOIN', [ 'user_id=oidc_user' ] ] + ] ); - if ( $row === false ) { - return null; - } else { + if ( $row !== false ) { return $row->user_id; } + return null; } private static function getMigratedIdByEmail( $email ) { - wfDebug( "Matching user to email " . $email ); + wfDebugLog( 'OpenID Connect', 'Matching user to email ' . $email . '.' . + PHP_EOL ); $dbr = wfGetDB( DB_REPLICA ); - $row = $dbr->selectRow( 'user', + $row = $dbr->selectRow( + [ + 'user', + 'openid_connect' + ], [ 'user_id', - 'user_name' + 'user_name', + 'oidc_user' ], [ - 'user_email' => $email, - 'subject' => null, - 'issuer' => null + 'user_email' => $email ], __METHOD__, [ // if multiple matching accounts, use the oldest one - 'ORDER BY' => 'user_registration', - 'LIMIT' => 1 + 'ORDER BY' => 'user_registration' + ], + [ + 'openid_connect' => [ 'LEFT JOIN', [ 'user_id=oidc_user' ] ] ] ); - if ( $row === false ) { - return [ null, null ]; - } else { + if ( $row !== false && $row->oidc_user === null ) { return [ $row->user_id, $row->user_name ]; } + return [ null, null ]; } - private static function getAvailableUsername( $preferred_username, - $realname, $email, $subject ) { - if ( strlen( $preferred_username ) > 0 ) { - $name = $preferred_username; - } elseif ( strlen( $realname ) > 0 && - $GLOBALS['wgOpenIDConnect_UseRealNameAsUserName'] === true ) { - $name = $realname; - } elseif ( strlen( $email ) > 0 && - $GLOBALS['wgOpenIDConnect_UseEmailNameAsUserName'] === true ) { - $pos = strpos( $email, '@' ); - if ( $pos !== false && $pos > 0 ) { - $name = substr( $email, 0, $pos ); - } else { - $name = $email; - } + private static function getAvailableUsername( $preferred_username ) { + if ( $preferred_username === null ) { + $preferred_username = 'User'; } - $nt = Title::makeTitleSafe( NS_USER, $name ); - if ( is_null( $nt ) ) { - $name = "User"; - } elseif ( is_null( User::idFromName( $name ) ) ) { - return $nt->getText(); - } else { - $name = $nt->getText(); + + if ( User::idFromName( $preferred_username ) === null ) { + return $preferred_username; } + $count = 1; - while ( !is_null( User::idFromName( $name . $count ) ) ) { + while ( User::idFromName( $preferred_username . $count ) !== null ) { $count++; } - return $name . $count; + return $preferred_username . $count; } - private static function redirect( $page, $params = null, $doExit = false ) { + private static function redirect( $page, $params = [], $doExit = false ) { $title = Title::newFromText( $page ); - if ( is_null( $title ) ) { + if ( $title === null ) { $title = Title::newMainPage(); } - $url = $title->getFullURL(); - if ( is_array( $params ) && count( $params ) > 0 ) { - foreach ( $params as $key => $value ) { - $url = wfAppendQuery( $url, $key . '=' . $value ); - } - } + $url = $title->getFullURL( $params ); header( 'Location: ' . $url ); if ( $doExit ) { exit; @@ -369,7 +461,34 @@ class OpenIDConnect extends PluggableAuth { */ public static function loadExtensionSchemaUpdates( $updater ) { $dir = $GLOBALS['wgExtensionDirectory'] . '/OpenIDConnect/sql/'; - $updater->addExtensionField( 'user', 'subject', $dir . '/AddSubject.sql' ); - $updater->addExtensionField( 'user', 'issuer', $dir . '/AddIssuer.sql' ); + $type = $updater->getDB()->getType(); + $updater->addExtensionTable( 'openid_connect', + $dir . $type . '/AddTable.sql' ); + $updater->addExtensionUpdate( [ [ __CLASS__, 'migrateSubjectAndIssuer' ], + $updater ] ); + } + + /** + * Migrate subject and issuer columns from user table to openid_connect + * table. + * + * @param DatabaseUpdater $updater + */ + public static function migrateSubjectAndIssuer( $updater ) { + if ( $updater->getDB()->fieldExists( 'user', 'subject', __METHOD__ ) && + $updater->getDB()->fieldExists( 'user', 'issuer', __METHOD__ ) ) { + $maintenance = new FakeMaintenance(); + $task = $maintenance->runChild( + 'MigrateOIDCSubjectAndIssuerFromUserTable' ); + if ( $task->execute() ) { + $dir = $GLOBALS['wgExtensionDirectory'] . '/OpenIDConnect/sql/'; + $type = $updater->getDB()->getType(); + $patch = $dir . $type . '/DropColumnsFromUserTable.sql'; + $updater->modifyField( 'user', 'subject', $patch, true ); + } + } else { + $updater->output( + '...user table does not have subject and issuer columns.' . PHP_EOL ); + } } } diff --git a/OpenIDConnect/src/SelectOpenIDConnectIssuer.php b/OpenIDConnect/src/SelectOpenIDConnectIssuer.php index 62199215..b31f02fd 100644 --- a/OpenIDConnect/src/SelectOpenIDConnectIssuer.php +++ b/OpenIDConnect/src/SelectOpenIDConnectIssuer.php @@ -40,9 +40,8 @@ class SelectOpenIDConnectIssuer extends UnlistedSpecialPage { if ( strlen( $_REQUEST['query'] ) > 0 ) { $url .= "?" . urldecode( $_REQUEST['query'] ); } - if ( session_id() == '' ) { - wfSetupSession(); - } + $this->getRequest()->getSession()->persist(); + $_SESSION['iss'] = $_REQUEST['iss']; $GLOBALS['wgOut']->redirect( $url ); } else { diff --git a/OpenIDConnect/version b/OpenIDConnect/version deleted file mode 100644 index a25699de..00000000 --- a/OpenIDConnect/version +++ /dev/null @@ -1,4 +0,0 @@ -OpenIDConnect: REL1_31 -2020-05-25T18:25:51 - -baea47f |