diff --git a/src/Bootstrap/dist/css/bootstrap-theme.css b/src/Bootstrap/dist/css/bootstrap-theme.css index ac664b7afc..8af7c600ef 100644 --- a/src/Bootstrap/dist/css/bootstrap-theme.css +++ b/src/Bootstrap/dist/css/bootstrap-theme.css @@ -16,6 +16,35 @@ body h2, body h3 { margin-top: 40px; } +#autocomplete-results-container { + position: absolute; + right: 0; + left: 0; + z-index: 1; + display: none; + margin-right: 15px; + margin-left: 15px; + background-color: white; + border: 1px solid black; +} +#autocomplete-results-container .autocomplete-row-data { + padding-top: 2px; + padding-bottom: 2px; + color: #000; +} +#autocomplete-results-container .autocomplete-row-data :focus { + background-color: #66afe9; +} +#autocomplete-results-container .autocomplete-row-id { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +#autocomplete-results-container .autocomplete-row-owners { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} .footer { display: table-row; height: 1px; @@ -293,8 +322,14 @@ img.reserved-indicator-icon { align-items: center; } .modal-container .modal-box { + width: -webkit-calc(100% - 40px); + width: calc(100% - 40px); + min-width: 320px; + max-width: 448px; + min-height: 364px; padding: 0; - margin-left: 37.5%; + margin-right: auto; + margin-left: auto; overflow: hidden; background-color: white; border-radius: 8px; @@ -304,9 +339,6 @@ img.reserved-indicator-icon { color: white; background-color: #4181b8; } -.modal-container .modal-box .modal-title .title-text { - vertical-align: top; -} .modal-container .modal-box .modal-title .dismiss-button { float: right; padding: 0; @@ -315,30 +347,27 @@ img.reserved-indicator-icon { border: 0; } .modal-container .modal-box .modal-body { - padding: 15px; + padding: 0; } .modal-container .modal-box .modal-body .tag-node { padding: 13px; - margin: -15px; text-align: left; background-color: #fff4ce; } -.modal-container .modal-box .modal-body .error-message { - margin-right: -15px; - margin-left: -15px; +.modal-container .modal-box .modal-body .tag-node .title-text { + vertical-align: top; } -.modal-container .modal-box .modal-body .signin-node { - padding: 15% 15%; - margin-top: 16px; +.modal-container .modal-box .modal-body .action-node { + padding: 9% 6%; text-align: center; } -.modal-container .modal-box .modal-body .signin-node .provider-button { +.modal-container .modal-box .modal-body .action-button { font-size: 16px; line-height: 2em; } -.modal-container .modal-box .modal-body .transform-node { - margin-bottom: 10px; - text-align: center; +.modal-container .modal-box .modal-body .transform-text { + padding: 0 15px; + margin: 0; } #edit-metadata-form-container .loading:after { display: inline-block; @@ -953,6 +982,42 @@ img.reserved-indicator-icon { top: 3px; margin-right: 10px; } +.page-manage-organizations h1 { + margin-bottom: 0; +} +.page-manage-organizations h2 { + margin-top: 0; + margin-bottom: 10px; +} +.page-manage-organizations .form-section-title { + margin-top: 40px; +} +@media (min-width: 768px) { + .page-manage-organizations .form-section-title { + float: left; + text-align: right; + } +} +@media (min-width: 768px) { + .page-manage-organizations .form-section-data { + float: right; + margin-top: 40px; + line-height: 3; + text-align: right; + } +} +.page-manage-organizations .panel { + margin-top: 5px; +} +.page-manage-organizations .form-group + .form-group .btn { + margin-top: 20px; +} +.manage-members-listing tbody:first-child { + border-style: hidden; +} +.manage-members-listing { + margin-bottom: 0; +} .page-manage-owners h2 .ms-Icon { position: relative; top: -2px; diff --git a/src/Bootstrap/less/theme/all.less b/src/Bootstrap/less/theme/all.less index ca85294fcf..d65be97d39 100644 --- a/src/Bootstrap/less/theme/all.less +++ b/src/Bootstrap/less/theme/all.less @@ -17,6 +17,7 @@ @import "page-home.less"; @import "page-list-packages.less"; @import "page-manage-curated-feed.less"; +@import "page-manage-organizations.less"; @import "page-manage-owners.less"; @import "page-manage-packages.less"; @import "page-delete-package.less"; diff --git a/src/Bootstrap/less/theme/base.less b/src/Bootstrap/less/theme/base.less index 168e8aa87f..98e4129284 100644 --- a/src/Bootstrap/less/theme/base.less +++ b/src/Bootstrap/less/theme/base.less @@ -19,6 +19,40 @@ body { } } +#autocomplete-results-container { + position: absolute; + z-index: 1; + background-color: white; + border: 1px solid black; + display: none; + left: 0; + right: 0; + margin-left: floor((@grid-gutter-width / 2)); + margin-right: floor((@grid-gutter-width / 2)); + + .autocomplete-row-data { + padding-top:2px; + padding-bottom:2px; + color:@text-color; + + :focus { + background-color: @input-border-focus; + } + } + + .autocomplete-row-id { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .autocomplete-row-owners { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } +} + .footer { background-color: @panel-footer-bg; color: @panel-footer-color; diff --git a/src/Bootstrap/less/theme/modals.less b/src/Bootstrap/less/theme/modals.less index f65a7fff11..3ac6e51421 100644 --- a/src/Bootstrap/less/theme/modals.less +++ b/src/Bootstrap/less/theme/modals.less @@ -11,10 +11,15 @@ overflow: hidden; .modal-box { - border-radius: 8px; - background-color: white; + border-radius: 8px; + background-color: white; + margin-left: auto; + margin-right: auto; + min-height: 364px; + min-width: 320px; + max-width: 448px; + width: calc(100% - 40px); overflow: hidden; - margin-left: 37.5%; padding: 0px; .modal-title { @@ -22,10 +27,6 @@ color: white; padding: 15px; - .title-text { - vertical-align: top; - } - .dismiss-button { background: transparent; border: 0px; @@ -36,34 +37,31 @@ } .modal-body { - padding: 15px; + padding: 0px; .tag-node { background-color: #fff4ce; padding: 13px; - margin: -15px; text-align: left; - } - .error-message { - margin-left: -15px; - margin-right: -15px; + .title-text { + vertical-align: top; + } } - .signin-node { - padding: 15% 15%; + .action-node { + padding: 9% 6%; text-align: center; - margin-top: 16px; + } - .provider-button { - font-size: 16px; - line-height: 2em; - } + .action-button { + font-size: 16px; + line-height: 2em; } - .transform-node { - text-align: center; - margin-bottom: 10px; + .transform-text { + padding: 0px 15px; + margin: 0px; } } } diff --git a/src/Bootstrap/less/theme/page-manage-organizations.less b/src/Bootstrap/less/theme/page-manage-organizations.less new file mode 100644 index 0000000000..e5bb19ca39 --- /dev/null +++ b/src/Bootstrap/less/theme/page-manage-organizations.less @@ -0,0 +1,48 @@ +.page-manage-organizations { + @section-margin-top: 40px; + + h1 { + margin-bottom: 0; + } + + h2 { + margin-bottom: 10px; + margin-top: 0; + } + + .form-section-title { + margin-top: @section-margin-top; + + @media (min-width: @screen-sm-min) { + float: left; + text-align: right; + } + } + + .form-section-data { + @media (min-width: @screen-sm-min) { + margin-top: @section-margin-top; + line-height: 3; + float: right; + text-align: right; + } + } + + .panel { + margin-top: 5px; + } + + .form-group + .form-group { + .btn { + margin-top: 20px; + } + } +} + +.manage-members-listing tbody:first-child { + border-style: hidden +} + +.manage-members-listing { + margin-bottom: 0; +} \ No newline at end of file diff --git a/src/Bootstrap/package-lock.json b/src/Bootstrap/package-lock.json index d44a57a27b..afe43d746d 100644 --- a/src/Bootstrap/package-lock.json +++ b/src/Bootstrap/package-lock.json @@ -2,7 +2,14 @@ "name": "bootstrap", "version": "3.3.7", "lockfileVersion": 1, + "requires": true, "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.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", @@ -13,7 +20,11 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", - "dev": true + "dev": true, + "requires": { + "mime-types": "2.1.15", + "negotiator": "0.6.1" + } }, "acorn": { "version": "3.3.0", @@ -26,6 +37,9 @@ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", "dev": true, + "requires": { + "acorn": "4.0.13" + }, "dependencies": { "acorn": { "version": "4.0.13", @@ -40,6 +54,10 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz", "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=", "dev": true, + "requires": { + "extend": "3.0.1", + "semver": "5.0.3" + }, "dependencies": { "semver": { "version": "5.0.3", @@ -53,13 +71,22 @@ "version": "4.11.8", "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true + "dev": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } }, "align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true + "dev": true, + "requires": { + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" + } }, "amdefine": { "version": "1.0.1", @@ -84,12 +111,26 @@ "resolved": "https://registry.npmjs.org/archiver/-/archiver-1.3.0.tgz", "integrity": "sha1-TyGU1tj5nfP1MeaIHxTxXVX6ryI=", "dev": true, + "requires": { + "archiver-utils": "1.3.0", + "async": "2.5.0", + "buffer-crc32": "0.2.13", + "glob": "7.0.6", + "lodash": "4.17.4", + "readable-stream": "2.3.3", + "tar-stream": "1.5.4", + "walkdir": "0.0.11", + "zip-stream": "1.2.0" + }, "dependencies": { "async": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", - "dev": true + "dev": true, + "requires": { + "lodash": "4.17.4" + } }, "lodash": { "version": "4.17.4", @@ -104,6 +145,14 @@ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-1.3.0.tgz", "integrity": "sha1-5QtMCccL89aA4y/xt5lOn52JUXQ=", "dev": true, + "requires": { + "glob": "7.0.6", + "graceful-fs": "4.1.11", + "lazystream": "1.0.0", + "lodash": "4.17.4", + "normalize-path": "2.1.1", + "readable-stream": "2.3.3" + }, "dependencies": { "lodash": { "version": "4.17.4", @@ -117,7 +166,10 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", - "dev": true + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } }, "array-differ": { "version": "1.0.0", @@ -135,7 +187,10 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } }, "array-uniq": { "version": "1.0.3", @@ -183,7 +238,13 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/autoprefixer-core/-/autoprefixer-core-5.2.1.tgz", "integrity": "sha1-5kDEFK5Bmq4hwa1DyOoPPbgqVm0=", - "dev": true + "dev": true, + "requires": { + "browserslist": "0.4.0", + "caniuse-db": "1.0.30000696", + "num2fraction": "1.2.2", + "postcss": "4.1.16" + } }, "aws-sign2": { "version": "0.6.0", @@ -201,7 +262,11 @@ "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=", - "dev": true + "dev": true, + "requires": { + "core-js": "2.4.1", + "regenerator-runtime": "0.10.5" + } }, "babylon": { "version": "6.17.4", @@ -232,31 +297,56 @@ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", "dev": true, - "optional": true + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } }, "bl": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz", "integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=", - "dev": true + "dev": true, + "requires": { + "readable-stream": "2.3.3" + } }, "body-parser": { "version": "1.14.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.14.2.tgz", "integrity": "sha1-EBXLH+LEQ4WCWVgdtTMy+NDPUPk=", "dev": true, + "requires": { + "bytes": "2.2.0", + "content-type": "1.0.2", + "debug": "2.2.0", + "depd": "1.1.0", + "http-errors": "1.3.1", + "iconv-lite": "0.4.13", + "on-finished": "2.3.0", + "qs": "5.2.0", + "raw-body": "2.1.7", + "type-is": "1.6.15" + }, "dependencies": { "debug": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true + "dev": true, + "requires": { + "ms": "0.7.1" + } }, "http-errors": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=", - "dev": true + "dev": true, + "requires": { + "inherits": "2.0.3", + "statuses": "1.3.1" + } }, "iconv-lite": { "version": "0.4.13", @@ -282,25 +372,38 @@ "version": "2.10.1", "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true + "dev": true, + "requires": { + "hoek": "2.16.3" + } }, "brace-expansion": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } }, "browserify-zlib": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", - "dev": true + "dev": true, + "requires": { + "pako": "0.2.9" + } }, "browserslist": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-0.4.0.tgz", "integrity": "sha1-O9SrkZncG5FQ1NbbpNnTqrvIbdQ=", - "dev": true + "dev": true, + "requires": { + "caniuse-db": "1.0.30000696" + } }, "btoa": { "version": "1.1.2", @@ -330,7 +433,11 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "dev": true + "dev": true, + "requires": { + "no-case": "2.3.1", + "upper-case": "1.1.3" + } }, "camelcase": { "version": "2.1.1", @@ -342,7 +449,11 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true + "dev": true, + "requires": { + "camelcase": "2.1.1", + "map-obj": "1.0.1" + } }, "caniuse-db": { "version": "1.0.30000696", @@ -360,43 +471,93 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true + "dev": true, + "requires": { + "align-text": "0.1.4", + "lazy-cache": "1.0.4" + } }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } }, "change-case": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/change-case/-/change-case-3.0.1.tgz", "integrity": "sha1-7l9a0EFa0a2egHLPSc1M+nZgpVQ=", - "dev": true + "dev": true, + "requires": { + "camel-case": "3.0.0", + "constant-case": "2.0.0", + "dot-case": "2.1.1", + "header-case": "1.0.1", + "is-lower-case": "1.1.3", + "is-upper-case": "1.1.2", + "lower-case": "1.1.4", + "lower-case-first": "1.0.2", + "no-case": "2.3.1", + "param-case": "2.1.1", + "pascal-case": "2.0.1", + "path-case": "2.1.1", + "sentence-case": "2.1.1", + "snake-case": "2.1.0", + "swap-case": "1.1.2", + "title-case": "2.1.1", + "upper-case": "1.1.3", + "upper-case-first": "1.1.2" + } }, "character-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", - "dev": true + "dev": true, + "requires": { + "is-regex": "1.0.4" + } }, "clean-css": { "version": "3.4.27", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.27.tgz", "integrity": "sha1-re91sxwWD/pdcvTeZ5ZuJmDBolU=", - "dev": true + "dev": true, + "requires": { + "commander": "2.8.1", + "source-map": "0.4.4" + } }, "cli": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", "dev": true, + "requires": { + "exit": "0.1.2", + "glob": "7.1.2" + }, "dependencies": { "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } } } }, @@ -405,6 +566,9 @@ "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", "dev": true, + "requires": { + "colors": "1.0.3" + }, "dependencies": { "colors": { "version": "1.0.3", @@ -418,7 +582,12 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true + "dev": true, + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" + } }, "co": { "version": "4.6.0", @@ -442,25 +611,40 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "dev": true + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } }, "commander": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", - "dev": true + "dev": true, + "requires": { + "graceful-readlink": "1.0.1" + } }, "comment-parser": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-0.3.1.tgz", "integrity": "sha1-/WV6rIwUktMIyaYQD8m0nSQ1q6E=", - "dev": true + "dev": true, + "requires": { + "readable-stream": "2.3.3" + } }, "compress-commons": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.2.0.tgz", "integrity": "sha1-WFhwku8g03y1i68AARLJJ4/3O58=", - "dev": true + "dev": true, + "requires": { + "buffer-crc32": "0.2.13", + "crc32-stream": "2.0.0", + "normalize-path": "2.1.1", + "readable-stream": "2.3.3" + } }, "concat-map": { "version": "0.0.1", @@ -472,13 +656,24 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", - "dev": true + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" + } }, "connect": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.2.tgz", "integrity": "sha1-aU6NIGgb/kkCgsiriGvpjwn0L+c=", - "dev": true + "dev": true, + "requires": { + "debug": "2.6.7", + "finalhandler": "1.0.3", + "parseurl": "1.3.1", + "utils-merge": "1.0.0" + } }, "connect-livereload": { "version": "0.5.4", @@ -490,19 +685,30 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true + "dev": true, + "requires": { + "date-now": "0.1.4" + } }, "constant-case": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-2.0.0.tgz", "integrity": "sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY=", - "dev": true + "dev": true, + "requires": { + "snake-case": "2.1.0", + "upper-case": "1.1.3" + } }, "constantinople": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.0.tgz", "integrity": "sha1-dWnKqKo/jVk11i4fqW+fcCzYHHk=", - "dev": true + "dev": true, + "requires": { + "acorn": "3.3.0", + "is-expression": "2.1.0" + } }, "content-type": { "version": "1.0.2", @@ -532,19 +738,32 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-2.0.0.tgz", "integrity": "sha1-483TtN8xaN10494/u8t7KX/pCPQ=", - "dev": true + "dev": true, + "requires": { + "crc": "3.4.4", + "readable-stream": "2.3.3" + } }, "cryptiles": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true + "dev": true, + "requires": { + "boom": "2.10.1" + } }, "csscomb": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/csscomb/-/csscomb-3.1.8.tgz", "integrity": "sha1-qKc4iE9Am6817JRhr8UuHHW9I6I=", "dev": true, + "requires": { + "commander": "2.0.0", + "csscomb-core": "3.0.0-3.1", + "gonzales-pe": "3.0.0-28", + "vow": "0.4.4" + }, "dependencies": { "commander": { "version": "2.0.0", @@ -559,12 +778,22 @@ "resolved": "https://registry.npmjs.org/csscomb-core/-/csscomb-core-3.0.0-3.1.tgz", "integrity": "sha1-tBHI18/g3z8v4d+E0b1kpvAEbGg=", "dev": true, + "requires": { + "gonzales-pe": "3.0.0-28", + "minimatch": "0.2.12", + "vow": "0.4.4", + "vow-fs": "0.3.2" + }, "dependencies": { "minimatch": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.12.tgz", "integrity": "sha1-6oKgEqxmLH3fqhRPHBR+aUb12vs=", - "dev": true + "dev": true, + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } } } }, @@ -572,19 +801,30 @@ "version": "0.10.0", "resolved": "https://registry.npmjs.org/csslint/-/csslint-0.10.0.tgz", "integrity": "sha1-OmoE51Zcjp0ZvrSXZ8fslug2WAU=", - "dev": true + "dev": true, + "requires": { + "parserlib": "0.2.5" + } }, "cst": { "version": "0.4.10", "resolved": "https://registry.npmjs.org/cst/-/cst-0.4.10.tgz", "integrity": "sha512-U5ETe1IOjq2h56ZcBE3oe9rT7XryCH6IKgPMv0L7sSk6w29yR3p5egCK0T3BDNHHV95OoUBgXsqiVG+3a900Ag==", - "dev": true + "dev": true, + "requires": { + "babel-runtime": "6.23.0", + "babylon": "6.17.4", + "source-map-support": "0.4.15" + } }, "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 + "dev": true, + "requires": { + "array-find-index": "1.0.2" + } }, "cycle": { "version": "1.0.3", @@ -597,6 +837,9 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -616,19 +859,29 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/date-time/-/date-time-1.1.0.tgz", "integrity": "sha1-GIdtC9pMGf5w3Tv0sDTygbEqQLY=", - "dev": true + "dev": true, + "requires": { + "time-zone": "0.1.0" + } }, "dateformat": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", - "dev": true + "dev": true, + "requires": { + "get-stdin": "4.0.1", + "meow": "3.7.0" + } }, "debug": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz", "integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=", - "dev": true + "dev": true, + "requires": { + "ms": "2.0.0" + } }, "decamelize": { "version": "1.2.0", @@ -677,6 +930,10 @@ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", "dev": true, + "requires": { + "domelementtype": "1.1.3", + "entities": "1.1.1" + }, "dependencies": { "domelementtype": { "version": "1.1.3", @@ -702,26 +959,39 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", - "dev": true + "dev": true, + "requires": { + "domelementtype": "1.3.0" + } }, "domutils": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true + "dev": true, + "requires": { + "dom-serializer": "0.1.0", + "domelementtype": "1.3.0" + } }, "dot-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.1.tgz", "integrity": "sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4=", - "dev": true + "dev": true, + "requires": { + "no-case": "2.3.1" + } }, "ecc-jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", "dev": true, - "optional": true + "optional": true, + "requires": { + "jsbn": "0.1.1" + } }, "ee-first": { "version": "1.1.1", @@ -739,7 +1009,10 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz", "integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=", - "dev": true + "dev": true, + "requires": { + "once": "1.4.0" + } }, "entities": { "version": "1.0.0", @@ -752,13 +1025,19 @@ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", "dev": true, - "optional": true + "optional": true, + "requires": { + "prr": "0.0.0" + } }, "error-ex": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "dev": true + "dev": true, + "requires": { + "is-arrayish": "0.2.1" + } }, "es6-promise": { "version": "2.3.0", @@ -819,12 +1098,23 @@ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.5.0.tgz", "integrity": "sha1-ksz22B73Cp+kwXRxFMzvbYaIpsQ=", "dev": true, + "requires": { + "concat-stream": "1.5.0", + "debug": "0.7.4", + "mkdirp": "0.5.0", + "yauzl": "2.4.1" + }, "dependencies": { "concat-stream": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.0.tgz", "integrity": "sha1-U/fUPFHF5D+ByP3QMyHGMb5o1hE=", - "dev": true + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.0.6", + "typedarray": "0.0.6" + } }, "debug": { "version": "0.7.4", @@ -842,13 +1132,24 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", - "dev": true + "dev": true, + "requires": { + "minimist": "0.0.8" + } }, "readable-stream": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } }, "string_decoder": { "version": "0.10.31", @@ -874,19 +1175,29 @@ "version": "0.10.0", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true + "dev": true, + "requires": { + "websocket-driver": "0.6.5" + } }, "fd-slicer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", - "dev": true + "dev": true, + "requires": { + "pend": "1.2.0" + } }, "fg-lodash": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/fg-lodash/-/fg-lodash-0.0.2.tgz", "integrity": "sha1-mINSU39CfaavIiEpu2OsyknmL6M=", "dev": true, + "requires": { + "lodash": "2.4.2", + "underscore.string": "2.3.3" + }, "dependencies": { "lodash": { "version": "2.4.2", @@ -906,7 +1217,11 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1" + } }, "file-sync-cmp": { "version": "0.1.1", @@ -918,25 +1233,48 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.3.tgz", "integrity": "sha1-70fneVDpmXgOhgIqVg4yF+DQzIk=", - "dev": true + "dev": true, + "requires": { + "debug": "2.6.7", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.1", + "statuses": "1.3.1", + "unpipe": "1.0.0" + } }, "find-up": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } }, "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.15" + }, "dependencies": { "glob": { "version": "5.0.15", "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } } } }, @@ -950,7 +1288,12 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" + } }, "fresh": { "version": "0.5.0", @@ -962,7 +1305,14 @@ "version": "0.26.7", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", - "dev": true + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "2.4.0", + "klaw": "1.3.1", + "path-is-absolute": "1.0.1", + "rimraf": "2.2.8" + } }, "fs.realpath": { "version": "1.0.0", @@ -980,7 +1330,10 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", - "dev": true + "dev": true, + "requires": { + "globule": "1.2.0" + } }, "generate-function": { "version": "2.0.0", @@ -992,7 +1345,10 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true + "dev": true, + "requires": { + "is-property": "1.0.2" + } }, "get-stdin": { "version": "4.0.1", @@ -1011,6 +1367,9 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -1024,19 +1383,40 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", - "dev": true + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } }, "globule": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", "dev": true, + "requires": { + "glob": "7.1.2", + "lodash": "4.17.4", + "minimatch": "3.0.4" + }, "dependencies": { "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } }, "lodash": { "version": "4.17.4", @@ -1069,12 +1449,36 @@ "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.1.tgz", "integrity": "sha1-6HeHZOlEsY8yuw8QuQeEdcnftWs=", "dev": true, + "requires": { + "coffee-script": "1.10.0", + "dateformat": "1.0.12", + "eventemitter2": "0.4.14", + "exit": "0.1.2", + "findup-sync": "0.3.0", + "glob": "7.0.6", + "grunt-cli": "1.2.0", + "grunt-known-options": "1.1.0", + "grunt-legacy-log": "1.0.0", + "grunt-legacy-util": "1.0.0", + "iconv-lite": "0.4.18", + "js-yaml": "3.5.5", + "minimatch": "3.0.4", + "nopt": "3.0.6", + "path-is-absolute": "1.0.1", + "rimraf": "2.2.8" + }, "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 + "dev": true, + "requires": { + "findup-sync": "0.3.0", + "grunt-known-options": "1.1.0", + "nopt": "3.0.6", + "resolve": "1.1.7" + } } } }, @@ -1083,6 +1487,12 @@ "resolved": "https://registry.npmjs.org/grunt-autoprefixer/-/grunt-autoprefixer-3.0.4.tgz", "integrity": "sha1-/kLiR7z6ucKSoSwGLa1PNb3pAsU=", "dev": true, + "requires": { + "autoprefixer-core": "5.2.1", + "chalk": "1.0.0", + "diff": "1.3.2", + "postcss": "4.1.16" + }, "dependencies": { "ansi-regex": { "version": "1.1.1", @@ -1094,19 +1504,33 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.0.0.tgz", "integrity": "sha1-s89O0P9Tl8mcdbj2edsvUoMfltw=", - "dev": true + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "1.0.3", + "strip-ansi": "2.0.1", + "supports-color": "1.3.1" + } }, "has-ansi": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-1.0.3.tgz", "integrity": "sha1-wLWxYV2eOCsP9nFp2We0JeSMpTg=", - "dev": true + "dev": true, + "requires": { + "ansi-regex": "1.1.1", + "get-stdin": "4.0.1" + } }, "strip-ansi": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz", "integrity": "sha1-32LBqpTtLxFOHQ8h/R1QSCt5pg4=", - "dev": true + "dev": true, + "requires": { + "ansi-regex": "1.1.1" + } }, "supports-color": { "version": "1.3.1", @@ -1121,12 +1545,19 @@ "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-1.0.0.tgz", "integrity": "sha1-ay7ZQRfix//jLuBFeMlv5GJam20=", "dev": true, + "requires": { + "async": "1.5.2", + "rimraf": "2.6.1" + }, "dependencies": { "rimraf": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", - "dev": true + "dev": true, + "requires": { + "glob": "7.0.6" + } } } }, @@ -1135,6 +1566,13 @@ "resolved": "https://registry.npmjs.org/grunt-contrib-compress/-/grunt-contrib-compress-1.3.0.tgz", "integrity": "sha1-XlwmogBJCCPH93KIr9LXNQ2Vxj0=", "dev": true, + "requires": { + "archiver": "1.3.0", + "chalk": "1.1.3", + "lodash": "4.17.4", + "pretty-bytes": "3.0.1", + "stream-buffers": "2.2.0" + }, "dependencies": { "lodash": { "version": "4.17.4", @@ -1149,6 +1587,10 @@ "resolved": "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-1.0.1.tgz", "integrity": "sha1-YVCYYwhOhx1+ht5IwBUlntl3Rb0=", "dev": true, + "requires": { + "chalk": "1.1.3", + "source-map": "0.5.6" + }, "dependencies": { "source-map": { "version": "0.5.6", @@ -1162,43 +1604,85 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/grunt-contrib-connect/-/grunt-contrib-connect-1.0.2.tgz", "integrity": "sha1-XPkzuRpnOGBEJzwLJERgPNmIebo=", - "dev": true + "dev": true, + "requires": { + "async": "1.5.2", + "connect": "3.6.2", + "connect-livereload": "0.5.4", + "http2": "3.3.6", + "morgan": "1.8.2", + "opn": "4.0.2", + "portscanner": "1.2.0", + "serve-index": "1.9.0", + "serve-static": "1.12.3" + } }, "grunt-contrib-copy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-1.0.0.tgz", "integrity": "sha1-cGDGWB6QS4qw0A8HbgqPbj58NXM=", - "dev": true + "dev": true, + "requires": { + "chalk": "1.1.3", + "file-sync-cmp": "0.1.1" + } }, "grunt-contrib-csslint": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/grunt-contrib-csslint/-/grunt-contrib-csslint-1.0.0.tgz", "integrity": "sha1-0Fk4y6KncYXEppdTwXUdYW6p+pg=", - "dev": true + "dev": true, + "requires": { + "chalk": "1.1.3", + "csslint": "0.10.0", + "lodash": "3.10.1", + "strip-json-comments": "1.0.4" + } }, "grunt-contrib-cssmin": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/grunt-contrib-cssmin/-/grunt-contrib-cssmin-1.0.2.tgz", "integrity": "sha1-FzTL09hMpzZHWLflj/GOUqpgu3Y=", - "dev": true + "dev": true, + "requires": { + "chalk": "1.1.3", + "clean-css": "3.4.27", + "maxmin": "1.1.0" + } }, "grunt-contrib-htmlmin": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/grunt-contrib-htmlmin/-/grunt-contrib-htmlmin-1.5.0.tgz", "integrity": "sha1-Stes838ClP6OqX83NFrnC6elglE=", - "dev": true + "dev": true, + "requires": { + "chalk": "1.1.3", + "html-minifier": "2.1.7", + "pretty-bytes": "3.0.1" + } }, "grunt-contrib-jshint": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.0.0.tgz", "integrity": "sha1-MPQFpR3mVr+m6wKbmkZLn+AqQCo=", - "dev": true + "dev": true, + "requires": { + "chalk": "1.1.3", + "hooker": "0.2.3", + "jshint": "2.9.5" + } }, "grunt-contrib-less": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/grunt-contrib-less/-/grunt-contrib-less-1.3.0.tgz", "integrity": "sha1-UY73yG3GDhWeZRCKp125OpyP9dQ=", "dev": true, + "requires": { + "async": "1.5.2", + "chalk": "1.1.3", + "less": "2.6.1", + "lodash": "4.17.4" + }, "dependencies": { "lodash": { "version": "4.17.4", @@ -1212,19 +1696,33 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/grunt-contrib-pug/-/grunt-contrib-pug-1.0.0.tgz", "integrity": "sha1-tSWlwK/wRiL3Vw4x+SQQbxb4G0Y=", - "dev": true + "dev": true, + "requires": { + "chalk": "1.1.3", + "pug": "2.0.0-rc.2" + } }, "grunt-contrib-qunit": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/grunt-contrib-qunit/-/grunt-contrib-qunit-0.7.0.tgz", "integrity": "sha1-YqaaNv8NEeHKhxIJC55Qt3W4+CU=", - "dev": true + "dev": true, + "requires": { + "grunt-lib-phantomjs": "0.6.0" + } }, "grunt-contrib-uglify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-1.0.2.tgz", "integrity": "sha1-rmekb5FT7dTLEYE6Vetpxw19svs=", "dev": true, + "requires": { + "chalk": "1.1.3", + "lodash": "4.17.4", + "maxmin": "1.1.0", + "uglify-js": "2.6.4", + "uri-path": "1.0.0" + }, "dependencies": { "lodash": { "version": "4.17.4", @@ -1238,13 +1736,22 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.0.0.tgz", "integrity": "sha1-hKGnodar0m7VaEE0lscxM+mQAY8=", - "dev": true + "dev": true, + "requires": { + "async": "1.5.2", + "gaze": "1.1.2", + "lodash": "3.10.1", + "tiny-lr": "0.2.1" + } }, "grunt-csscomb": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/grunt-csscomb/-/grunt-csscomb-3.1.1.tgz", "integrity": "sha1-RUfb9ESaN+MgoKpRCigodbKI6jw=", - "dev": true + "dev": true, + "requires": { + "csscomb": "3.1.8" + } }, "grunt-exec": { "version": "1.0.1", @@ -1256,19 +1763,32 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/grunt-html/-/grunt-html-8.0.2.tgz", "integrity": "sha1-iG2cFwqF+Q9SnzIGl9GEfYnjv6A=", - "dev": true + "dev": true, + "requires": { + "async": "1.5.2", + "chalk": "1.1.3" + } }, "grunt-jekyll": { "version": "0.4.6", "resolved": "https://registry.npmjs.org/grunt-jekyll/-/grunt-jekyll-0.4.6.tgz", "integrity": "sha1-U7ut6Grp0UEkUhjgs6tMM0DNIGc=", - "dev": true + "dev": true, + "requires": { + "tmp": "0.0.31" + } }, "grunt-jscs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/grunt-jscs/-/grunt-jscs-3.0.1.tgz", "integrity": "sha1-H65Q4+lV3546nZQlrsIqzK4AgJI=", "dev": true, + "requires": { + "hooker": "0.2.3", + "jscs": "3.0.7", + "lodash": "4.6.1", + "vow": "0.4.4" + }, "dependencies": { "lodash": { "version": "4.6.1", @@ -1288,13 +1808,24 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-1.0.0.tgz", "integrity": "sha1-+4bxgJhHvAfcR4Q/ns1srLYt8tU=", - "dev": true + "dev": true, + "requires": { + "colors": "1.1.2", + "grunt-legacy-log-utils": "1.0.0", + "hooker": "0.2.3", + "lodash": "3.10.1", + "underscore.string": "3.2.3" + } }, "grunt-legacy-log-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-1.0.0.tgz", "integrity": "sha1-p7ji0Ps1taUPSvmG/BEnSevJbz0=", "dev": true, + "requires": { + "chalk": "1.1.3", + "lodash": "4.3.0" + }, "dependencies": { "lodash": { "version": "4.3.0", @@ -1309,6 +1840,15 @@ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.0.0.tgz", "integrity": "sha1-OGqnjcbtUJhsKxiVcmWxtIq7m4Y=", "dev": true, + "requires": { + "async": "1.5.2", + "exit": "0.1.2", + "getobject": "0.1.0", + "hooker": "0.2.3", + "lodash": "4.3.0", + "underscore.string": "3.2.3", + "which": "1.2.14" + }, "dependencies": { "lodash": { "version": "4.3.0", @@ -1323,6 +1863,12 @@ "resolved": "https://registry.npmjs.org/grunt-lib-phantomjs/-/grunt-lib-phantomjs-0.6.0.tgz", "integrity": "sha1-rR9/IS/EojJfMvzUnGoNo2h8H7Q=", "dev": true, + "requires": { + "eventemitter2": "0.4.14", + "phantomjs": "1.9.20", + "semver": "1.0.14", + "temporary": "0.0.8" + }, "dependencies": { "semver": { "version": "1.0.14", @@ -1337,6 +1883,14 @@ "resolved": "https://registry.npmjs.org/grunt-saucelabs/-/grunt-saucelabs-9.0.0.tgz", "integrity": "sha1-1ogCWgVQHeysCp4SndTk9QpCAUo=", "dev": true, + "requires": { + "colors": "1.1.2", + "lodash": "4.13.1", + "q": "1.4.1", + "requestretry": "1.9.1", + "sauce-tunnel": "2.5.0", + "saucelabs": "1.2.0" + }, "dependencies": { "lodash": { "version": "4.13.1", @@ -1350,7 +1904,11 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-1.0.0.tgz", "integrity": "sha1-Zs+LEBBHInuVus5uodoMF37Vwi8=", - "dev": true + "dev": true, + "requires": { + "browserify-zlib": "0.1.4", + "concat-stream": "1.6.0" + } }, "har-schema": { "version": "1.0.5", @@ -1362,19 +1920,29 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "dev": true + "dev": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } }, "has": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", - "dev": true + "dev": true, + "requires": { + "function-bind": "1.1.0" + } }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } }, "has-color": { "version": "0.1.7", @@ -1386,13 +1954,23 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz", "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=", - "dev": true + "dev": true, + "requires": { + "is-stream": "1.1.0", + "pinkie-promise": "2.0.1" + } }, "hawk": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true + "dev": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } }, "he": { "version": "1.1.1", @@ -1404,7 +1982,11 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz", "integrity": "sha1-lTWXMZfBRLCWE81l0xfvGZY70C0=", - "dev": true + "dev": true, + "requires": { + "no-case": "2.3.1", + "upper-case": "1.1.3" + } }, "hoek": { "version": "2.16.3", @@ -1429,12 +2011,24 @@ "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-2.1.7.tgz", "integrity": "sha1-kFHW/LvPIU7TB+GtdPQyu5rWVcw=", "dev": true, + "requires": { + "change-case": "3.0.1", + "clean-css": "3.4.27", + "commander": "2.9.0", + "he": "1.1.1", + "ncname": "1.0.0", + "relateurl": "0.2.7", + "uglify-js": "2.6.4" + }, "dependencies": { "commander": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "dev": true + "dev": true, + "requires": { + "graceful-readlink": "1.0.1" + } } } }, @@ -1443,6 +2037,13 @@ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", "dev": true, + "requires": { + "domelementtype": "1.3.0", + "domhandler": "2.3.0", + "domutils": "1.5.1", + "entities": "1.0.0", + "readable-stream": "1.1.14" + }, "dependencies": { "isarray": { "version": "0.0.1", @@ -1454,7 +2055,13 @@ "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } }, "string_decoder": { "version": "0.10.31", @@ -1468,13 +2075,24 @@ "version": "1.6.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.1.tgz", "integrity": "sha1-X4uO2YrKVFZWv1cplzh/kEpyIlc=", - "dev": true + "dev": true, + "requires": { + "depd": "1.1.0", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" + } }, "http-signature": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true + "dev": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.0", + "sshpk": "1.13.1" + } }, "http2": { "version": "3.3.6", @@ -1486,7 +2104,12 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz", "integrity": "sha1-NffabEjOTdv6JkiRrFk+5f+GceY=", - "dev": true + "dev": true, + "requires": { + "agent-base": "2.1.1", + "debug": "2.6.7", + "extend": "3.0.1" + } }, "i": { "version": "0.3.5", @@ -1511,13 +2134,20 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true + "dev": true, + "requires": { + "repeating": "2.0.1" + } }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } }, "inherit": { "version": "2.2.6", @@ -1553,31 +2183,50 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true + "dev": true, + "requires": { + "builtin-modules": "1.1.1" + } }, "is-expression": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-2.1.0.tgz", "integrity": "sha1-kb6dR968/vB3l36XIr5tz7RGXvA=", - "dev": true + "dev": true, + "requires": { + "acorn": "3.3.0", + "object-assign": "4.1.1" + } }, "is-finite": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } }, "is-lower-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", "integrity": "sha1-fhR75HaNxGbbO/shzGCzHmrWk5M=", - "dev": true + "dev": true, + "requires": { + "lower-case": "1.1.4" + } }, "is-my-json-valid": { "version": "2.16.0", "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", - "dev": true + "dev": true, + "requires": { + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" + } }, "is-promise": { "version": "2.1.0", @@ -1595,7 +2244,10 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true + "dev": true, + "requires": { + "has": "1.0.1" + } }, "is-stream": { "version": "1.1.0", @@ -1613,7 +2265,10 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", "integrity": "sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8=", - "dev": true + "dev": true, + "requires": { + "upper-case": "1.1.3" + } }, "is-utf8": { "version": "0.2.1", @@ -1655,7 +2310,11 @@ "version": "3.5.5", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz", "integrity": "sha1-A3fDgBfKvHMisNH7zSWkkWQfL74=", - "dev": true + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "2.7.3" + } }, "jsbn": { "version": "0.1.1", @@ -1669,24 +2328,67 @@ "resolved": "https://registry.npmjs.org/jscs/-/jscs-3.0.7.tgz", "integrity": "sha1-cUG03/W4bjLQ6Z12S4NnZ8MNIBo=", "dev": true, + "requires": { + "chalk": "1.1.3", + "cli-table": "0.3.1", + "commander": "2.9.0", + "cst": "0.4.10", + "estraverse": "4.2.0", + "exit": "0.1.2", + "glob": "5.0.15", + "htmlparser2": "3.8.3", + "js-yaml": "3.4.6", + "jscs-jsdoc": "2.0.0", + "jscs-preset-wikimedia": "1.0.0", + "jsonlint": "1.6.2", + "lodash": "3.10.1", + "minimatch": "3.0.4", + "natural-compare": "1.2.2", + "pathval": "0.1.1", + "prompt": "0.2.14", + "reserved-words": "0.1.1", + "resolve": "1.1.7", + "strip-bom": "2.0.0", + "strip-json-comments": "1.0.4", + "to-double-quotes": "2.0.0", + "to-single-quotes": "2.0.1", + "vow": "0.4.16", + "vow-fs": "0.3.6", + "xmlbuilder": "3.1.0" + }, "dependencies": { "commander": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "dev": true + "dev": true, + "requires": { + "graceful-readlink": "1.0.1" + } }, "glob": { "version": "5.0.15", "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } }, "js-yaml": { "version": "3.4.6", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.4.6.tgz", "integrity": "sha1-a+GyP2JJ9T0pM3D9TRqqY84bTrA=", - "dev": true + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "2.7.3", + "inherit": "2.2.6" + } }, "uuid": { "version": "2.0.3", @@ -1705,12 +2407,26 @@ "resolved": "https://registry.npmjs.org/vow-fs/-/vow-fs-0.3.6.tgz", "integrity": "sha1-LUxZviLivyYY3fWXq0uqkjvnIA0=", "dev": true, + "requires": { + "glob": "7.1.2", + "uuid": "2.0.3", + "vow": "0.4.16", + "vow-queue": "0.4.2" + }, "dependencies": { "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } } } }, @@ -1718,7 +2434,10 @@ "version": "0.4.2", "resolved": "https://registry.npmjs.org/vow-queue/-/vow-queue-0.4.2.tgz", "integrity": "sha1-5/4XFg4Vx8QYTRtmapvGThjjAYQ=", - "dev": true + "dev": true, + "requires": { + "vow": "0.4.16" + } } } }, @@ -1726,7 +2445,11 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/jscs-jsdoc/-/jscs-jsdoc-2.0.0.tgz", "integrity": "sha1-9T684CmqMSW9iCkLpQ1k1FEKSHE=", - "dev": true + "dev": true, + "requires": { + "comment-parser": "0.3.1", + "jsdoctypeparser": "1.2.0" + } }, "jscs-preset-wikimedia": { "version": "1.0.0", @@ -1738,13 +2461,26 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-1.2.0.tgz", "integrity": "sha1-597cFToRhJ/8UUEUSuhqfvDCU5I=", - "dev": true + "dev": true, + "requires": { + "lodash": "3.10.1" + } }, "jshint": { "version": "2.9.5", "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.5.tgz", "integrity": "sha1-HnJSkVzmgbQIJ+4UJIxG006apiw=", "dev": true, + "requires": { + "cli": "1.0.1", + "console-browserify": "1.1.0", + "exit": "0.1.2", + "htmlparser2": "3.8.3", + "lodash": "3.7.0", + "minimatch": "3.0.4", + "shelljs": "0.3.0", + "strip-json-comments": "1.0.4" + }, "dependencies": { "lodash": { "version": "3.7.0", @@ -1770,7 +2506,10 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true + "dev": true, + "requires": { + "jsonify": "0.0.0" + } }, "json-stringify-safe": { "version": "5.0.1", @@ -1782,7 +2521,10 @@ "version": "2.4.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", - "dev": true + "dev": true, + "requires": { + "graceful-fs": "4.1.11" + } }, "jsonify": { "version": "0.0.0", @@ -1794,7 +2536,11 @@ "version": "1.6.2", "resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.2.tgz", "integrity": "sha1-VzcEUIX1XrRVxosf9OvAG9UOiDA=", - "dev": true + "dev": true, + "requires": { + "JSV": "4.0.2", + "nomnom": "1.8.1" + } }, "jsonpointer": { "version": "4.0.1", @@ -1807,6 +2553,12 @@ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -1820,13 +2572,11 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", - "dev": true - }, - "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 + "dev": true, + "requires": { + "is-promise": "2.1.0", + "promise": "7.3.1" + } }, "kew": { "version": "0.7.0", @@ -1838,13 +2588,19 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true + "dev": true, + "requires": { + "is-buffer": "1.1.5" + } }, "klaw": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", - "dev": true + "dev": true, + "requires": { + "graceful-fs": "4.1.11" + } }, "lazy-cache": { "version": "1.0.4", @@ -1856,13 +2612,26 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", - "dev": true + "dev": true, + "requires": { + "readable-stream": "2.3.3" + } }, "less": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/less/-/less-2.6.1.tgz", "integrity": "sha1-ZY4B7JrDFJlZxrbfvPvAoXCv2no=", "dev": true, + "requires": { + "errno": "0.1.4", + "graceful-fs": "4.1.11", + "image-size": "0.4.0", + "mime": "1.3.4", + "mkdirp": "0.5.1", + "promise": "7.3.1", + "request": "2.81.0", + "source-map": "0.5.6" + }, "dependencies": { "source-map": { "version": "0.5.6", @@ -1877,7 +2646,10 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz", "integrity": "sha1-2UpGSPmxwXnWT6lykSaL22zpQ08=", - "dev": true + "dev": true, + "requires": { + "uc.micro": "1.0.3" + } }, "livereload-js": { "version": "2.2.2", @@ -1889,13 +2661,26 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-3.5.2.tgz", "integrity": "sha1-ByhWEYD9IP+KaSdQWFL8WKrqDIg=", - "dev": true + "dev": true, + "requires": { + "arrify": "1.0.1", + "multimatch": "2.1.0", + "pkg-up": "1.0.0", + "resolve-pkg": "0.1.0" + } }, "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 + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + } }, "lodash": { "version": "3.10.1", @@ -1913,7 +2698,11 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true + "dev": true, + "requires": { + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.2" + } }, "lower-case": { "version": "1.1.4", @@ -1925,7 +2714,10 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz", "integrity": "sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E=", - "dev": true + "dev": true, + "requires": { + "lower-case": "1.1.4" + } }, "lru-cache": { "version": "2.7.3", @@ -1944,6 +2736,13 @@ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-7.0.1.tgz", "integrity": "sha1-8S2LiKk+ZCVDSN/Rg71wv2BWekI=", "dev": true, + "requires": { + "argparse": "1.0.9", + "entities": "1.1.1", + "linkify-it": "2.0.3", + "mdurl": "1.0.1", + "uc.micro": "1.0.3" + }, "dependencies": { "entities": { "version": "1.1.1", @@ -1958,12 +2757,22 @@ "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-1.1.0.tgz", "integrity": "sha1-cTZehKmd2Piz99X94vANHn9zvmE=", "dev": true, + "requires": { + "chalk": "1.1.3", + "figures": "1.7.0", + "gzip-size": "1.0.0", + "pretty-bytes": "1.0.4" + }, "dependencies": { "pretty-bytes": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", - "dev": true + "dev": true, + "requires": { + "get-stdin": "4.0.1", + "meow": "3.7.0" + } } } }, @@ -1983,7 +2792,19 @@ "version": "3.7.0", "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true + "dev": true, + "requires": { + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.0", + "normalize-package-data": "2.4.0", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" + } }, "mime": { "version": "1.3.4", @@ -2001,13 +2822,19 @@ "version": "2.1.15", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", - "dev": true + "dev": true, + "requires": { + "mime-db": "1.27.0" + } }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } }, "minimist": { "version": "1.2.0", @@ -2020,6 +2847,9 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, + "requires": { + "minimist": "0.0.8" + }, "dependencies": { "minimist": { "version": "0.0.8", @@ -2034,12 +2864,22 @@ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.8.2.tgz", "integrity": "sha1-eErHc05KRTqcbm6GgKkyknXItoc=", "dev": true, + "requires": { + "basic-auth": "1.1.0", + "debug": "2.6.8", + "depd": "1.1.0", + "on-finished": "2.3.0", + "on-headers": "1.0.1" + }, "dependencies": { "debug": { "version": "2.6.8", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", - "dev": true + "dev": true, + "requires": { + "ms": "2.0.0" + } } } }, @@ -2053,7 +2893,13 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", - "dev": true + "dev": true, + "requires": { + "array-differ": "1.0.0", + "array-union": "1.0.2", + "arrify": "1.0.1", + "minimatch": "3.0.4" + } }, "mute-stream": { "version": "0.0.7", @@ -2071,7 +2917,10 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/ncname/-/ncname-1.0.0.tgz", "integrity": "sha1-W1etGLHKCShk72Kwse2BlPODtxw=", - "dev": true + "dev": true, + "requires": { + "xml-char-classes": "1.0.0" + } }, "ncp": { "version": "0.4.2", @@ -2089,7 +2938,10 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz", "integrity": "sha1-euuhxzpSGEJlVUt9wDuvcg34AIE=", - "dev": true + "dev": true, + "requires": { + "lower-case": "1.1.4" + } }, "node-uuid": { "version": "1.4.0", @@ -2102,6 +2954,10 @@ "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", @@ -2113,7 +2969,12 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", - "dev": true + "dev": true, + "requires": { + "ansi-styles": "1.0.0", + "has-color": "0.1.7", + "strip-ansi": "0.1.1" + } }, "strip-ansi": { "version": "0.1.1", @@ -2127,19 +2988,31 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true + "dev": true, + "requires": { + "abbrev": "1.1.0" + } }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "dev": true + "dev": true, + "requires": { + "hosted-git-info": "2.5.0", + "is-builtin-module": "1.0.0", + "semver": "5.3.0", + "validate-npm-package-license": "3.0.1" + } }, "normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true + "dev": true, + "requires": { + "remove-trailing-separator": "1.0.2" + } }, "num2fraction": { "version": "1.2.2", @@ -2169,7 +3042,10 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true + "dev": true, + "requires": { + "ee-first": "1.1.1" + } }, "on-headers": { "version": "1.0.1", @@ -2181,13 +3057,20 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true + "dev": true, + "requires": { + "wrappy": "1.0.2" + } }, "opn": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", - "dev": true + "dev": true, + "requires": { + "object-assign": "4.1.1", + "pinkie-promise": "2.0.1" + } }, "os-tmpdir": { "version": "1.0.2", @@ -2211,13 +3094,19 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", - "dev": true + "dev": true, + "requires": { + "no-case": "2.3.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 + "dev": true, + "requires": { + "error-ex": "1.3.1" + } }, "parse-ms": { "version": "1.0.1", @@ -2241,19 +3130,29 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.1.tgz", "integrity": "sha1-LVeNNFX2YNpl7KGO+VtODekSdh4=", - "dev": true + "dev": true, + "requires": { + "camel-case": "3.0.0", + "upper-case-first": "1.1.2" + } }, "path-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/path-case/-/path-case-2.1.1.tgz", "integrity": "sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU=", - "dev": true + "dev": true, + "requires": { + "no-case": "2.3.1" + } }, "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 + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } }, "path-is-absolute": { "version": "1.0.1", @@ -2265,7 +3164,12 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } }, "pathval": { "version": "0.1.1", @@ -2290,18 +3194,34 @@ "resolved": "https://registry.npmjs.org/phantomjs/-/phantomjs-1.9.20.tgz", "integrity": "sha1-RCSsog4U0lXAsIia9va4lz2hDg0=", "dev": true, + "requires": { + "extract-zip": "1.5.0", + "fs-extra": "0.26.7", + "hasha": "2.2.0", + "kew": "0.7.0", + "progress": "1.1.8", + "request": "2.67.0", + "request-progress": "2.0.1", + "which": "1.2.14" + }, "dependencies": { "async": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", - "dev": true + "dev": true, + "requires": { + "lodash": "4.17.4" + } }, "bl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.3.tgz", "integrity": "sha1-/FQhoo/UImA2w7OJGmaiW8ZNIm4=", - "dev": true + "dev": true, + "requires": { + "readable-stream": "2.0.6" + } }, "caseless": { "version": "0.11.0", @@ -2319,13 +3239,24 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.1.tgz", "integrity": "sha1-rjFduaSQf6BlUCMEpm13M0de43w=", - "dev": true + "dev": true, + "requires": { + "async": "2.5.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" + } }, "har-validator": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "dev": true + "dev": true, + "requires": { + "chalk": "1.1.3", + "commander": "2.11.0", + "is-my-json-valid": "2.16.0", + "pinkie-promise": "2.0.1" + } }, "lodash": { "version": "4.17.4", @@ -2349,13 +3280,43 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } }, "request": { "version": "2.67.0", "resolved": "https://registry.npmjs.org/request/-/request-2.67.0.tgz", "integrity": "sha1-ivdHgOK/EeoK6aqWXBHxGv0nJ0I=", - "dev": true + "dev": true, + "requires": { + "aws-sign2": "0.6.0", + "bl": "1.0.3", + "caseless": "0.11.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "1.0.1", + "har-validator": "2.0.6", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.15", + "node-uuid": "1.4.8", + "oauth-sign": "0.8.2", + "qs": "5.2.1", + "stringstream": "0.0.5", + "tough-cookie": "2.2.2", + "tunnel-agent": "0.4.3" + } }, "string_decoder": { "version": "0.10.31", @@ -2393,13 +3354,19 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true + "dev": true, + "requires": { + "pinkie": "2.0.4" + } }, "pkg-up": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz", "integrity": "sha1-Pgj7RhUlxEIWJKM7n35tCvWwWiY=", - "dev": true + "dev": true, + "requires": { + "find-up": "1.1.2" + } }, "pkginfo": { "version": "0.4.0", @@ -2417,25 +3384,41 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-1.2.0.tgz", "integrity": "sha1-sUu9olfRTDEPqcwJaCrwLUCWGAI=", - "dev": true + "dev": true, + "requires": { + "async": "1.5.2" + } }, "postcss": { "version": "4.1.16", "resolved": "https://registry.npmjs.org/postcss/-/postcss-4.1.16.tgz", "integrity": "sha1-TESbTIr53zyvbTf44eV10DYXWNw=", - "dev": true + "dev": true, + "requires": { + "es6-promise": "2.3.0", + "js-base64": "2.1.9", + "source-map": "0.4.4" + } }, "pretty-bytes": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-3.0.1.tgz", "integrity": "sha1-J9AAjXeAY6C0gRuzXHnxvV1fvM8=", - "dev": true + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } }, "pretty-ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-2.1.0.tgz", "integrity": "sha1-QlfCVt8/sLRR1q/6qwIYhBJpgdw=", - "dev": true + "dev": true, + "requires": { + "is-finite": "1.0.2", + "parse-ms": "1.0.1", + "plur": "1.0.0" + } }, "process-nextick-args": { "version": "1.0.7", @@ -2453,13 +3436,23 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true + "dev": true, + "requires": { + "asap": "2.0.5" + } }, "prompt": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/prompt/-/prompt-0.2.14.tgz", "integrity": "sha1-V3VPZPVD/XsIRXB8gY7OYY8F/9w=", - "dev": true + "dev": true, + "requires": { + "pkginfo": "0.4.0", + "read": "1.0.7", + "revalidator": "0.1.8", + "utile": "0.2.1", + "winston": "0.8.3" + } }, "prr": { "version": "0.0.0", @@ -2472,19 +3465,44 @@ "version": "2.0.0-rc.2", "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.0-rc.2.tgz", "integrity": "sha1-B4RVJ3kKssa+Z9z16x8xgECB8Eo=", - "dev": true + "dev": true, + "requires": { + "pug-code-gen": "1.1.1", + "pug-filters": "2.1.3", + "pug-lexer": "3.1.0", + "pug-linker": "3.0.1", + "pug-load": "2.0.7", + "pug-parser": "3.0.0", + "pug-runtime": "2.0.3", + "pug-strip-comments": "1.0.2" + } }, "pug-attrs": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.2.tgz", "integrity": "sha1-i+KyIlVo/6ddG4Zpgr/59BEa/8s=", - "dev": true + "dev": true, + "requires": { + "constantinople": "3.1.0", + "js-stringify": "1.0.2", + "pug-runtime": "2.0.3" + } }, "pug-code-gen": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-1.1.1.tgz", "integrity": "sha1-HPcnRO8qA56uajNAyqoRBYcSWOg=", - "dev": true + "dev": true, + "requires": { + "constantinople": "3.1.0", + "doctypes": "1.1.0", + "js-stringify": "1.0.2", + "pug-attrs": "2.0.2", + "pug-error": "1.3.2", + "pug-runtime": "2.0.3", + "void-elements": "2.0.1", + "with": "5.1.1" + } }, "pug-error": { "version": "1.3.2", @@ -2496,13 +3514,27 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-2.1.3.tgz", "integrity": "sha1-1ZdnoiDeeX3XVUifZoNM+aqDqlQ=", - "dev": true + "dev": true, + "requires": { + "clean-css": "3.4.27", + "constantinople": "3.1.0", + "jstransformer": "1.0.0", + "pug-error": "1.3.2", + "pug-walk": "1.1.3", + "resolve": "1.1.7", + "uglify-js": "2.6.4" + } }, "pug-lexer": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-3.1.0.tgz", "integrity": "sha1-/QhzdtSmdbT1n4/vQiiDQ06VgaI=", "dev": true, + "requires": { + "character-parser": "2.2.0", + "is-expression": "3.0.0", + "pug-error": "1.3.2" + }, "dependencies": { "acorn": { "version": "4.0.13", @@ -2514,7 +3546,11 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", - "dev": true + "dev": true, + "requires": { + "acorn": "4.0.13", + "object-assign": "4.1.1" + } } } }, @@ -2522,19 +3558,31 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.1.tgz", "integrity": "sha1-uj+P8hPKjzowSFm0T+0Tynud+hk=", - "dev": true + "dev": true, + "requires": { + "pug-error": "1.3.2", + "pug-walk": "1.1.3" + } }, "pug-load": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.7.tgz", "integrity": "sha1-Ux0MbhFUYBDphGMNA99AY2fS3nc=", - "dev": true + "dev": true, + "requires": { + "object-assign": "4.1.1", + "pug-walk": "1.1.3" + } }, "pug-parser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-3.0.0.tgz", "integrity": "sha1-N8YZ3YAPZCGHzk1s4aFkzddUh6M=", - "dev": true + "dev": true, + "requires": { + "pug-error": "1.3.2", + "token-stream": "0.0.1" + } }, "pug-runtime": { "version": "2.0.3", @@ -2546,7 +3594,10 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.2.tgz", "integrity": "sha1-0xOvoBvMN0mA4TmeI+vy65vchRM=", - "dev": true + "dev": true, + "requires": { + "pug-error": "1.3.2" + } }, "pug-walk": { "version": "1.1.3", @@ -2583,6 +3634,11 @@ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz", "integrity": "sha1-rf6s4uT7MJgFgBTQjActzFl1h3Q=", "dev": true, + "requires": { + "bytes": "2.4.0", + "iconv-lite": "0.4.13", + "unpipe": "1.0.0" + }, "dependencies": { "bytes": { "version": "2.4.0", @@ -2602,37 +3658,65 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", - "dev": true + "dev": true, + "requires": { + "mute-stream": "0.0.7" + } }, "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 + "dev": true, + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.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 + "dev": true, + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + } }, "readable-stream": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } }, "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true + "dev": true, + "requires": { + "resolve": "1.1.7" + } }, "redent": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true + "dev": true, + "requires": { + "indent-string": "2.1.0", + "strip-indent": "1.0.1" + } }, "regenerator-runtime": { "version": "0.10.5", @@ -2662,25 +3746,61 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true + "dev": true, + "requires": { + "is-finite": "1.0.2" + } }, "request": { "version": "2.81.0", "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "dev": true + "dev": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.15", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + } }, "request-progress": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz", "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=", - "dev": true + "dev": true, + "requires": { + "throttleit": "1.0.0" + } }, "requestretry": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.9.1.tgz", "integrity": "sha1-CioATq8hGWnEzCz+vz/p5XuSx04=", - "dev": true + "dev": true, + "requires": { + "extend": "3.0.1", + "fg-lodash": "0.0.2", + "request": "2.81.0", + "when": "3.7.8" + } }, "reserved-words": { "version": "0.1.1", @@ -2704,7 +3824,10 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/resolve-pkg/-/resolve-pkg-0.1.0.tgz", "integrity": "sha1-AsyZNBDik2livZcWahsHfalyVTE=", - "dev": true + "dev": true, + "requires": { + "resolve-from": "2.0.0" + } }, "revalidator": { "version": "0.1.8", @@ -2716,7 +3839,10 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true + "dev": true, + "requires": { + "align-text": "0.1.4" + } }, "rimraf": { "version": "2.2.8", @@ -2734,13 +3860,21 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/sauce-tunnel/-/sauce-tunnel-2.5.0.tgz", "integrity": "sha1-DuTE/5tH4BPosHLL+sSVt/7Y6Os=", - "dev": true + "dev": true, + "requires": { + "chalk": "1.1.3", + "request": "2.81.0", + "split": "1.0.0" + } }, "saucelabs": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.2.0.tgz", "integrity": "sha1-XoBHazbaG0LRDzlwfprypTsD2Io=", - "dev": true + "dev": true, + "requires": { + "https-proxy-agent": "1.0.0" + } }, "semver": { "version": "5.3.0", @@ -2752,25 +3886,56 @@ "version": "0.15.3", "resolved": "https://registry.npmjs.org/send/-/send-0.15.3.tgz", "integrity": "sha1-UBP5+ZAj31DRvZiSwZ4979HVMwk=", - "dev": true + "dev": true, + "requires": { + "debug": "2.6.7", + "depd": "1.1.0", + "destroy": "1.0.4", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.0", + "fresh": "0.5.0", + "http-errors": "1.6.1", + "mime": "1.3.4", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.3.1" + } }, "sentence-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-2.1.1.tgz", "integrity": "sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ=", - "dev": true + "dev": true, + "requires": { + "no-case": "2.3.1", + "upper-case-first": "1.1.2" + } }, "serve-index": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.0.tgz", "integrity": "sha1-0rKA/FYNYW7oG0i/D6gqvtJIXOc=", "dev": true, + "requires": { + "accepts": "1.3.3", + "batch": "0.6.1", + "debug": "2.6.8", + "escape-html": "1.0.3", + "http-errors": "1.6.1", + "mime-types": "2.1.15", + "parseurl": "1.3.1" + }, "dependencies": { "debug": { "version": "2.6.8", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", - "dev": true + "dev": true, + "requires": { + "ms": "2.0.0" + } } } }, @@ -2778,7 +3943,13 @@ "version": "1.12.3", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.3.tgz", "integrity": "sha1-n0uhni8wMMVH+K+ZEHg47DjVseI=", - "dev": true + "dev": true, + "requires": { + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "parseurl": "1.3.1", + "send": "0.15.3" + } }, "setprototypeof": { "version": "1.0.3", @@ -2790,13 +3961,22 @@ "version": "0.7.8", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", - "dev": true + "dev": true, + "requires": { + "glob": "7.0.6", + "interpret": "1.0.3", + "rechoir": "0.6.2" + } }, "shx": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/shx/-/shx-0.1.4.tgz", "integrity": "sha1-mjqUJx5su3iBSf3GaWI3FUq2N58=", - "dev": true + "dev": true, + "requires": { + "minimist": "1.2.0", + "shelljs": "0.7.8" + } }, "sigmund": { "version": "1.0.1", @@ -2814,25 +3994,37 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", "integrity": "sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=", - "dev": true + "dev": true, + "requires": { + "no-case": "2.3.1" + } }, "sntp": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true + "dev": true, + "requires": { + "hoek": "2.16.3" + } }, "source-map": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true + "dev": true, + "requires": { + "amdefine": "1.0.1" + } }, "source-map-support": { "version": "0.4.15", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz", "integrity": "sha1-AyAt9lwG0r2MfsI2KhkwVv7407E=", "dev": true, + "requires": { + "source-map": "0.5.6" + }, "dependencies": { "source-map": { "version": "0.5.6", @@ -2846,7 +4038,10 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", - "dev": true + "dev": true, + "requires": { + "spdx-license-ids": "1.2.2" + } }, "spdx-expression-parse": { "version": "1.0.4", @@ -2864,7 +4059,10 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/split/-/split-1.0.0.tgz", "integrity": "sha1-xDlc5oOrzSVLwo/h2rtuXCfc/64=", - "dev": true + "dev": true, + "requires": { + "through": "2.3.8" + } }, "sprintf-js": { "version": "1.0.3", @@ -2877,6 +4075,16 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", "dev": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -2908,7 +4116,10 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } }, "stringstream": { "version": "0.0.5", @@ -2920,19 +4131,28 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } }, "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 + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } }, "strip-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true + "dev": true, + "requires": { + "get-stdin": "4.0.1" + } }, "strip-json-comments": { "version": "1.0.4", @@ -2950,19 +4170,32 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", "integrity": "sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM=", - "dev": true + "dev": true, + "requires": { + "lower-case": "1.1.4", + "upper-case": "1.1.3" + } }, "tar-stream": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz", "integrity": "sha1-NlSc8E7RrumyowwBQyUiONr5QBY=", - "dev": true + "dev": true, + "requires": { + "bl": "1.2.1", + "end-of-stream": "1.4.0", + "readable-stream": "2.3.3", + "xtend": "4.0.1" + } }, "temporary": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/temporary/-/temporary-0.0.8.tgz", "integrity": "sha1-oYqYHSi6jKNgJ/s8MFOMPst0CsA=", - "dev": true + "dev": true, + "requires": { + "package": "1.0.1" + } }, "text-table": { "version": "0.2.0", @@ -2986,7 +4219,16 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/time-grunt/-/time-grunt-1.4.0.tgz", "integrity": "sha1-BiIT5mDJB+hvRAVWwB6mWXtxJCA=", - "dev": true + "dev": true, + "requires": { + "chalk": "1.1.3", + "date-time": "1.1.0", + "figures": "1.7.0", + "hooker": "0.2.3", + "number-is-nan": "1.0.1", + "pretty-ms": "2.1.0", + "text-table": "0.2.0" + } }, "time-zone": { "version": "0.1.0", @@ -2999,12 +4241,23 @@ "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.2.1.tgz", "integrity": "sha1-s/26gC5dVqM8L28QeUsy5Hescp0=", "dev": true, + "requires": { + "body-parser": "1.14.2", + "debug": "2.2.0", + "faye-websocket": "0.10.0", + "livereload-js": "2.2.2", + "parseurl": "1.3.1", + "qs": "5.1.0" + }, "dependencies": { "debug": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true + "dev": true, + "requires": { + "ms": "0.7.1" + } }, "ms": { "version": "0.7.1", @@ -3024,13 +4277,20 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz", "integrity": "sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=", - "dev": true + "dev": true, + "requires": { + "no-case": "2.3.1", + "upper-case": "1.1.3" + } }, "tmp": { "version": "0.0.31", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", - "dev": true + "dev": true, + "requires": { + "os-tmpdir": "1.0.2" + } }, "to-double-quotes": { "version": "2.0.0", @@ -3054,7 +4314,10 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", - "dev": true + "dev": true, + "requires": { + "punycode": "1.4.1" + } }, "trim-newlines": { "version": "1.0.0", @@ -3066,7 +4329,10 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } }, "tweetnacl": { "version": "0.14.5", @@ -3079,7 +4345,11 @@ "version": "1.6.15", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", - "dev": true + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.15" + } }, "typedarray": { "version": "0.0.6", @@ -3098,6 +4368,12 @@ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.6.4.tgz", "integrity": "sha1-ZeovswWck5RpLxX+2HwrNsFrmt8=", "dev": true, + "requires": { + "async": "0.2.10", + "source-map": "0.5.6", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + }, "dependencies": { "async": { "version": "0.2.10", @@ -3147,7 +4423,10 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", "integrity": "sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=", - "dev": true + "dev": true, + "requires": { + "upper-case": "1.1.3" + } }, "uri-path": { "version": "1.0.0", @@ -3166,6 +4445,14 @@ "resolved": "https://registry.npmjs.org/utile/-/utile-0.2.1.tgz", "integrity": "sha1-kwyI6ZCY1iIINMNWy9mncFItkNc=", "dev": true, + "requires": { + "async": "0.2.10", + "deep-equal": "1.0.1", + "i": "0.3.5", + "mkdirp": "0.5.1", + "ncp": "0.4.2", + "rimraf": "2.2.8" + }, "dependencies": { "async": { "version": "0.2.10", @@ -3191,13 +4478,20 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", - "dev": true + "dev": true, + "requires": { + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" + } }, "verror": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", - "dev": true + "dev": true, + "requires": { + "extsprintf": "1.0.2" + } }, "void-elements": { "version": "2.0.1", @@ -3216,18 +4510,32 @@ "resolved": "https://registry.npmjs.org/vow-fs/-/vow-fs-0.3.2.tgz", "integrity": "sha1-6isDTYXh24wnfrLpqG0cFfXTjno=", "dev": true, + "requires": { + "glob": "3.2.8", + "node-uuid": "1.4.0", + "vow": "0.4.4", + "vow-queue": "0.3.1" + }, "dependencies": { "glob": { "version": "3.2.8", "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.8.tgz", "integrity": "sha1-VQb0MRchvMYYx9jboUQYh1AwcHM=", - "dev": true + "dev": true, + "requires": { + "inherits": "2.0.3", + "minimatch": "0.2.14" + } }, "minimatch": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", - "dev": true + "dev": true, + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } } } }, @@ -3235,7 +4543,10 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/vow-queue/-/vow-queue-0.3.1.tgz", "integrity": "sha1-WYxRoVsKgabV/AX0dhzrRi3h6Gg=", - "dev": true + "dev": true, + "requires": { + "vow": "0.4.4" + } }, "walkdir": { "version": "0.0.11", @@ -3247,7 +4558,10 @@ "version": "0.6.5", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", - "dev": true + "dev": true, + "requires": { + "websocket-extensions": "0.1.1" + } }, "websocket-extensions": { "version": "0.1.1", @@ -3265,7 +4579,10 @@ "version": "1.2.14", "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", - "dev": true + "dev": true, + "requires": { + "isexe": "2.0.0" + } }, "window-size": { "version": "0.1.0", @@ -3278,6 +4595,15 @@ "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", "integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=", "dev": true, + "requires": { + "async": "0.2.10", + "colors": "0.6.2", + "cycle": "1.0.3", + "eyes": "0.1.8", + "isstream": "0.1.2", + "pkginfo": "0.3.1", + "stack-trace": "0.0.10" + }, "dependencies": { "async": { "version": "0.2.10", @@ -3303,7 +4629,11 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", - "dev": true + "dev": true, + "requires": { + "acorn": "3.3.0", + "acorn-globals": "3.1.0" + } }, "wordwrap": { "version": "0.0.2", @@ -3327,7 +4657,10 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-3.1.0.tgz", "integrity": "sha1-LIaIjy1OrehQ+jjKf3Ij9yCVFuE=", - "dev": true + "dev": true, + "requires": { + "lodash": "3.10.1" + } }, "xtend": { "version": "4.0.1", @@ -3340,6 +4673,12 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + }, "dependencies": { "camelcase": { "version": "1.2.1", @@ -3353,13 +4692,22 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", - "dev": true + "dev": true, + "requires": { + "fd-slicer": "1.0.1" + } }, "zip-stream": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.2.0.tgz", "integrity": "sha1-qLxF9MG0lpnGuQGYuqyqzbzUugQ=", "dev": true, + "requires": { + "archiver-utils": "1.3.0", + "compress-commons": "1.2.0", + "lodash": "4.17.4", + "readable-stream": "2.3.3" + }, "dependencies": { "lodash": { "version": "4.17.4", diff --git a/src/NuGetGallery.Core/CoreConstants.cs b/src/NuGetGallery.Core/CoreConstants.cs index e0f133bd12..0050fabc8c 100644 --- a/src/NuGetGallery.Core/CoreConstants.cs +++ b/src/NuGetGallery.Core/CoreConstants.cs @@ -5,12 +5,16 @@ namespace NuGetGallery { public static class CoreConstants { + public const string AdminRoleName = "Admins"; + public const int MaxPackageIdLength = 128; public const string PackageFileSavePathTemplate = "{0}.{1}{2}"; public const string NuGetPackageFileExtension = ".nupkg"; + public const string Sha512HashAlgorithmId = "SHA512"; + public const string PackageContentType = "binary/octet-stream"; public const string OctetStreamContentType = "application/octet-stream"; public const string TextContentType = "text/plain"; diff --git a/src/NuGetGallery.Core/Entities/Organization.cs b/src/NuGetGallery.Core/Entities/Organization.cs index 071031f677..fa054e67c0 100644 --- a/src/NuGetGallery.Core/Entities/Organization.cs +++ b/src/NuGetGallery.Core/Entities/Organization.cs @@ -1,7 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; namespace NuGetGallery { @@ -15,14 +18,18 @@ namespace NuGetGallery /// public class Organization : User { - public Organization() : base() + public Organization() : this(null) { - Members = new List(); } public Organization(string name) : base(name) { Members = new List(); + + _administrators = new Lazy>( + () => Members.Where(m => m.IsAdmin).Select(m => m.Member).ToList()); + _collaborators = new Lazy>( + () => Members.Where(m => !m.IsAdmin).Select(m => m.Member).ToList()); } /// @@ -34,5 +41,31 @@ public Organization(string name) : base(name) /// Requests to become a member of this . /// public virtual ICollection MemberRequests { get; set; } + + #region per-request query cache + + private Lazy> _administrators; + private Lazy> _collaborators; + + [NotMapped] + public IEnumerable Administrators + { + get + { + return _administrators.Value; + } + } + + [NotMapped] + public IEnumerable Collaborators + { + get + { + return _collaborators.Value; + } + } + + #endregion + } } diff --git a/src/NuGetGallery.Core/Entities/User.cs b/src/NuGetGallery.Core/Entities/User.cs index 47e149b0a9..87fe8aa2d3 100644 --- a/src/NuGetGallery.Core/Entities/User.cs +++ b/src/NuGetGallery.Core/Entities/User.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace NuGetGallery { @@ -17,6 +18,25 @@ namespace NuGetGallery /// public class User : IEntity, IEquatable { + #region per-request query cache + + private bool? _isAdmin; + + [NotMapped] + public bool IsAdministrator + { + get + { + if (!_isAdmin.HasValue) + { + _isAdmin = IsInRole(CoreConstants.AdminRoleName); + } + return _isAdmin.Value; + } + } + + #endregion + public User() : this(null) { } diff --git a/src/NuGetGallery.Core/NuGetGallery.Core.csproj b/src/NuGetGallery.Core/NuGetGallery.Core.csproj index 900903ed79..0964d1bfde 100644 --- a/src/NuGetGallery.Core/NuGetGallery.Core.csproj +++ b/src/NuGetGallery.Core/NuGetGallery.Core.csproj @@ -240,6 +240,7 @@ + diff --git a/src/NuGetGallery/Infrastructure/Authentication/CryptographyService.cs b/src/NuGetGallery.Core/Services/CryptographyService.cs similarity index 93% rename from src/NuGetGallery/Infrastructure/Authentication/CryptographyService.cs rename to src/NuGetGallery.Core/Services/CryptographyService.cs index c439a8e7c9..ee8f50f71d 100644 --- a/src/NuGetGallery/Infrastructure/Authentication/CryptographyService.cs +++ b/src/NuGetGallery.Core/Services/CryptographyService.cs @@ -12,7 +12,7 @@ public static class CryptographyService { public static string GenerateHash( Stream input, - string hashAlgorithmId = Constants.Sha512HashAlgorithmId) + string hashAlgorithmId = CoreConstants.Sha512HashAlgorithmId) { input.Position = 0; diff --git a/src/NuGetGallery/App_Code/ViewHelpers.cshtml b/src/NuGetGallery/App_Code/ViewHelpers.cshtml index 9ab9e75de4..7831577342 100644 --- a/src/NuGetGallery/App_Code/ViewHelpers.cshtml +++ b/src/NuGetGallery/App_Code/ViewHelpers.cshtml @@ -495,7 +495,9 @@ var hlp = new AccordionHelper(name, formModelStatePrefix, expanded, page); Func content, bool expanded = true, string expandedIcon = "ChevronDown", - string collapsedIcon = "ChevronRight") + string collapsedIcon = "ChevronRight", + string disabledIcon = "Lock", + bool disabled = false) { @Section(viewPage, id, @@ -515,29 +517,45 @@ var hlp = new AccordionHelper(name, formModelStatePrefix, expanded, page); Func content, bool expanded = false, string expandedIcon = "ChevronDown", - string collapsedIcon = "ChevronRight") + string collapsedIcon = "ChevronRight", + string disabledIcon = "Lock", + bool disabled = false) { - AddSection(viewPage.ViewBag, id); + if (!disabled) + { + AddSection(viewPage.ViewBag, id); + }

- - + @if (!disabled) + { + + + @title(MvcHtmlString.Empty) + + } + else + { + @title(MvcHtmlString.Empty) - + }

@data(MvcHtmlString.Empty)
-
-
- @content(MvcHtmlString.Empty) + if (!disabled) + { +
+
+ @content(MvcHtmlString.Empty) +
-
+ } } \ No newline at end of file diff --git a/src/NuGetGallery/App_Start/AppActivator.cs b/src/NuGetGallery/App_Start/AppActivator.cs index 019b8dc6f8..ac89a4c22b 100644 --- a/src/NuGetGallery/App_Start/AppActivator.cs +++ b/src/NuGetGallery/App_Start/AppActivator.cs @@ -193,7 +193,8 @@ private static void BundlingPostStart() .Include("~/Scripts/gallery/knockout-3.4.2.js") .Include("~/Scripts/gallery/bootstrap.js") .Include("~/Scripts/gallery/moment-2.18.1.js") - .Include("~/Scripts/gallery/common.js"); + .Include("~/Scripts/gallery/common.js") + .Include("~/Scripts/gallery/autocomplete.js"); BundleTable.Bundles.Add(newScriptBundle); var d3ScriptBundle = new ScriptBundle("~/Scripts/gallery/stats.min.js") diff --git a/src/NuGetGallery/App_Start/Routes.cs b/src/NuGetGallery/App_Start/Routes.cs index cf63407109..a12cbab7d5 100644 --- a/src/NuGetGallery/App_Start/Routes.cs +++ b/src/NuGetGallery/App_Start/Routes.cs @@ -272,7 +272,7 @@ public static void RegisterUIRoutes(RouteCollection routes) routes.MapRoute( RouteName.ConfirmAccount, - "account/confirm/{username}/{token}", + "account/confirm/{accountName}/{token}", new { controller = "Users", action = "Confirm" }, new RouteExtensions.ObfuscatedMetadata(2, Obfuscator.DefaultTelemetryUserName)); @@ -522,6 +522,11 @@ public static void RegisterApiV2Routes(RouteCollection routes) "api/v2/package-versions/{id}", new { controller = "Api", action = "PackageVersions" }); + routes.MapRoute( + "v2Query", + "api/v2/query", + new { controller = "Api", action = "Query" }); + routes.MapRoute( RouteName.StatisticsDownloadsApi, "api/v2/stats/downloads/last6weeks", diff --git a/src/NuGetGallery/App_Start/StorageDependent.cs b/src/NuGetGallery/App_Start/StorageDependent.cs index 08f41bbcab..ed621f20b0 100644 --- a/src/NuGetGallery/App_Start/StorageDependent.cs +++ b/src/NuGetGallery/App_Start/StorageDependent.cs @@ -85,7 +85,6 @@ public static IEnumerable GetAll(IAppConfiguration configurati var dependents = new[] { Create(configuration.AzureStorage_Content_ConnectionString, isSingleInstance: true), - Create(configuration.AzureStorage_NuGetExe_ConnectionString, isSingleInstance: false), Create(configuration.AzureStorage_Packages_ConnectionString, isSingleInstance: false), Create(configuration.AzureStorage_Uploads_ConnectionString, isSingleInstance: false), }; diff --git a/src/NuGetGallery/Authentication/Providers/AzureActiveDirectoryV2/AzureActiveDirectoryV2Authenticator.cs b/src/NuGetGallery/Authentication/Providers/AzureActiveDirectoryV2/AzureActiveDirectoryV2Authenticator.cs index 6a559bbbb6..67e313e0e4 100644 --- a/src/NuGetGallery/Authentication/Providers/AzureActiveDirectoryV2/AzureActiveDirectoryV2Authenticator.cs +++ b/src/NuGetGallery/Authentication/Providers/AzureActiveDirectoryV2/AzureActiveDirectoryV2Authenticator.cs @@ -3,8 +3,10 @@ using System; using System.Security.Claims; +using System.Threading.Tasks; using System.Web.Mvc; using Microsoft.IdentityModel.Protocols; +using Microsoft.Owin.Security.Notifications; using Microsoft.Owin.Security.OpenIdConnect; using NuGetGallery.Configuration; using Owin; @@ -33,6 +35,8 @@ public static class AuthenticationType public static readonly string V2CommonTenant = "common"; public static readonly string Authority = "https://login.microsoftonline.com/{0}/v2.0"; + private static readonly string ACCESS_DENIED = "access_denied"; + protected override void AttachToOwinApp(IGalleryConfigurationService config, IAppBuilder app) { // Fetch site root from configuration @@ -52,13 +56,17 @@ protected override void AttachToOwinApp(IGalleryConfigurationService config, IAp Scope = OpenIdConnectScopes.OpenIdProfile + " email", ResponseType = OpenIdConnectResponseTypes.CodeIdToken, TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters() { ValidateIssuer = false }, + Notifications = new OpenIdConnectAuthenticationNotifications + { + AuthenticationFailed = AuthenticationFailed + } }; Config.ApplyToOwinSecurityOptions(options); app.UseOpenIdConnectAuthentication(options); } - + public override AuthenticatorUI GetUI() { return new AuthenticatorUI( @@ -136,5 +144,31 @@ public override IdentityInformation GetIdentityInformation(ClaimsIdentity claims return new IdentityInformation(identifier, nameClaim?.Value, emailClaim.Value, authenticationType, tenantId); } + + // The OpenIdConnect. throws OpenIdConnectProtocolException upon denial of access permissions by the user, + // this could result in an internal server error, catch this exception and continue to the controller where appropriate + // error handling is done. + private Task AuthenticationFailed(AuthenticationFailedNotification notification) + { + if (notification.Exception.Message == ACCESS_DENIED) + { + // For every 'Challenge' sent to the external providers, we store the 'State' + // with the redirect uri where we intend to return after successful authentication. + // Extract this "RedirectUri" property from this "State" object for redirecting on failed authentication as well. + var authenticationPropertiesEncodedString = notification + .ProtocolMessage + .State + .Split('='); + var authenticationProperties = notification + .Options + .StateDataFormat + .Unprotect(authenticationPropertiesEncodedString[1]); + + notification.HandleResponse(); + notification.Response.Redirect(authenticationProperties.RedirectUri); + } + + return Task.FromResult(0); + } } } \ No newline at end of file diff --git a/src/NuGetGallery/Configuration/AppConfiguration.cs b/src/NuGetGallery/Configuration/AppConfiguration.cs index 91ffbed063..c90a8e60df 100644 --- a/src/NuGetGallery/Configuration/AppConfiguration.cs +++ b/src/NuGetGallery/Configuration/AppConfiguration.cs @@ -48,9 +48,6 @@ public class AppConfiguration : IAppConfiguration [DisplayName("AzureStorage.Errors.ConnectionString")] public string AzureStorage_Errors_ConnectionString { get; set; } - [DisplayName("AzureStorage.NuGetExe.ConnectionString")] - public string AzureStorage_NuGetExe_ConnectionString { get; set; } - [DisplayName("AzureStorage.Packages.ConnectionString")] public string AzureStorage_Packages_ConnectionString { get; set; } diff --git a/src/NuGetGallery/Configuration/IAppConfiguration.cs b/src/NuGetGallery/Configuration/IAppConfiguration.cs index 436cd1e824..ce9f25c636 100644 --- a/src/NuGetGallery/Configuration/IAppConfiguration.cs +++ b/src/NuGetGallery/Configuration/IAppConfiguration.cs @@ -55,11 +55,6 @@ public interface IAppConfiguration : ICoreMessageServiceConfiguration /// string AzureStorage_Errors_ConnectionString { get; set; } - /// - /// The Azure Storage connection string used for downloading nuget.exe. - /// - string AzureStorage_NuGetExe_ConnectionString { get; set; } - /// /// The Azure Storage connection string used for packages, after upload. /// diff --git a/src/NuGetGallery/Constants.cs b/src/NuGetGallery/Constants.cs index c363f4ee3c..f0d74a8b24 100644 --- a/src/NuGetGallery/Constants.cs +++ b/src/NuGetGallery/Constants.cs @@ -7,7 +7,6 @@ namespace NuGetGallery { public static class Constants { - public const string AdminRoleName = "Admins"; public const string AlphabeticSortOrder = "package-title"; public const int DefaultPackageListPageSize = 20; public const string DefaultPackageListSortOrder = "package-download-count"; @@ -18,8 +17,6 @@ public static class Constants public const int ColumnsWideAuthenticationSm = 8; public const int ColumnsWideAuthenticationMd = 6; public const int ColumnsFormMd = 10; - public const int ColumnsModalSm = 3; - public const int ColumnsModalMd = 3; public const int VisibleVersions = 5; @@ -52,7 +49,6 @@ public static class Constants public const string RelevanceSortOrder = "relevance"; public const string Sha1HashAlgorithmId = "SHA1"; - public const string Sha512HashAlgorithmId = "SHA512"; public const string PBKDF2HashAlgorithmId = "PBKDF2"; public const string UploadFileNameTemplate = "{0}{1}"; diff --git a/src/NuGetGallery/Controllers/AccountsController.cs b/src/NuGetGallery/Controllers/AccountsController.cs index b0f54a31f0..5f581ace99 100644 --- a/src/NuGetGallery/Controllers/AccountsController.cs +++ b/src/NuGetGallery/Controllers/AccountsController.cs @@ -84,15 +84,13 @@ public virtual ActionResult ConfirmationRequiredPost(string accountName = null) { return new HttpStatusCodeResult(HttpStatusCode.Forbidden, Strings.Unauthorized); } - - var confirmationUrl = Url.ConfirmEmail(account.Username, account.EmailConfirmationToken, relativeUrl: false); - + var alreadyConfirmed = account.UnconfirmedEmailAddress == null; ConfirmationViewModel model; if (!alreadyConfirmed) { - MessageService.SendNewAccountEmail(new MailAddress(account.UnconfirmedEmailAddress, account.Username), confirmationUrl); + SendNewAccountEmail(account); model = new ConfirmationViewModel(account) { @@ -106,14 +104,16 @@ public virtual ActionResult ConfirmationRequiredPost(string accountName = null) return View(model); } + protected abstract void SendNewAccountEmail(User account); + [UIAuthorize(allowDiscontinuedLogins: true)] - public virtual async Task Confirm(string username, string token) + public virtual async Task Confirm(string accountName, string token) { // We don't want Login to go to this page as a return URL // By having this value present in the dictionary BUT null, we don't put "returnUrl" on the Login link at all ViewData[Constants.ReturnUrlViewDataKey] = null; - var account = GetAccount(username); + var account = GetAccount(accountName); if (account == null || ActionsRequiringPermissions.ManageAccount.CheckPermissions(GetCurrentUser(), account) @@ -236,8 +236,7 @@ public virtual async Task ChangeEmail(TAccountViewModel model) if (account.Confirmed) { - var confirmationUrl = Url.ConfirmEmail(account.Username, account.EmailConfirmationToken, relativeUrl: false); - MessageService.SendEmailChangeConfirmationNotice(new MailAddress(account.UnconfirmedEmailAddress, account.Username), confirmationUrl); + SendEmailChangedConfirmationNotice(account); TempData["Message"] = Messages.EmailUpdatedWithConfirmationRequired; } @@ -249,6 +248,8 @@ public virtual async Task ChangeEmail(TAccountViewModel model) return RedirectToAction(AccountAction); } + protected abstract void SendEmailChangedConfirmationNotice(User account); + [HttpPost] [UIAuthorize] [ValidateAntiForgeryToken] @@ -283,7 +284,7 @@ protected virtual TUser GetAccount(string accountName) protected virtual ActionResult AccountView(TUser account, TAccountViewModel model = null) { if (account == null - || ActionsRequiringPermissions.ManageAccount.CheckPermissions(GetCurrentUser(), account) + || ActionsRequiringPermissions.ViewAccount.CheckPermissions(GetCurrentUser(), account) != PermissionsCheckResult.Allowed) { return new HttpStatusCodeResult(HttpStatusCode.Forbidden, Strings.Unauthorized); @@ -301,6 +302,9 @@ protected virtual void UpdateAccountViewModel(TUser account, TAccountViewModel m model.Account = account; model.AccountName = account.Username; + model.CanManage = ActionsRequiringPermissions.ManageAccount.CheckPermissions( + GetCurrentUser(), account) == PermissionsCheckResult.Allowed; + model.CuratedFeeds = CuratedFeedService .GetFeedsForManager(account.Key) .Select(f => f.Name) diff --git a/src/NuGetGallery/Controllers/ApiController.cs b/src/NuGetGallery/Controllers/ApiController.cs index 71fc39b21c..9bc7c915b8 100644 --- a/src/NuGetGallery/Controllers/ApiController.cs +++ b/src/NuGetGallery/Controllers/ApiController.cs @@ -35,7 +35,6 @@ public partial class ApiController public IApiScopeEvaluator ApiScopeEvaluator { get; set; } public IEntitiesContext EntitiesContext { get; set; } - public INuGetExeDownloaderService NugetExeDownloaderService { get; set; } public IPackageFileService PackageFileService { get; set; } public IPackageService PackageService { get; set; } public IUserService UserService { get; set; } @@ -66,7 +65,6 @@ public ApiController( IPackageService packageService, IPackageFileService packageFileService, IUserService userService, - INuGetExeDownloaderService nugetExeDownloaderService, IContentService contentService, IIndexingService indexingService, ISearchService searchService, @@ -87,7 +85,6 @@ public ApiController( PackageService = packageService; PackageFileService = packageFileService; UserService = userService; - NugetExeDownloaderService = nugetExeDownloaderService; ContentService = contentService; IndexingService = indexingService; SearchService = searchService; @@ -111,7 +108,6 @@ public ApiController( IPackageService packageService, IPackageFileService packageFileService, IUserService userService, - INuGetExeDownloaderService nugetExeDownloaderService, IContentService contentService, IIndexingService indexingService, ISearchService searchService, @@ -127,7 +123,7 @@ public ApiController( ISecurityPolicyService securityPolicies, IReservedNamespaceService reservedNamespaceService, IPackageUploadService packageUploadService) - : this(apiScopeEvaluator, entitiesContext, packageService, packageFileService, userService, nugetExeDownloaderService, contentService, + : this(apiScopeEvaluator, entitiesContext, packageService, packageFileService, userService, contentService, indexingService, searchService, autoCuratePackage, statusService, messageService, auditingService, configurationService, telemetryService, authenticationService, credentialBuilder, securityPolicies, reservedNamespaceService, packageUploadService) @@ -296,7 +292,7 @@ private async Task VerifyPackageKeyInternalAsync(U // Write an audit record await AuditingService.SaveAuditRecordAsync( new PackageAuditRecord(package, AuditedPackageAction.Verify)); - + string[] requestedActions; if (CredentialTypes.IsPackageVerificationApiKey(credential.Type)) { @@ -465,7 +461,7 @@ await AuditingService.SaveAuditRecordAsync( var packageStreamMetadata = new PackageStreamMetadata { - HashAlgorithm = Constants.Sha512HashAlgorithmId, + HashAlgorithm = CoreConstants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(packageStream.AsSeekableStream()), Size = packageStream.Length }; @@ -686,6 +682,22 @@ public virtual async Task GetPackageVersions( }; } + [HttpGet] + [ActionName("Query")] + public virtual async Task Query(string q) + { + var queryFilter = new SearchFilter(SearchFilter.ODataSearchContext); + queryFilter.SemVerLevel = SemVerLevelKey.SemVerLevel2; + queryFilter.SearchTerm = q; + var results = await SearchService.Search(queryFilter); + + return new JsonResult + { + Data = results, + JsonRequestBehavior = JsonRequestBehavior.AllowGet + }; + } + [HttpGet] [ActionName("StatisticsDownloadsApi")] public virtual async Task GetStatsDownloads(int? count) diff --git a/src/NuGetGallery/Controllers/AuthenticationController.cs b/src/NuGetGallery/Controllers/AuthenticationController.cs index 9805fa3437..b5a4fdd151 100644 --- a/src/NuGetGallery/Controllers/AuthenticationController.cs +++ b/src/NuGetGallery/Controllers/AuthenticationController.cs @@ -166,7 +166,7 @@ public virtual async Task SignIn(LogOnViewModel model, string retu if (linkingAccount) { // Verify account has no other external accounts - if (authenticatedUser.User.Credentials.Any(c => c.IsExternal()) && !authenticatedUser.User.IsAdministrator()) + if (authenticatedUser.User.Credentials.Any(c => c.IsExternal()) && !authenticatedUser.User.IsAdministrator) { var message = string.Format( CultureInfo.CurrentCulture, @@ -208,7 +208,7 @@ internal bool ShouldChallengeEnforcedProvider(string enforcedProviders, Authenti { if (!string.IsNullOrEmpty(enforcedProviders) && authenticatedUser.CredentialUsed.Type != null - && authenticatedUser.User.IsInRole(Constants.AdminRoleName)) + && authenticatedUser.User.IsAdministrator) { // Seems we *need* a specific authentication provider. Check if we logged in using one... var providers = enforcedProviders.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); @@ -347,7 +347,15 @@ public virtual ActionResult AuthenticatePost(string returnUrl, string provider) [HttpGet] public virtual ActionResult AuthenticateExternal(string returnUrl) { - string externalAuthProvider = GetExternalProvider(); + var user = GetCurrentUser(); + var aadCredential = user?.Credentials.GetAzureActiveDirectoryCredential(); + if (aadCredential != null) + { + TempData["WarningMessage"] = Strings.ChangeCredential_NotAllowed; + return Redirect(returnUrl); + } + + var externalAuthProvider = GetExternalProvider(); if (externalAuthProvider == null) { TempData["Message"] = Strings.ChangeCredential_ProviderNotFound; @@ -482,7 +490,7 @@ public virtual async Task LinkExternalAccount(string returnUrl) { existingUserLinkingError = AssociateExternalAccountViewModel.ExistingUserLinkingErrorType.AccountIsOrganization; } - else if (existingUser.Credentials.Any(c => c.IsExternal()) && !existingUser.IsAdministrator()) + else if (existingUser.Credentials.Any(c => c.IsExternal()) && !existingUser.IsAdministrator) { existingUserLinkingError = AssociateExternalAccountViewModel.ExistingUserLinkingErrorType.AccountIsAlreadyLinked; } diff --git a/src/NuGetGallery/Controllers/OrganizationsController.cs b/src/NuGetGallery/Controllers/OrganizationsController.cs index 914ff58dd6..b0a7a418d3 100644 --- a/src/NuGetGallery/Controllers/OrganizationsController.cs +++ b/src/NuGetGallery/Controllers/OrganizationsController.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Net; +using System.Net.Mail; using System.Threading.Tasks; using System.Web.Mvc; using NuGetGallery.Authentication; @@ -33,6 +34,19 @@ public OrganizationsController( EmailUpdatedWithConfirmationRequired = Strings.OrganizationEmailUpdatedWithConfirmationRequired }; + protected override void SendNewAccountEmail(User account) + { + var confirmationUrl = Url.ConfirmOrganizationEmail(account.Username, account.EmailConfirmationToken, relativeUrl: false); + + MessageService.SendNewAccountEmail(new MailAddress(account.UnconfirmedEmailAddress, account.Username), confirmationUrl); + } + + protected override void SendEmailChangedConfirmationNotice(User account) + { + var confirmationUrl = Url.ConfirmOrganizationEmail(account.Username, account.EmailConfirmationToken, relativeUrl: false); + MessageService.SendEmailChangeConfirmationNotice(new MailAddress(account.UnconfirmedEmailAddress, account.Username), confirmationUrl); + } + [HttpGet] [UIAuthorize] public virtual ActionResult ManageOrganization(string accountName) diff --git a/src/NuGetGallery/Controllers/PackagesController.cs b/src/NuGetGallery/Controllers/PackagesController.cs index 6f46b59afb..9391ac7f7d 100644 --- a/src/NuGetGallery/Controllers/PackagesController.cs +++ b/src/NuGetGallery/Controllers/PackagesController.cs @@ -1561,7 +1561,7 @@ public virtual async Task VerifyPackage(VerifyPackageRequest formDat var packageStreamMetadata = new PackageStreamMetadata { - HashAlgorithm = Constants.Sha512HashAlgorithmId, + HashAlgorithm = CoreConstants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(uploadFile.AsSeekableStream()), Size = uploadFile.Length, }; diff --git a/src/NuGetGallery/Controllers/UsersController.cs b/src/NuGetGallery/Controllers/UsersController.cs index be05fcde87..8826bc88ef 100644 --- a/src/NuGetGallery/Controllers/UsersController.cs +++ b/src/NuGetGallery/Controllers/UsersController.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Linq; using System.Net; +using System.Net.Mail; using System.Threading.Tasks; using System.Web.Mvc; using NuGetGallery.Areas.Admin; @@ -60,6 +61,19 @@ public UsersController( EmailUpdatedWithConfirmationRequired = Strings.UserEmailUpdatedWithConfirmationRequired }; + protected override void SendNewAccountEmail(User account) + { + var confirmationUrl = Url.ConfirmEmail(account.Username, account.EmailConfirmationToken, relativeUrl: false); + + MessageService.SendNewAccountEmail(new MailAddress(account.UnconfirmedEmailAddress, account.Username), confirmationUrl); + } + + protected override void SendEmailChangedConfirmationNotice(User account) + { + var confirmationUrl = Url.ConfirmEmail(account.Username, account.EmailConfirmationToken, relativeUrl: false); + MessageService.SendEmailChangeConfirmationNotice(new MailAddress(account.UnconfirmedEmailAddress, account.Username), confirmationUrl); + } + protected override User GetAccount(string accountName) { var currentUser = GetCurrentUser(); @@ -596,15 +610,6 @@ public virtual async Task RemoveCredential(string credentialType, [ValidateAntiForgeryToken] public virtual ActionResult LinkOrChangeExternalCredential() { - var user = GetCurrentUser(); - var userHasAADCredential = user.Credentials.Any(c => CredentialTypes.IsAzureActiveDirectoryAccount(c.Type)); - - if (userHasAADCredential) - { - TempData["WarningMessage"] = Strings.ChangeCredential_NotAllowed; - return RedirectToAction("Account"); - } - return Redirect(Url.AuthenticateExternal(Url.AccountSettings())); } diff --git a/src/NuGetGallery/Extensions/PrincipalExtensions.cs b/src/NuGetGallery/Extensions/PrincipalExtensions.cs index 699c42299e..75e7ecc1e6 100644 --- a/src/NuGetGallery/Extensions/PrincipalExtensions.cs +++ b/src/NuGetGallery/Extensions/PrincipalExtensions.cs @@ -78,7 +78,7 @@ public static bool IsAdministrator(this IPrincipal self) return false; } - return self.Identity.IsAuthenticated && self.IsInRole(Constants.AdminRoleName); + return self.Identity.IsAuthenticated && self.IsInRole(CoreConstants.AdminRoleName); } /// diff --git a/src/NuGetGallery/Extensions/UserExtensions.cs b/src/NuGetGallery/Extensions/UserExtensions.cs index 8e13b01628..12ac3b5095 100644 --- a/src/NuGetGallery/Extensions/UserExtensions.cs +++ b/src/NuGetGallery/Extensions/UserExtensions.cs @@ -14,11 +14,6 @@ namespace NuGetGallery /// public static class UserExtensions { - public static bool IsAdministrator(this User self) - { - return self.IsInRole(Constants.AdminRoleName); - } - /// /// Get the user's with a type of . /// diff --git a/src/NuGetGallery/Helpers/PermissionsHelpers.cs b/src/NuGetGallery/Helpers/PermissionsHelpers.cs index d0d66faa3c..fc3f713a64 100644 --- a/src/NuGetGallery/Helpers/PermissionsHelpers.cs +++ b/src/NuGetGallery/Helpers/PermissionsHelpers.cs @@ -89,7 +89,7 @@ public static bool IsRequirementSatisfied(PermissionsRequirement permissionsRequ return IsRequirementSatisfied( permissionsRequirement, - currentUser.IsAdministrator(), + currentUser.IsAdministrator, u => currentUser.MatchesUser(u), entityOwners); } @@ -114,20 +114,19 @@ private static bool IsRequirementSatisfied(PermissionsRequirement permissionsReq return true; } - var matchingMembers = entityOwners - .OfType() - .SelectMany(o => o.Members) - .Where(m => isUserMatch(m.Member)) - .ToList(); + var entityOrganizationOwners = entityOwners + .OfType(); + // use cached Administrators collection to avoid querying members directly if (WouldSatisfy(PermissionsRequirement.OrganizationAdmin, permissionsRequirement) && - matchingMembers.Any(m => m.IsAdmin)) + entityOrganizationOwners.Any(o => o.Administrators.Any(m => isUserMatch(m)))) { return true; } + // use cached Collaborators collection to avoid querying members directly if (WouldSatisfy(PermissionsRequirement.OrganizationCollaborator, permissionsRequirement) && - matchingMembers.Any(m => !m.IsAdmin)) + entityOrganizationOwners.Any(o => o.Collaborators.Any(m => isUserMatch(m)))) { return true; } diff --git a/src/NuGetGallery/Migrations/MigrationsConfiguration.cs b/src/NuGetGallery/Migrations/MigrationsConfiguration.cs index 0bb16e3407..338f1f1983 100644 --- a/src/NuGetGallery/Migrations/MigrationsConfiguration.cs +++ b/src/NuGetGallery/Migrations/MigrationsConfiguration.cs @@ -16,9 +16,9 @@ public MigrationsConfiguration() protected override void Seed(EntitiesContext context) { var roles = context.Set(); - if (!roles.Any(x => x.Name == Constants.AdminRoleName)) + if (!roles.Any(x => x.Name == CoreConstants.AdminRoleName)) { - roles.Add(new Role { Name = Constants.AdminRoleName }); + roles.Add(new Role { Name = CoreConstants.AdminRoleName }); context.SaveChanges(); } diff --git a/src/NuGetGallery/NuGetGallery.csproj b/src/NuGetGallery/NuGetGallery.csproj index 926cba7385..e28afd075e 100644 --- a/src/NuGetGallery/NuGetGallery.csproj +++ b/src/NuGetGallery/NuGetGallery.csproj @@ -1638,8 +1638,8 @@ - - + + @@ -1802,12 +1802,10 @@ - - @@ -1866,7 +1864,6 @@ - @@ -2056,6 +2053,7 @@ + @@ -2177,7 +2175,7 @@ - + diff --git a/src/NuGetGallery/Scripts/gallery/autocomplete.js b/src/NuGetGallery/Scripts/gallery/autocomplete.js new file mode 100644 index 0000000000..ebc30d1650 --- /dev/null +++ b/src/NuGetGallery/Scripts/gallery/autocomplete.js @@ -0,0 +1,198 @@ +$(function () { + 'use strict'; + var _autocompleteTimeout = 0; + var _autocompleteDelay = 100; // time to wait between keypresses before starting autocomplete requests + var _resultsCache = {}; + var _maxResults = 9; + var _lastIndex = 0; + var _keyCodes = { + tab: 9, + esc: 27, + upArrow: 38, + downArrow: 40 + }; + + function hookAutocomplete(maxResults) { + _resultsCache.results = ko.observable(); + $(document).keydown(function (e) { + if (e.keyCode === _keyCodes.esc) { + removeOldAutocompleteResults(); + e.stopPropagation(); + } + }); + + $("#autocomplete-results-container").keydown(function (e) { + if (e.keyCode === _keyCodes.downArrow) { + if (_lastIndex < $("#autocomplete-results-list").children().length - 1) { + _lastIndex++; + + $("#autocomplete-results-list").children()[_lastIndex].focus(); + } else if (_lastIndex == $("#autocomplete-results-list").children().length - 1) { + $("#search").focus(); + _lastIndex = 0; + } + e.preventDefault(); + } + else if (e.keyCode === _keyCodes.upArrow) { + if (_lastIndex > 0) { + _lastIndex--; + $("#autocomplete-results-list").children()[_lastIndex].focus(); + } else if (_lastIndex == 0) { + $("#search").focus(); + } + + e.preventDefault(); + } + else if (e.keyCode === _keyCodes.tab) { + removeOldAutocompleteResults(); + } + }); + + $("#autocomplete-results-container").focusin(function (e) { + $("#autocomplete-results-list").children()[_lastIndex].focus(); + }); + + var searchBox = $("#search"); + searchBox.on("keyup", function (e) { + clearTimeout(_autocompleteTimeout); + if (e.keyCode === _keyCodes.esc || $(this).val().length < 1) { + removeOldAutocompleteResults(); + e.stopPropagation();; + return; + } + + if ((e.keyCode >= 46 && e.keyCode <= 90) //delete, 0-9, a-z + || (e.keyCode >= 96 && e.keyCode <= 111) //numpad + || (e.keyCode >= 186) //punctuation + || (e.keyCode == 8)) //backspace + { + _autocompleteTimeout = setTimeout(doSearch.bind(this, maxResults), _autocompleteDelay); + } + }); + + searchBox.keydown(function (e) { + if (e.keyCode == _keyCodes.downArrow) { + $("#autocomplete-results-container").focus(); + e.preventDefault(); + } + else if (e.keyCode == _keyCodes.upArrow && _lastIndex == 0) { + _lastIndex = $("#autocomplete-results-list").children().length - 1; + $("#autocomplete-results-container").focus(); + e.preventDefault(); + } + else if (e.keyCode === _keyCodes.tab) { + removeOldAutocompleteResults(); + } + }) + } + + function removeOldAutocompleteResults() { + var oldBox = $("#autocomplete-results"); + oldBox.remove(); + $("#autocomplete-results-container").hide(); + + _lastIndex = 0; + _autocompleteTimeout = 0; + } + + function doSearch(maxResults) { + var currInput = $("#search").val(); + if (currInput.length < 1) { + removeOldAutocompleteResults(); + return; + } + + var requestUrl = "/api/v2/package-ids?partialId=" + currInput + "&semVerLevel=2.0.0"; + $.ajax({ + url: requestUrl, + method: "GET", + success: function (data, status) { + if (data.length < 1) { + removeOldAutocompleteResults(); + return; + } + + $("#autocomplete-results-container").show(); + + _resultsCache.results({ data: data.slice(0, maxResults) }); + + var container = $("#autocomplete-results"); + if (container.length < 1) { + container = document.createElement("div"); + $(container).attr("id", "autocomplete-results"); + $(container).attr("data-bind", "template: { name: 'autocomplete-results-template', data: results }"); + $("#autocomplete-results-container").append(container); + + ko.applyBindings(_resultsCache, container); + } + + for (var i = 0; i < data.length; i++) { + var id = data[i]; + var temp = _resultsCache[safeId(id)]; + if (!temp) { + _resultsCache[safeId(id)] = ko.observable(id); + } + } + + setupAllAuxData(data.slice(0, maxResults)); + } + }); + } + + function setupAllAuxData(idList) { + var requestUrl = "/api/v2/query?q="; + for (var i = 0; i < idList.length; i++) { + var tempId = idList[i]; + var searchData = _resultsCache[safeId(tempId)]; + if (typeof searchData() == "string") { + requestUrl += "packageid:" + idList[i] + " "; + } + + appendAuxData(idList[i]); + } + + + $.ajax({ + url: requestUrl, + method: "GET", + success: function (data, status) { + var dataList = data.Data; + for (var i = 0; i < dataList.length; i++) { + var dataBlock = dataList[i]; + dataBlock.OwnersString = dataBlock.PackageRegistration.Owners.reduce(function (out, input) { return out + " " + input.Username; }, ""); + var someId = dataBlock.PackageRegistration.Id; + + if (_resultsCache[safeId(someId)]) { + _resultsCache[safeId(someId)](dataBlock); + } else { + _resultsCache[safeId(someId)] = ko.observable(dataBlock); + } + } + } + }); + } + + function appendAuxData(id) { + var testNotExist = $("#autocomplete-results-row-" + jquerySafeId(id)).length < 1; + + if (testNotExist) { + var container = document.createElement("div"); + $(container).attr("id", "autocomplete-results-row-" + id); + $(container).attr("data-bind", "template: { name: 'autocomplete-results-row', data: " + safeId(id) + " }"); + var parent = $("#autocomplete-container-" + jquerySafeId(id)); + parent.append(container); + + ko.applyBindings(_resultsCache, container); + } + } + + function jquerySafeId(id) { + return id.replace(/\./g, "\\."); + } + + function safeId(id) { + return id.replace(/(\.|-)/g, ""); + } + + hookAutocomplete(_maxResults); +}); \ No newline at end of file diff --git a/src/NuGetGallery/Scripts/gallery/page-manage-organization.js b/src/NuGetGallery/Scripts/gallery/page-manage-organization.js index 11a0af64bd..33274555b0 100644 --- a/src/NuGetGallery/Scripts/gallery/page-manage-organization.js +++ b/src/NuGetGallery/Scripts/gallery/page-manage-organization.js @@ -77,6 +77,9 @@ success: function () { parent.Error(null); parent.Members.remove(self); + if (self.IsCurrentUser) { + document.location.href = "/"; + } }, error: function (jqXHR, textStatus, errorThrown) { var error = "Unknown error when trying to delete member '" + self.Username + "'."; @@ -93,7 +96,7 @@ var data = { accountName: self.OrganizationViewModel.AccountName, memberName: self.Username, - isAdmin: self.IsAdmin() + isAdmin: self.IsAdmin(), }; addAntiForgeryToken(data); @@ -103,9 +106,12 @@ type: 'POST', dataType: 'json', data: data, - success: function () { + success: function (data) { parent.Error(null); parent.UpdateMemberCounts(); + if (self.IsCurrentUser) { + window.location.reload(); + } }, error: function (jqXHR, textStatus, errorThrown) { var error = "Unknown error when trying to update member '" + self.Username + "'."; @@ -113,6 +119,7 @@ error = jqXHR.responseJSON; } parent.Error(error); + self.IsAdmin(!self.IsAdmin()) } }); }; diff --git a/src/NuGetGallery/Security/RequireOrganizationTenantPolicy.cs b/src/NuGetGallery/Security/RequireOrganizationTenantPolicy.cs index 5de7a7f203..f3d5349964 100644 --- a/src/NuGetGallery/Security/RequireOrganizationTenantPolicy.cs +++ b/src/NuGetGallery/Security/RequireOrganizationTenantPolicy.cs @@ -75,7 +75,7 @@ public override SecurityPolicyResult Evaluate(UserSecurityPolicyEvaluationContex var targetCredential = targetAccount.Credentials.GetAzureActiveDirectoryCredential(); if (targetCredential == null - || !targetCredential.TenantId.Equals(state.Tenant, StringComparison.OrdinalIgnoreCase)) + || !state.Tenant.Equals(targetCredential.TenantId, StringComparison.OrdinalIgnoreCase)) { return SecurityPolicyResult.CreateErrorResult(string.Format(CultureInfo.CurrentCulture, Strings.AddMember_UserDoesNotMeetOrganizationPolicy, targetAccount.Username)); diff --git a/src/NuGetGallery/Services/ActionsRequiringPermissions.cs b/src/NuGetGallery/Services/ActionsRequiringPermissions.cs index e67fda9e2e..e36bf46b83 100644 --- a/src/NuGetGallery/Services/ActionsRequiringPermissions.cs +++ b/src/NuGetGallery/Services/ActionsRequiringPermissions.cs @@ -94,5 +94,12 @@ public static class ActionsRequiringPermissions public static ActionRequiringAccountPermissions ManageAccount = new ActionRequiringAccountPermissions( accountPermissionsRequirement: RequireOwnerOrOrganizationAdmin); + + /// + /// The action of viewing (read-only) a user or organization account. + /// + public static ActionRequiringAccountPermissions ViewAccount = + new ActionRequiringAccountPermissions( + accountPermissionsRequirement: RequireOwnerOrOrganizationMember); } } \ No newline at end of file diff --git a/src/NuGetGallery/Services/ContentObjectService.cs b/src/NuGetGallery/Services/ContentObjectService.cs index 934fcc287e..6fb5605098 100644 --- a/src/NuGetGallery/Services/ContentObjectService.cs +++ b/src/NuGetGallery/Services/ContentObjectService.cs @@ -28,13 +28,14 @@ public ContentObjectService(IContentService contentService) public async Task Refresh() { LoginDiscontinuationConfiguration = - await Refresh(Constants.ContentNames.LoginDiscontinuationConfiguration); + await Refresh(Constants.ContentNames.LoginDiscontinuationConfiguration) ?? + new LoginDiscontinuationConfiguration(); } private async Task Refresh(string contentName) where T : class { - var configString = (await _contentService.GetContentItemAsync(contentName, TimeSpan.FromHours(RefreshIntervalHours))).ToString(); + var configString = (await _contentService.GetContentItemAsync(contentName, TimeSpan.FromHours(RefreshIntervalHours)))?.ToString(); if (string.IsNullOrEmpty(configString)) { return null; diff --git a/src/NuGetGallery/Services/INuGetExeDownloaderService.cs b/src/NuGetGallery/Services/INuGetExeDownloaderService.cs deleted file mode 100644 index effd44afbf..0000000000 --- a/src/NuGetGallery/Services/INuGetExeDownloaderService.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Threading.Tasks; -using System.Web.Mvc; - -namespace NuGetGallery -{ - public interface INuGetExeDownloaderService - { - Task CreateNuGetExeDownloadActionResultAsync(Uri requestUrl); - } -} \ No newline at end of file diff --git a/src/NuGetGallery/Services/IPackageService.cs b/src/NuGetGallery/Services/IPackageService.cs index 154bbf1e11..3d2562c71a 100644 --- a/src/NuGetGallery/Services/IPackageService.cs +++ b/src/NuGetGallery/Services/IPackageService.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using NuGet.Packaging; using NuGetGallery.Packaging; @@ -34,7 +35,7 @@ public interface IPackageService : ICorePackageService IEnumerable FindPackagesByAnyMatchingOwner(User user, bool includeUnlisted, bool includeVersions = false); - IEnumerable FindPackageRegistrationsByOwner(User user); + IQueryable FindPackageRegistrationsByOwner(User user); IEnumerable FindDependentPackages(Package package); diff --git a/src/NuGetGallery/Services/LoginDiscontinuationConfiguration.cs b/src/NuGetGallery/Services/LoginDiscontinuationConfiguration.cs index 631b112d7d..e5c4a8bd23 100644 --- a/src/NuGetGallery/Services/LoginDiscontinuationConfiguration.cs +++ b/src/NuGetGallery/Services/LoginDiscontinuationConfiguration.cs @@ -15,6 +15,11 @@ public class LoginDiscontinuationConfiguration : ILoginDiscontinuationConfigurat public HashSet DiscontinuedForDomains { get; } public HashSet ExceptionsForEmailAddresses { get; } + public LoginDiscontinuationConfiguration() + : this(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()) + { + } + [JsonConstructor] public LoginDiscontinuationConfiguration( IEnumerable discontinuedForEmailAddresses, diff --git a/src/NuGetGallery/Services/NuGetExeDownloaderService.cs b/src/NuGetGallery/Services/NuGetExeDownloaderService.cs deleted file mode 100644 index 11a15a54f5..0000000000 --- a/src/NuGetGallery/Services/NuGetExeDownloaderService.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using System.Web.Mvc; -using NuGetGallery.Packaging; - -namespace NuGetGallery -{ - public class NuGetExeDownloaderService : INuGetExeDownloaderService - { - private const int MaxNuGetExeFileSize = 10 * 1024 * 1024; - private readonly IFileStorageService _fileStorageService; - - public NuGetExeDownloaderService( - IFileStorageService fileStorageService) - { - _fileStorageService = fileStorageService; - } - - public Task CreateNuGetExeDownloadActionResultAsync(Uri requestUrl) - { - return _fileStorageService.CreateDownloadFileActionResultAsync(requestUrl, CoreConstants.DownloadsFolderName, "nuget.exe"); - } - } -} diff --git a/src/NuGetGallery/Services/PackageOwnershipManagementService.cs b/src/NuGetGallery/Services/PackageOwnershipManagementService.cs index 96bb36cf92..9a83541993 100644 --- a/src/NuGetGallery/Services/PackageOwnershipManagementService.cs +++ b/src/NuGetGallery/Services/PackageOwnershipManagementService.cs @@ -198,7 +198,7 @@ public async Task DeletePackageOwnershipRequestAsync(PackageRegistration package // 3. Or the other user also does not own a namespace. private static bool OwnerHasPermissionsToRemove(User requestingOwner, User ownerToBeRemoved, PackageRegistration packageRegistration) { - if (requestingOwner.IsInRole(Constants.AdminRoleName)) + if (requestingOwner.IsAdministrator) { return true; } diff --git a/src/NuGetGallery/Services/PackageService.cs b/src/NuGetGallery/Services/PackageService.cs index 97fd3ea968..4b992a452a 100644 --- a/src/NuGetGallery/Services/PackageService.cs +++ b/src/NuGetGallery/Services/PackageService.cs @@ -318,7 +318,7 @@ public IEnumerable GetPackageUserAccountOwners(Package package) .Distinct(); } - public IEnumerable FindPackageRegistrationsByOwner(User user) + public IQueryable FindPackageRegistrationsByOwner(User user) { return _packageRegistrationRepository.GetAll().Where(p => p.Owners.Any(o => o.Key == user.Key)); } diff --git a/src/NuGetGallery/Services/ReflowPackageService.cs b/src/NuGetGallery/Services/ReflowPackageService.cs index faa050509f..87c3113aab 100644 --- a/src/NuGetGallery/Services/ReflowPackageService.cs +++ b/src/NuGetGallery/Services/ReflowPackageService.cs @@ -45,7 +45,7 @@ public async Task ReflowAsync(string id, string version) // 2) Determine package metadata from binary var packageStreamMetadata = new PackageStreamMetadata { - HashAlgorithm = Constants.Sha512HashAlgorithmId, + HashAlgorithm = CoreConstants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(packageStream.AsSeekableStream()), Size = packageStream.Length, }; diff --git a/src/NuGetGallery/Services/UserService.cs b/src/NuGetGallery/Services/UserService.cs index 20c31843f7..97a024afe1 100644 --- a/src/NuGetGallery/Services/UserService.cs +++ b/src/NuGetGallery/Services/UserService.cs @@ -109,10 +109,9 @@ public async Task UpdateMemberAsync(Organization organization, strin if (membership.IsAdmin != isAdmin) { // block removal of last admin - if (membership.IsAdmin && organization.Members.Count(m => m.IsAdmin) == 1) + if (membership.IsAdmin && organization.Administrators.Count() == 1) { - throw new EntityException(string.Format(CultureInfo.CurrentCulture, - Strings.UpdateOrDeleteMember_CannotRemoveLastAdmin, memberName)); + throw new EntityException(Strings.UpdateMember_CannotRemoveLastAdmin); } membership.IsAdmin = isAdmin; @@ -134,10 +133,9 @@ public async Task DeleteMemberAsync(Organization organization, string memberName } // block removal of last admin - if (membership.IsAdmin && organization.Members.Count(m => m.IsAdmin) == 1) + if (membership.IsAdmin && organization.Administrators.Count() == 1) { - throw new EntityException(string.Format(CultureInfo.CurrentCulture, - Strings.UpdateOrDeleteMember_CannotRemoveLastAdmin, memberName)); + throw new EntityException(Strings.DeleteMember_CannotRemoveLastAdmin); } organization.Members.Remove(membership); diff --git a/src/NuGetGallery/Strings.Designer.cs b/src/NuGetGallery/Strings.Designer.cs index 92a73f3ad2..b88108c7b4 100644 --- a/src/NuGetGallery/Strings.Designer.cs +++ b/src/NuGetGallery/Strings.Designer.cs @@ -622,6 +622,15 @@ public static string DeleteMember_CannotDeleteLastAdmin { } } + /// + /// Looks up a localized string similar to You can't leave the organization. In order to leave the organization, another collaborator must be assigned as an administrator.. + /// + public static string DeleteMember_CannotRemoveLastAdmin { + get { + return ResourceManager.GetString("DeleteMember_CannotRemoveLastAdmin", resourceCulture); + } + } + /// /// Looks up a localized string similar to Organization member was deleted.. /// @@ -1701,11 +1710,11 @@ public static string Unsupported { } /// - /// Looks up a localized string similar to Organization member '{0}' is the last admin and cannot be changed.. + /// Looks up a localized string similar to You can't change your role to collaborator. In order to change, another collaborator must be assigned as an administrator.. /// - public static string UpdateOrDeleteMember_CannotRemoveLastAdmin { + public static string UpdateMember_CannotRemoveLastAdmin { get { - return ResourceManager.GetString("UpdateOrDeleteMember_CannotRemoveLastAdmin", resourceCulture); + return ResourceManager.GetString("UpdateMember_CannotRemoveLastAdmin", resourceCulture); } } diff --git a/src/NuGetGallery/Strings.resx b/src/NuGetGallery/Strings.resx index 814c753540..d6007d4301 100644 --- a/src/NuGetGallery/Strings.resx +++ b/src/NuGetGallery/Strings.resx @@ -786,8 +786,8 @@ If you wish to update the linked Microsoft account you can do so from the accoun Organization member '{0}' is the last admin and cannot be deleted. - - Organization member '{0}' is the last admin and cannot be changed. + + You can't change your role to collaborator. In order to change, another collaborator must be assigned as an administrator. Organization member '{0}' was not found. @@ -816,4 +816,7 @@ If you wish to update the linked Microsoft account you can do so from the accoun Administrator account '{0}' is not linked to an AAD credential with an organization tenant. + + You can't leave the organization. In order to leave the organization, another collaborator must be assigned as an administrator. + \ No newline at end of file diff --git a/src/NuGetGallery/UrlExtensions.cs b/src/NuGetGallery/UrlExtensions.cs index b54aa01293..879422e2a3 100644 --- a/src/NuGetGallery/UrlExtensions.cs +++ b/src/NuGetGallery/UrlExtensions.cs @@ -924,13 +924,28 @@ public static string ConfirmEmail( { var routeValues = new RouteValueDictionary { - ["username"] = username, + ["accountName"] = username, ["token"] = token }; return GetActionLink(url, "Confirm", "Users", relativeUrl, routeValues); } + public static string ConfirmOrganizationEmail( + this UrlHelper url, + string username, + string token, + bool relativeUrl = true) + { + var routeValues = new RouteValueDictionary + { + ["accountName"] = username, + ["token"] = token + }; + + return GetActionLink(url, "Confirm", "Organizations", relativeUrl, routeValues); + } + public static string ResetEmailOrPassword( this UrlHelper url, string username, @@ -1033,7 +1048,8 @@ public static string Authenticate(this UrlHelper url, string providerName, strin { { "provider", providerName }, { "returnUrl", returnUrl } - }); + }, + interceptReturnUrl: false); } public static string RemoveCredential(this UrlHelper url, bool relativeUrl = true) diff --git a/src/NuGetGallery/ViewModels/AccountViewModel.cs b/src/NuGetGallery/ViewModels/AccountViewModel.cs index 682faddd84..d5d5ac31ce 100644 --- a/src/NuGetGallery/ViewModels/AccountViewModel.cs +++ b/src/NuGetGallery/ViewModels/AccountViewModel.cs @@ -19,6 +19,8 @@ public bool IsOrganization public string AccountName { get; set; } + public bool CanManage { get; set; } + public IList CuratedFeeds { get; set; } public ChangeEmailViewModel ChangeEmail { get; set; } diff --git a/src/NuGetGallery/ViewModels/ListOrganizationsItemViewModel.cs b/src/NuGetGallery/ViewModels/ManageOrganizationsItemViewModel.cs similarity index 100% rename from src/NuGetGallery/ViewModels/ListOrganizationsItemViewModel.cs rename to src/NuGetGallery/ViewModels/ManageOrganizationsItemViewModel.cs diff --git a/src/NuGetGallery/ViewModels/ListOrganizationsViewModel.cs b/src/NuGetGallery/ViewModels/ManageOrganizationsViewModel.cs similarity index 100% rename from src/NuGetGallery/ViewModels/ListOrganizationsViewModel.cs rename to src/NuGetGallery/ViewModels/ManageOrganizationsViewModel.cs diff --git a/src/NuGetGallery/ViewModels/ManagePackageOwnersViewModel.cs b/src/NuGetGallery/ViewModels/ManagePackageOwnersViewModel.cs index f6d4ec6cca..002db2bd37 100644 --- a/src/NuGetGallery/ViewModels/ManagePackageOwnersViewModel.cs +++ b/src/NuGetGallery/ViewModels/ManagePackageOwnersViewModel.cs @@ -12,7 +12,7 @@ public class ManagePackageOwnersViewModel : ListPackageItemViewModel public ManagePackageOwnersViewModel(Package package, User currentUser) : base(package, currentUser) { - IsCurrentUserAnAdmin = currentUser.IsAdministrator(); + IsCurrentUserAnAdmin = currentUser.IsAdministrator; } } } \ No newline at end of file diff --git a/src/NuGetGallery/Views/Authentication/LinkExternal.cshtml b/src/NuGetGallery/Views/Authentication/LinkExternal.cshtml index c4dd308d93..aa6b04c789 100644 --- a/src/NuGetGallery/Views/Authentication/LinkExternal.cshtml +++ b/src/NuGetGallery/Views/Authentication/LinkExternal.cshtml @@ -66,7 +66,7 @@ else {

- Looks like we don't have an account with this email address (@Model.SignIn.UserNameOrEmail) in our records. + Looks like we don't have an account with this email address (@Model.Register.EmailAddress) in our records.

Please provide a username so that we can create an account for you! diff --git a/src/NuGetGallery/Views/Organizations/ManageOrganization.cshtml b/src/NuGetGallery/Views/Organizations/ManageOrganization.cshtml index 0029df7d2b..2b8f1eb9ea 100644 --- a/src/NuGetGallery/Views/Organizations/ManageOrganization.cshtml +++ b/src/NuGetGallery/Views/Organizations/ManageOrganization.cshtml @@ -7,7 +7,7 @@ TempData["Parent"] = this; } -

diff --git a/src/NuGetGallery/Web.config b/src/NuGetGallery/Web.config index 6e581180c6..ebd677c464 100644 --- a/src/NuGetGallery/Web.config +++ b/src/NuGetGallery/Web.config @@ -30,7 +30,6 @@ - diff --git a/tests/NuGetGallery.Facts/App_Start/StorageDependentFacts.cs b/tests/NuGetGallery.Facts/App_Start/StorageDependentFacts.cs index 881133246b..ec7af2d451 100644 --- a/tests/NuGetGallery.Facts/App_Start/StorageDependentFacts.cs +++ b/tests/NuGetGallery.Facts/App_Start/StorageDependentFacts.cs @@ -54,12 +54,10 @@ public void StorageDependentsHaveExpectedTypes() // Assert var implementationToInterface = dependents.ToDictionary(x => x.ImplementationType, x => x.InterfaceType); Assert.Contains(typeof(ContentService), implementationToInterface.Keys); - Assert.Contains(typeof(NuGetExeDownloaderService), implementationToInterface.Keys); Assert.Contains(typeof(PackageFileService), implementationToInterface.Keys); Assert.Contains(typeof(UploadFileService), implementationToInterface.Keys); - Assert.Equal(4, implementationToInterface.Count); + Assert.Equal(3, implementationToInterface.Count); Assert.Equal(implementationToInterface[typeof(ContentService)], typeof(IContentService)); - Assert.Equal(implementationToInterface[typeof(NuGetExeDownloaderService)], typeof(INuGetExeDownloaderService)); Assert.Equal(implementationToInterface[typeof(PackageFileService)], typeof(IPackageFileService)); Assert.Equal(implementationToInterface[typeof(UploadFileService)], typeof(IUploadFileService)); } @@ -76,7 +74,6 @@ public void StorageDependentsUseCorrectConnectionString() // Assert var typeToConnectionString = dependents.ToDictionary(x => x.ImplementationType, x => x.AzureStorageConnectionString); Assert.Equal(typeToConnectionString[typeof(ContentService)], config.AzureStorage_Content_ConnectionString); - Assert.Equal(typeToConnectionString[typeof(NuGetExeDownloaderService)], config.AzureStorage_NuGetExe_ConnectionString); Assert.Equal(typeToConnectionString[typeof(PackageFileService)], config.AzureStorage_Packages_ConnectionString); Assert.Equal(typeToConnectionString[typeof(UploadFileService)], config.AzureStorage_Uploads_ConnectionString); } @@ -86,8 +83,7 @@ public void StorageDependentsAreGroupedByConnectionString() { // Arrange var mock = new Mock(); - mock.Setup(x => x.AzureStorage_Content_ConnectionString).Returns("Content and NuGetExe"); - mock.Setup(x => x.AzureStorage_NuGetExe_ConnectionString).Returns("Content and NuGetExe"); + mock.Setup(x => x.AzureStorage_Content_ConnectionString).Returns("Content"); mock.Setup(x => x.AzureStorage_Packages_ConnectionString).Returns("Packages and Uploads"); mock.Setup(x => x.AzureStorage_Uploads_ConnectionString).Returns("Packages and Uploads"); var config = mock.Object; @@ -97,7 +93,6 @@ public void StorageDependentsAreGroupedByConnectionString() // Assert var typeToBindingKey = dependents.ToDictionary(x => x.ImplementationType, x => x.BindingKey); - Assert.Equal(typeToBindingKey[typeof(ContentService)], typeToBindingKey[typeof(NuGetExeDownloaderService)]); Assert.Equal(typeToBindingKey[typeof(PackageFileService)], typeToBindingKey[typeof(UploadFileService)]); } @@ -105,7 +100,6 @@ private static IAppConfiguration GetConfiguration() { var mock = new Mock(); mock.Setup(x => x.AzureStorage_Content_ConnectionString).Returns("Content"); - mock.Setup(x => x.AzureStorage_NuGetExe_ConnectionString).Returns("NuGetExe"); mock.Setup(x => x.AzureStorage_Packages_ConnectionString).Returns("Packages"); mock.Setup(x => x.AzureStorage_Uploads_ConnectionString).Returns("Uploads"); return mock.Object; diff --git a/tests/NuGetGallery.Facts/Controllers/AccountsControllerFacts.cs b/tests/NuGetGallery.Facts/Controllers/AccountsControllerFacts.cs index 55d91238bd..207f2a8aa9 100644 --- a/tests/NuGetGallery.Facts/Controllers/AccountsControllerFacts.cs +++ b/tests/NuGetGallery.Facts/Controllers/AccountsControllerFacts.cs @@ -472,7 +472,9 @@ public virtual void WhenIsNotConfirmed_SendsEmail() account.EmailAddress = null; // Act - var confirmationUrl = TestUtility.GallerySiteRootHttps + $"account/confirm/{account.Username}/confirmation"; + var confirmationUrl = (account is Organization) + ? TestUtility.GallerySiteRootHttps + $"organization/{account.Username}/Confirm?token=confirmation" + : TestUtility.GallerySiteRootHttps + $"account/confirm/{account.Username}/confirmation"; var result = InvokeConfirmationRequiredPost(controller, account, confirmationUrl); // Assert diff --git a/tests/NuGetGallery.Facts/Controllers/ApiControllerFacts.cs b/tests/NuGetGallery.Facts/Controllers/ApiControllerFacts.cs index 7436d8b6fd..dad3a64e66 100644 --- a/tests/NuGetGallery.Facts/Controllers/ApiControllerFacts.cs +++ b/tests/NuGetGallery.Facts/Controllers/ApiControllerFacts.cs @@ -37,7 +37,6 @@ internal class TestableApiController public Mock MockPackageService { get; private set; } public Mock MockPackageFileService { get; private set; } public Mock MockUserService { get; private set; } - public Mock MockNuGetExeDownloaderService { get; private set; } public Mock MockContentService { get; private set; } public Mock MockStatisticsService { get; private set; } public Mock MockIndexingService { get; private set; } @@ -60,7 +59,6 @@ public TestableApiController( EntitiesContext = (MockEntitiesContext = new Mock()).Object; PackageService = (MockPackageService = new Mock(behavior)).Object; UserService = (MockUserService = new Mock(behavior)).Object; - NugetExeDownloaderService = (MockNuGetExeDownloaderService = new Mock(MockBehavior.Strict)).Object; ContentService = (MockContentService = new Mock()).Object; StatisticsService = (MockStatisticsService = new Mock()).Object; IndexingService = (MockIndexingService = new Mock()).Object; diff --git a/tests/NuGetGallery.Facts/Controllers/AuthenticationControllerFacts.cs b/tests/NuGetGallery.Facts/Controllers/AuthenticationControllerFacts.cs index 4107f98de2..2c4c2542dd 100644 --- a/tests/NuGetGallery.Facts/Controllers/AuthenticationControllerFacts.cs +++ b/tests/NuGetGallery.Facts/Controllers/AuthenticationControllerFacts.cs @@ -330,8 +330,14 @@ public async Task WhenAttemptingToLinkExternalToAdminUserWithExistingExternals_A var user = new User("theUsername") { EmailAddress = "confirmed@example.com", - Credentials = new[] { new Credential { Type = CredentialTypes.External.Prefix + "Foo" } }, - Roles = new[] { new Role { Name = Constants.AdminRoleName } } + Credentials = new[] + { + new Credential { Type = CredentialTypes.External.Prefix + "Foo" } + }, + Roles = new[] + { + new Role { Name = CoreConstants.AdminRoleName } + } }; var authUser = new AuthenticatedUser( @@ -482,7 +488,7 @@ public async Task GivenAdminLogsInWithValidExternalAuth_ItChallengesWhenNotUsing UnconfirmedEmailAddress = "confirmed@example.com", Roles = { - new Role { Name = Constants.AdminRoleName } + new Role { Name = CoreConstants.AdminRoleName } } }, externalCred); @@ -798,7 +804,7 @@ public async Task GivenAdminLogsInWithExternalIdentity_ItChallengesWhenNotUsingR EmailConfirmationToken = "t0k3n", Roles = { - new Role { Name = Constants.AdminRoleName } + new Role { Name = CoreConstants.AdminRoleName } } }, externalCred); @@ -988,6 +994,77 @@ public async Task GivenNewCredential_ItSuccessfullyReplacesExternalCredentialsAn } } + public class TheAuthenticateExternalAction : TestContainer + { + [Fact] + public void ForAADLinkedAccount_ErrorIsReturned() + { + var fakes = Get(); + var aadCred = new CredentialBuilder().CreateExternalCredential("AzureActiveDirectory", "blorg", "bloog"); + var passwordCred = new Credential("password.v3", "random"); + var msftCred = new CredentialBuilder().CreateExternalCredential("MicrosoftAccount", "bloom", "filter"); + var user = fakes.CreateUser("test", aadCred, passwordCred, msftCred); + var controller = GetController(); + controller.SetCurrentUser(user); + + // Act + var result = controller.AuthenticateExternal("theReturnUrl"); + + // Assert + ResultAssert.IsRedirectTo(result, "theReturnUrl"); + Assert.Equal(Strings.ChangeCredential_NotAllowed, controller.TempData["WarningMessage"]); + } + + [Fact] + public void ForMissingExternalProvider_ErrorIsReturned() + { + var controller = GetController(); + + // Act + var result = controller.AuthenticateExternal("theReturnUrl"); + + // Assert + ResultAssert.IsRedirectTo(result, "theReturnUrl"); + Assert.Equal(Strings.ChangeCredential_ProviderNotFound, controller.TempData["Message"]); + } + + [Fact] + public void WillCallChallengeAuthenticationForAADv2ProviderForUserWithNoAADCredential() + { + // Arrange + const string returnUrl = "/theReturnUrl"; + EnableAllAuthenticators(Get()); + + var fakes = Get(); + var passwordCred = new Credential("password.v3", "random"); + var msftCred = new CredentialBuilder().CreateExternalCredential("MicrosoftAccount", "bloom", "filter"); + var user = fakes.CreateUser("test", passwordCred, msftCred); + var controller = GetController(); + controller.SetCurrentUser(user); + + // Act + var result = controller.AuthenticateExternal(returnUrl); + + // Assert + ResultAssert.IsChallengeResult(result, "AzureActiveDirectoryV2", controller.Url.LinkOrChangeExternalCredential(returnUrl)); + } + + [Fact] + public void WillCallChallengeAuthenticationForAADv2Provider() + { + // Arrange + const string returnUrl = "/theReturnUrl"; + EnableAllAuthenticators(Get()); + var controller = GetController(); + + // Act + var result = controller.AuthenticateExternal(returnUrl); + + // Assert + ResultAssert.IsChallengeResult(result, "AzureActiveDirectoryV2", controller.Url.LinkOrChangeExternalCredential(returnUrl)); + } + } + public class TheLinkExternalAccountAction : TestContainer { [Fact] @@ -1062,7 +1139,7 @@ public async Task GivenAssociatedLocalAdminUser_ItChallengesWhenNotUsingRequired fakes.CreateUser("test", cred), cred); - authUser.User.Roles.Add(new Role { Name = Constants.AdminRoleName }); + authUser.User.Roles.Add(new Role { Name = CoreConstants.AdminRoleName }); GetMock() .Setup(x => x.AuthenticateExternalLogin(controller.OwinContext)) @@ -1343,8 +1420,14 @@ public async Task GivenNoLinkButEmailMatchingLocalAdminUserWithExistingExternal_ var existingUser = new User("existingUser") { EmailAddress = "existing@example.com", - Credentials = new[] { new Credential(CredentialTypes.External.Prefix + "foo", "externalloginvalue") }, - Roles = new[] { new Role { Name = Constants.AdminRoleName } } + Credentials = new[] + { + new Credential(CredentialTypes.External.Prefix + "foo", "externalloginvalue") + }, + Roles = new[] + { + new Role { Name = CoreConstants.AdminRoleName } + } }; var cred = new CredentialBuilder().CreateExternalCredential("MicrosoftAccount", "blorg", "Bloog"); @@ -1404,7 +1487,7 @@ public void VerifyShouldChallenge(string providerUsedForLogin, bool shouldChalle EmailAddress = "confirmed@example.com", Roles = { - new Role { Name = Constants.AdminRoleName } + new Role { Name = CoreConstants.AdminRoleName } } }, new Credential { Type = providerUsedForLogin }); diff --git a/tests/NuGetGallery.Facts/Controllers/OrganizationsControllerFacts.cs b/tests/NuGetGallery.Facts/Controllers/OrganizationsControllerFacts.cs index 563bbe0b57..aec6d9c7c4 100644 --- a/tests/NuGetGallery.Facts/Controllers/OrganizationsControllerFacts.cs +++ b/tests/NuGetGallery.Facts/Controllers/OrganizationsControllerFacts.cs @@ -28,7 +28,7 @@ protected override User GetCurrentUser(OrganizationsController controller) // Note general account tests are in the base class. Organization-specific tests are below. [Fact] - public void WhenCurrentUserIsCollaborator_ReturnsForbidden() + public void WhenCurrentUserIsCollaborator_ReturnsReadOnly() { // Arrange var controller = GetController(); @@ -36,11 +36,11 @@ public void WhenCurrentUserIsCollaborator_ReturnsForbidden() controller.SetCurrentUser(Fakes.OrganizationCollaborator); // Act - var result = InvokeAccount(controller) as HttpStatusCodeResult; + var result = InvokeAccount(controller); // Assert - Assert.NotNull(result); - Assert.Equal((int)HttpStatusCode.Forbidden, result.StatusCode); + var model = ResultAssert.IsView(result, "ManageOrganization"); + Assert.False(model.CanManage); } [Fact] diff --git a/tests/NuGetGallery.Facts/Controllers/SupportControllerFacts.cs b/tests/NuGetGallery.Facts/Controllers/SupportControllerFacts.cs index af83d8d94e..70f626bab8 100644 --- a/tests/NuGetGallery.Facts/Controllers/SupportControllerFacts.cs +++ b/tests/NuGetGallery.Facts/Controllers/SupportControllerFacts.cs @@ -30,7 +30,10 @@ public void DeletedSupportIssuesAreFiltered() EmailAddress = "admin1.coldmail.com", Key = 111, EmailAllowed = true, - Roles = new List() { new Role() { Name = Constants.AdminRoleName } } + Roles = new List() + { + new Role() { Name = CoreConstants.AdminRoleName } + } }; var users = new List(){ diff --git a/tests/NuGetGallery.Facts/Controllers/UsersControllerFacts.cs b/tests/NuGetGallery.Facts/Controllers/UsersControllerFacts.cs index 1ea175b6a4..7358e6495d 100644 --- a/tests/NuGetGallery.Facts/Controllers/UsersControllerFacts.cs +++ b/tests/NuGetGallery.Facts/Controllers/UsersControllerFacts.cs @@ -1603,26 +1603,6 @@ public async Task GivenValidRequest_CanDeleteMicrosoftAccountWithMultipleMicroso public class TheLinkOrChangeCredentialAction : TestContainer { - [Fact] - public void ForAADLinkedAccount_ErrorIsReturned() - { - // Arrange - var fakes = Get(); - var aadCred = new CredentialBuilder().CreateExternalCredential("AzureActiveDirectory", "blorg", "bloog"); - var passwordCred = new Credential("password.v3", "random"); - var msftCred = new CredentialBuilder().CreateExternalCredential("MicrosoftAccount", "bloom", "filter"); - var user = fakes.CreateUser("test", aadCred, passwordCred, msftCred); - var controller = GetController(); - controller.SetCurrentUser(user); - - // Act - var result = controller.LinkOrChangeExternalCredential(); - - // Assert - ResultAssert.IsRedirectToRoute(result, new { action = "Account" }); - Assert.Equal(Strings.ChangeCredential_NotAllowed, controller.TempData["WarningMessage"]); - } - [Fact] public void ForNonAADLinkedAccount_RedirectsToAuthenticateExternalLogin() { diff --git a/tests/NuGetGallery.Facts/Framework/Fakes.cs b/tests/NuGetGallery.Facts/Framework/Fakes.cs index 75736bcb1e..5cb4a4754a 100644 --- a/tests/NuGetGallery.Facts/Framework/Fakes.cs +++ b/tests/NuGetGallery.Facts/Framework/Fakes.cs @@ -104,8 +104,14 @@ public Fakes() { Key = key++, EmailAddress = "confirmed3@example.com", - Credentials = new List { TestCredentialHelper.CreatePbkdf2Password(Password) }, - Roles = new List {new Role {Name = Constants.AdminRoleName}} + Credentials = new List + { + TestCredentialHelper.CreatePbkdf2Password(Password) + }, + Roles = new List + { + new Role {Name = CoreConstants.AdminRoleName} + } }; Owner = new User("testPackageOwner") diff --git a/tests/NuGetGallery.Facts/NuGetGallery.Facts.csproj b/tests/NuGetGallery.Facts/NuGetGallery.Facts.csproj index 6b674c17fb..6482879b1a 100644 --- a/tests/NuGetGallery.Facts/NuGetGallery.Facts.csproj +++ b/tests/NuGetGallery.Facts/NuGetGallery.Facts.csproj @@ -530,7 +530,6 @@ - diff --git a/tests/NuGetGallery.Facts/PolicyFacts.cs b/tests/NuGetGallery.Facts/PolicyFacts.cs index d832cbc74b..80422a7d89 100644 --- a/tests/NuGetGallery.Facts/PolicyFacts.cs +++ b/tests/NuGetGallery.Facts/PolicyFacts.cs @@ -22,7 +22,7 @@ public void AllAdminControllersHaveAuthorizeAttributeOnClassSettingAllowedRolesT var failingTypes = (from t in TypesInTheSameNamespaceAs(typeof(AdminControllerBase)) where t.GetInterfaces().Contains(typeof(IController)) let a = t.GetCustomAttribute(inherit: true) - where a == null || !String.Equals(a.Roles, Constants.AdminRoleName) + where a == null || !String.Equals(a.Roles, CoreConstants.AdminRoleName) select t) .ToList(); diff --git a/tests/NuGetGallery.Facts/Services/NuGetExeDownloaderServiceFacts.cs b/tests/NuGetGallery.Facts/Services/NuGetExeDownloaderServiceFacts.cs deleted file mode 100644 index c0e215ab75..0000000000 --- a/tests/NuGetGallery.Facts/Services/NuGetExeDownloaderServiceFacts.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.IO; -using System.Threading.Tasks; -using System.Web.Mvc; -using Moq; -using NuGet; -using NuGetGallery.Packaging; -using Xunit; - -namespace NuGetGallery -{ - public class NuGetExeDownloaderServiceFacts - { - private static readonly Uri HttpRequestUrl = new Uri("http://nuget.org/nuget.exe"); - private static readonly Uri HttpsRequestUrl = new Uri("https://nuget.org/nuget.exe"); - - [Fact] - public async Task CreateNuGetExeDownloadDoesNotExtractFileIfItAlreadyExists() - { - // Arrange - var fileStorage = new Mock(MockBehavior.Strict); - fileStorage.Setup(s => s.CreateDownloadFileActionResultAsync(HttpRequestUrl, "downloads", "nuget.exe")) - .Returns(Task.FromResult(Mock.Of())) - .Verifiable(); - - // Act - var downloaderService = GetDownloaderService(fileStorageService: fileStorage); - await downloaderService.CreateNuGetExeDownloadActionResultAsync(HttpRequestUrl); - - // Assert - fileStorage.Verify(); - } - - private static NuGetExeDownloaderService GetDownloaderService( - Mock fileStorageService = null) - { - fileStorageService = fileStorageService ?? new Mock(MockBehavior.Strict); - - return new NuGetExeDownloaderService(fileStorageService.Object); - } - } -} \ No newline at end of file diff --git a/tests/NuGetGallery.Facts/Services/PackageOwnershipManagementServiceFacts.cs b/tests/NuGetGallery.Facts/Services/PackageOwnershipManagementServiceFacts.cs index bbb7a65a27..bec5dcef19 100644 --- a/tests/NuGetGallery.Facts/Services/PackageOwnershipManagementServiceFacts.cs +++ b/tests/NuGetGallery.Facts/Services/PackageOwnershipManagementServiceFacts.cs @@ -377,7 +377,15 @@ public async Task AdminCanRemoveAnyOwner() { var existingOwner1 = new User { Key = 100, Username = "microsoft" }; var package = new PackageRegistration { Key = 2, Id = "Microsoft.Aspnet.Package1", IsVerified = true, Owners = new List { existingOwner1 } }; - var adminOwner = new User { Key = 100, Username = "aspnet", Roles = new List { new Role { Name = Constants.AdminRoleName } } }; + var adminOwner = new User + { + Key = 100, + Username = "aspnet", + Roles = new List + { + new Role { Name = CoreConstants.AdminRoleName } + } + }; var existingNamespace1 = new ReservedNamespace("microsoft.aspnet.", isSharedNamespace: false, isPrefix: true); existingOwner1.ReservedNamespaces.Add(existingNamespace1); package.ReservedNamespaces.Add(existingNamespace1); diff --git a/tests/NuGetGallery.Facts/Services/PackageServiceFacts.cs b/tests/NuGetGallery.Facts/Services/PackageServiceFacts.cs index 39be9c0273..72a7f10328 100644 --- a/tests/NuGetGallery.Facts/Services/PackageServiceFacts.cs +++ b/tests/NuGetGallery.Facts/Services/PackageServiceFacts.cs @@ -248,17 +248,17 @@ public async Task WillStoreTheHashForTheCreatedPackage() var currentUser = new User(); var packageStream = nugetPackage.Object.GetStream().AsSeekableStream(); - var expectedHash = CryptographyService.GenerateHash(packageStream, Constants.Sha512HashAlgorithmId); + var expectedHash = CryptographyService.GenerateHash(packageStream, CoreConstants.Sha512HashAlgorithmId); var packageStreamMetadata = new PackageStreamMetadata { Hash = expectedHash, - HashAlgorithm = Constants.Sha512HashAlgorithmId, + HashAlgorithm = CoreConstants.Sha512HashAlgorithmId, Size = packageStream.Length }; var package = await service.CreatePackageAsync(nugetPackage.Object, packageStreamMetadata, currentUser, currentUser, isVerified: false); Assert.Equal(expectedHash, package.Hash); - Assert.Equal(Constants.Sha512HashAlgorithmId, package.HashAlgorithm); + Assert.Equal(CoreConstants.Sha512HashAlgorithmId, package.HashAlgorithm); } [Fact] @@ -309,11 +309,11 @@ public async Task WillSaveThePackageFileAndSetThePackageFileSize() var currentUser = new User(); var packageStream = nugetPackage.Object.GetStream().AsSeekableStream(); - var packageHash = CryptographyService.GenerateHash(packageStream, Constants.Sha512HashAlgorithmId); + var packageHash = CryptographyService.GenerateHash(packageStream, CoreConstants.Sha512HashAlgorithmId); var packageStreamMetadata = new PackageStreamMetadata { Hash = packageHash, - HashAlgorithm = Constants.Sha512HashAlgorithmId, + HashAlgorithm = CoreConstants.Sha512HashAlgorithmId, Size = 618 }; var package = await service.CreatePackageAsync(nugetPackage.Object, packageStreamMetadata, currentUser, currentUser, isVerified: false); diff --git a/tests/NuGetGallery.Facts/Services/PermissionsHelpersFacts.cs b/tests/NuGetGallery.Facts/Services/PermissionsHelpersFacts.cs index 52bd7c29c3..38c6d57520 100644 --- a/tests/NuGetGallery.Facts/Services/PermissionsHelpersFacts.cs +++ b/tests/NuGetGallery.Facts/Services/PermissionsHelpersFacts.cs @@ -72,7 +72,7 @@ public void ReturnsSatisfiedRequirementWhenExpected(IEnumerable(async () => + var exception = await Assert.ThrowsAsync(async () => { await service.DeleteMemberAsync(fakes.Organization, fakes.OrganizationAdmin.Username); }); service.MockEntitiesContext.Verify(c => c.SaveChangesAsync(), Times.Never); + + Assert.Equal( + Strings.DeleteMember_CannotRemoveLastAdmin, + exception.Message); } [Fact] @@ -227,12 +231,15 @@ public async Task WhenRemovingLastAdmin_ThrowsEntityException() var service = new TestableUserService(); // Act & Assert - await Assert.ThrowsAsync(async () => + var exception = await Assert.ThrowsAsync(async () => { await service.UpdateMemberAsync(fakes.Organization, fakes.OrganizationAdmin.Username, false); }); service.MockEntitiesContext.Verify(c => c.SaveChangesAsync(), Times.Never); + Assert.Equal( + Strings.UpdateMember_CannotRemoveLastAdmin, + exception.Message); } [Fact] diff --git a/tests/NuGetGallery.Facts/Telemetry/ClientTelemetryPIIProcessorTests.cs b/tests/NuGetGallery.Facts/Telemetry/ClientTelemetryPIIProcessorTests.cs index 85d53e541b..6d1787a143 100644 --- a/tests/NuGetGallery.Facts/Telemetry/ClientTelemetryPIIProcessorTests.cs +++ b/tests/NuGetGallery.Facts/Telemetry/ClientTelemetryPIIProcessorTests.cs @@ -147,7 +147,7 @@ public static IEnumerable PIIUrlDataGenerator() yield return new string[] { "packages/{id}/owners/{username}/reject/{token}", $"https://localhost/packages/pack1/owners/user1/reject/sometoken", $"https://localhost/packages/pack1/owners/ObfuscatedUserName/reject/sometoken" }; yield return new string[] { "packages/{id}/owners/{username}/cancel/{token}", $"https://localhost/packages/pack1/owners/user1/cancel/sometoken", $"https://localhost/packages/pack1/owners/ObfuscatedUserName/cancel/sometoken" }; - yield return new string[] { "account/confirm/{username}/{token}", $"https://localhost/account/confirm/user1/sometoken", $"https://localhost/account/confirm/ObfuscatedUserName/sometoken" }; + yield return new string[] { "account/confirm/{accountName}/{token}", $"https://localhost/account/confirm/user1/sometoken", $"https://localhost/account/confirm/ObfuscatedUserName/sometoken" }; yield return new string[] { "account/delete/{accountName}", "https://localhost/account/delete/user1", $"https://localhost/account/delete/ObfuscatedUserName" }; yield return new string[] { "profiles/{username}", $"https://localhost/profiles/user1", $"https://localhost/profiles/ObfuscatedUserName" }; diff --git a/tests/NuGetGallery.Facts/TestUtils/TestUtility.cs b/tests/NuGetGallery.Facts/TestUtils/TestUtility.cs index 23a7f3cab7..f01869fd70 100644 --- a/tests/NuGetGallery.Facts/TestUtils/TestUtility.cs +++ b/tests/NuGetGallery.Facts/TestUtils/TestUtility.cs @@ -1,10 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using System; -using System.Collections.Generic; using System.Collections.Specialized; using System.IO; -using System.Net.Mail; using System.Reflection; using System.Text; using System.Web; @@ -28,7 +27,16 @@ public static class TestUtility public static readonly string FakeAdminName = "theAdmin"; public static readonly int FakeAdminKey = _key++; - public static readonly User FakeAdminUser = new User() { Username = FakeAdminName, Key = FakeAdminKey, EmailAddress = "theAdmin@nuget.org", Roles = new[] { new Role { Name = Constants.AdminRoleName } } }; + public static readonly User FakeAdminUser = new User() + { + Username = FakeAdminName, + Key = FakeAdminKey, + EmailAddress = "theAdmin@nuget.org", + Roles = new[] + { + new Role { Name = CoreConstants.AdminRoleName } + } + }; public static readonly string FakeOrganizationName = "theOrganization"; public static readonly int FakeOrganizationKey = _key++;