diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..445707e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [{ + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}/lib/index.js", + "outFiles": [ + "${workspaceFolder}/**/*.js" + ] + }] +} \ No newline at end of file diff --git a/locales/de.json b/locales/de.json index 28ad708..66a783e 100644 --- a/locales/de.json +++ b/locales/de.json @@ -1,37 +1,39 @@ -{ - "User not found": "Benutzer nicht gefunden", - "Password or username wrong": "Passwort oder Benutzername falsch", - "Authorize %s": "Authorize %s", - "Login": "Einloggen", - "You are not logged in or your login is expired": "Du bist nicht länger angemeldet oder deine Anmeldung ist abgelaufen.", - "Username or Email": "Benutzername oder Email", - "Password": "Passwort", - "Next": "Weiter", - "Register": "Registrieren", - "Mail": "Mail", - "Repeat Password": "Passwort wiederholen", - "Username": "Benutzername", - "Name": "Name", - "Registration code": "Registrierungs Schlüssel", - "You need to select one of the options": "Du musst eine der Optionen auswälen", - "Male": "Mann", - "Female": "Frau", - "Other": "Anderes", - "Registration code required": "Registrierungs Schlüssel benötigt", - "Username required": "Benutzername benötigt", - "Name required": "Name benötigt", - "Mail required": "Mail benötigt", - "The passwords do not match": "Die Passwörter stimmen nicht überein", - "Password is required": "Password benötigt", - "Invalid registration code": "Ungültiger Registrierungs Schlüssel", - "Username taken": "Benutzername nicht verfügbar", - "Mail linked with other account": "Mail ist bereits mit einem anderen Account verbunden", - "Registration code already used": "Registrierungs Schlüssel wurde bereits verwendet", - "Administration": "Administration", - "Field {{field}} is not defined": "Feld {{field}} fehlt", - "Field {{field}} has wrong type. It should be from type {{type}}": "Feld {{field}} hat den falschen Typ. Es sollte vom Typ {{type}} sein", - "Client has no permission for acces password auth": "Dieser Client hat keine Berechtigung password auth zu benutzen", - "Invalid token": "Ungültiger Token", - "By clicking on ALLOW, you allow this app to access the requested recources.": "Wenn sie ALLOW drücken, berechtigen sie die Applikation die beantragten Resourcen zu benutzen.", - "User": "User" +{ + "User not found": "Benutzer nicht gefunden", + "Password or username wrong": "Passwort oder Benutzername falsch", + "Authorize %s": "Authorize %s", + "Login": "Einloggen", + "You are not logged in or your login is expired": "Du bist nicht länger angemeldet oder deine Anmeldung ist abgelaufen.", + "Username or Email": "Benutzername oder Email", + "Password": "Passwort", + "Next": "Weiter", + "Register": "Registrieren", + "Mail": "Mail", + "Repeat Password": "Passwort wiederholen", + "Username": "Benutzername", + "Name": "Name", + "Registration code": "Registrierungs Schlüssel", + "You need to select one of the options": "Du musst eine der Optionen auswälen", + "Male": "Mann", + "Female": "Frau", + "Other": "Anderes", + "Registration code required": "Registrierungs Schlüssel benötigt", + "Username required": "Benutzername benötigt", + "Name required": "Name benötigt", + "Mail required": "Mail benötigt", + "The passwords do not match": "Die Passwörter stimmen nicht überein", + "Password is required": "Password benötigt", + "Invalid registration code": "Ungültiger Registrierungs Schlüssel", + "Username taken": "Benutzername nicht verfügbar", + "Mail linked with other account": "Mail ist bereits mit einem anderen Account verbunden", + "Registration code already used": "Registrierungs Schlüssel wurde bereits verwendet", + "Administration": "Administration", + "Field {{field}} is not defined": "Feld {{field}} fehlt", + "Field {{field}} has wrong type. It should be from type {{type}}": "Feld {{field}} hat den falschen Typ. Es sollte vom Typ {{type}} sein", + "Client has no permission for acces password auth": "Dieser Client hat keine Berechtigung password auth zu benutzen", + "Invalid token": "Ungültiger Token", + "By clicking on ALLOW, you allow this app to access the requested recources.": "Wenn sie ALLOW drücken, berechtigen sie die Applikation die beantragten Resourcen zu benutzen.", + "User": "User", + "No special token": "No special token", + "Login token invalid": "Login token invalid" } \ No newline at end of file diff --git a/locales/en.json b/locales/en.json index 6f7655d..a7bf8e9 100644 --- a/locales/en.json +++ b/locales/en.json @@ -3,5 +3,11 @@ "Username or Email": "Username or Email", "Password": "Password", "Next": "Next", - "Invalid code": "Invalid code" + "Invalid code": "Invalid code", + "You are not logged in or your login is expired": "You are not logged in or your login is expired", + "User not found": "User not found", + "No special token": "No special token", + "You are not logged in or your login is expired(No special token)": "You are not logged in or your login is expired(No special token)", + "Special token invalid": "Special token invalid", + "You are not logged in or your login is expired(Special token invalid)": "You are not logged in or your login is expired(Special token invalid)" } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 56ea0b2..0b23c37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,10 +4,22 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@hibas123/logging": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@hibas123/logging/-/logging-2.1.0.tgz", + "integrity": "sha512-h8uZcFTxkbvEy3BgEYXl6ElfV5I1bonkYT6slBtzH38klkVv3GgkA7EL21JewPiYuNOsQYfXyXxoYxnxqWJgJA==", + "requires": { + "@hibas123/utils": "^2.1.0" + } + }, "@hibas123/nodelogging": { - "version": "1.3.21", - "resolved": "https://registry.npmjs.org/@hibas123/nodelogging/-/nodelogging-1.3.21.tgz", - "integrity": "sha512-olmnR0OuWIC41pVR5PBGQQX7Ja++76/Jkshw7uMwLUzX0RvbbpfGFj/a3agl21gQnRoNvYfFlR/U5Ce7qvYneg==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@hibas123/nodelogging/-/nodelogging-2.1.0.tgz", + "integrity": "sha512-Tms9Y3XQ7e9XV6kbp+fFKXQF9cEE4v6QQ1tzE1e9SKlO//mP/1+TpAmSJ0R+LPBlQR2xVzukvilJMD89xOfFUQ==", + "requires": { + "@hibas123/logging": "^2.1.0", + "@hibas123/utils": "^2.1.0" + } }, "@hibas123/nodeloggingserver_client": { "version": "1.1.2", @@ -18,15 +30,31 @@ } }, "@hibas123/safe_mongo": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@hibas123/safe_mongo/-/safe_mongo-1.5.3.tgz", - "integrity": "sha512-7YYnvaYEjJN33PB4xTnyFA07SA7vExjiNb5dgfd7IiBaDYazU81G93Y1h+IFYdzVYq9CpXiEHpmeGmElTqRwSA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@hibas123/safe_mongo/-/safe_mongo-1.6.1.tgz", + "integrity": "sha512-CfAX/e4Fdm06ELUhkxnLH64ClIvXjohWD41M2ktGdc9G2b6aQlJ4MeaclvLe8D8/fjo1T83hZFt+nFDYvbCr1w==", "requires": { - "@hibas123/nodelogging": "^1.3.18", - "@types/mongodb": "^3.1.19", - "mongodb": "^3.1.13" + "@hibas123/logging": "^2.1.0", + "@types/mongodb": "^3.1.30", + "mongodb": "^3.2.7" + }, + "dependencies": { + "@types/mongodb": { + "version": "3.1.31", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.1.31.tgz", + "integrity": "sha512-0l6z2ARkyAkgdtzF/Hqx3cRoADDQK/7VHvESikhLXjanSpIo1EJt2JL4NpM+jIqLWzn5P1IxRBSIIOqs5ZKBOQ==", + "requires": { + "@types/bson": "*", + "@types/node": "*" + } + } } }, + "@hibas123/utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@hibas123/utils/-/utils-2.1.0.tgz", + "integrity": "sha512-rS0SMENN7ta6pv9bNmIls8lvEOHbbDQXpiDI3u3z0Vx8B43ngEmEXbu5vKBA58ekYLr1WdHH6iIiySDChoi/RQ==" + }, "@types/body-parser": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", @@ -46,57 +74,12 @@ } }, "@types/compression": { - "version": "0.0.36", - "resolved": "http://registry.npmjs.org/@types/compression/-/compression-0.0.36.tgz", - "integrity": "sha512-B66iZCIcD2eB2F8e8YDIVtCUKgfiseOR5YOIbmMN2tM57Wu55j1xSdxdSw78aVzsPmbZ6G+hINc+1xe1tt4NBg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.0.0.tgz", + "integrity": "sha512-WrGAfLyrVFZ1NKdXTTzkXcKpaVPESJZYSV0zOx8FFoP551HpaOdWLRNbxr+nrj2gNLrCsDaE1RMzOEbdVrIzEA==", "dev": true, "requires": { "@types/express": "*" - }, - "dependencies": { - "@types/express": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.11.1.tgz", - "integrity": "sha512-ttWle8cnPA5rAelauSWeWJimtY2RsUf2aspYZs7xPHiWgOlPn6nnUfBMtrkcnjFJuIHJF4gNOdVvpLK2Zmvh6g==", - "dev": true, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "*", - "@types/serve-static": "*" - }, - "dependencies": { - "@types/body-parser": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", - "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.11.2", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.11.2.tgz", - "integrity": "sha512-5ukJmirhZqJh/jEDFn40GANZYtO95C7Pu3Xd9s8hHCtGhZORDVXiFtKLHKDE/s8T72Uvy4BZSTqsgFQMWGg/RA==", - "dev": true, - "requires": { - "@types/events": "*", - "@types/node": "*" - } - }, - "@types/serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", - "dev": true, - "requires": { - "@types/express-serve-static-core": "*", - "@types/mime": "*" - } - } - } - } } }, "@types/connect": { @@ -164,9 +147,9 @@ } }, "@types/dotenv": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.0.tgz", - "integrity": "sha512-gmbNb7V1LbJQA4MmH0hVFgqY1cyKsa6RvKC1Xrq0WBnZ0JuuvXKciXx/s8dN0LVXCJd8xO6wIaSFSyUIoGph9g==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz", + "integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==", "dev": true, "requires": { "@types/node": "*" @@ -179,9 +162,9 @@ "dev": true }, "@types/express": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.1.tgz", - "integrity": "sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.0.tgz", + "integrity": "sha512-CjaMu57cjgjuZbh9DpkloeGxV45CnMGlVd+XpG7Gm9QgVrd7KFq+X4HY0vM+2v0bczS48Wg7bvnMY5TN+Xmcfw==", "dev": true, "requires": { "@types/body-parser": "*", @@ -190,9 +173,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.1.tgz", - "integrity": "sha512-QgbIMRU1EVRry5cIu1ORCQP4flSYqLM1lS5LYyGWfKnFT3E58f0gKto7BR13clBFVrVZ0G0rbLZ1hUpSkgQQOA==", + "version": "4.16.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.7.tgz", + "integrity": "sha512-847KvL8Q1y3TtFLRTXcVakErLJQgdpFSaq+k043xefz9raEf0C7HalpSY7OW5PyjCnY8P7bPW5t/Co9qqp+USg==", "dev": true, "requires": { "@types/node": "*", @@ -227,18 +210,19 @@ "dev": true }, "@types/mongodb": { - "version": "3.1.22", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.1.22.tgz", - "integrity": "sha512-hvNR0txBlJJAy1eZOeIDshW4dnQaC694COou4eHHaMdIcteCfoCQATD7sYNlXxNxfTc1iIbHUi7A8CAhQe08uA==", + "version": "3.1.31", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.1.31.tgz", + "integrity": "sha512-0l6z2ARkyAkgdtzF/Hqx3cRoADDQK/7VHvESikhLXjanSpIo1EJt2JL4NpM+jIqLWzn5P1IxRBSIIOqs5ZKBOQ==", + "dev": true, "requires": { "@types/bson": "*", "@types/node": "*" } }, "@types/node": { - "version": "11.11.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.3.tgz", - "integrity": "sha512-wp6IOGu1lxsfnrD+5mX6qwSwWuqsdkKKxTN4aQc4wByHAKZJf9/D4KXPQ1POUjEbnCP5LMggB0OEFNY9OTsMqg==" + "version": "12.6.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.9.tgz", + "integrity": "sha512-+YB9FtyxXGyD54p8rXwWaN1EWEyar5L58GlGWgtH2I9rGmLGBQcw63+0jw+ujqVavNuO47S1ByAjm9zdHMnskw==" }, "@types/node-rsa": { "version": "1.0.0", @@ -250,9 +234,9 @@ } }, "@types/qrcode": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.3.1.tgz", - "integrity": "sha512-hmCM2k6+oo7Ey0NFu3qoxLMt1JCV4NZHMSa6PIfvP84Xe3OWh+wpax7HsHAohilpQBJBm6CmhuVZexoV10z+rQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.3.3.tgz", + "integrity": "sha512-+5vox9KhEPGP+d2ah8V+gnHAaTDvFHssLz8KJS7OgJuessGGybChJYfmo+fwNFzOVUtfcWkTCJqkFDRz15hCYw==", "dev": true, "requires": { "@types/node": "*" @@ -275,18 +259,18 @@ } }, "@types/speakeasy": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/speakeasy/-/speakeasy-2.0.4.tgz", - "integrity": "sha512-WcZalHN3tlh+StC8cszTuh2SkX+vn5s4K+eMwa2fXM4t3GDeYg6JVrpchHs9InqTkgXXsEtE8KNXaQxfkIdmng==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/speakeasy/-/speakeasy-2.0.5.tgz", + "integrity": "sha512-8jnDuZM/q77cd3naF5T3VRlNbNuYUvnixYYSu0dhpPxc9UNrBNubDlXoyVIyQgfvOd7pSc0cs4ETB5kygtpWZQ==", "dev": true, "requires": { "@types/node": "*" } }, "@types/uuid": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz", - "integrity": "sha512-tPIgT0GUmdJQNSHxp0X2jnpQfBSTfGxUMc/2CXBU2mnyTFVYVa2ojpoQ74w0U2yn2vw3jnC640+77lkFFpdVDw==", + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.5.tgz", + "integrity": "sha512-MNL15wC3EKyw1VLF+RoVO4hJJdk9t/Hlv3rt1OL65Qvuadm4BYo6g9ZJQqoq7X8NBFSsQXgAujWciovh2lpVjA==", "dev": true, "requires": { "@types/node": "*" @@ -353,13 +337,13 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -509,14 +493,15 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "dev": true, "requires": { "lodash": "^4.17.11" } }, "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, "async-limiter": { @@ -598,7 +583,8 @@ "base32.js": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.0.1.tgz", - "integrity": "sha1-0EVzalex9sE58MffQlGKhOkbsro=" + "integrity": "sha1-0EVzalex9sE58MffQlGKhOkbsro=", + "dev": true }, "base64-arraybuffer": { "version": "0.1.5", @@ -614,9 +600,9 @@ } }, "binary-extensions": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", - "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "dev": true }, "blob": { @@ -625,20 +611,35 @@ "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" }, "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", "requires": { - "bytes": "3.0.0", + "bytes": "3.1.0", "content-type": "~1.0.4", "debug": "2.6.9", "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } } }, "boxen": { @@ -703,21 +704,15 @@ } }, "bson": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.0.tgz", - "integrity": "sha512-9Aeai9TacfNtWXOYarkFJRW2CWo+dRon+fuLZYJmvLV3+MiUp0bEI6IAZfXEIg7/Pl/7IWlLaDnhzTsD81etQA==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz", + "integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg==" }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -750,14 +745,6 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==" }, - "can-promise": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/can-promise/-/can-promise-0.0.1.tgz", - "integrity": "sha512-gzVrHyyrvgt0YpDm7pn04MQt8gjh0ZAhN4ZDyCRtGl6YnuuK6b4aiUTD7G52r9l4YNmxfTtEscb92vxtAlL6XQ==", - "requires": { - "window-or-global": "^1.0.1" - } - }, "capture-stack-trace": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", @@ -787,9 +774,9 @@ } }, "chokidar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.2.tgz", - "integrity": "sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", + "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -803,7 +790,7 @@ "normalize-path": "^3.0.0", "path-is-absolute": "^1.0.0", "readdirp": "^2.2.1", - "upath": "^1.1.0" + "upath": "^1.1.1" } }, "ci-info": { @@ -845,6 +832,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, "requires": { "string-width": "^2.1.1", "strip-ansi": "^4.0.0", @@ -854,7 +842,8 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true }, "collection-visit": { "version": "1.0.0", @@ -880,7 +869,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -888,8 +876,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { "version": "1.5.3", @@ -924,9 +911,9 @@ } }, "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", "optional": true }, "component-bind": { @@ -945,23 +932,23 @@ "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" }, "compressible": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.15.tgz", - "integrity": "sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw==", + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", + "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", "requires": { - "mime-db": ">= 1.36.0 < 2" + "mime-db": ">= 1.40.0 < 2" } }, "compression": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", - "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", "requires": { "accepts": "~1.3.5", "bytes": "3.0.0", - "compressible": "~2.0.14", + "compressible": "~2.0.16", "debug": "2.6.9", - "on-headers": "~1.0.1", + "on-headers": "~1.0.2", "safe-buffer": "5.1.2", "vary": "~1.1.2" } @@ -972,9 +959,9 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concurrently": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-4.1.0.tgz", - "integrity": "sha512-pwzXCE7qtOB346LyO9eFWpkFJVO3JQZ/qU/feGeaAHiX1M3Rw3zgXKc5cZ8vSH5DGygkjzLFDzA/pwoQDkRNGg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-4.1.1.tgz", + "integrity": "sha512-48+FE5RJ0qc8azwKv4keVQWlni1hZeSjcWr8shBelOBtBHcKj1aJFM9lHRiSc1x7lq416pkvsqfBMhSRja+Lhw==", "dev": true, "requires": { "chalk": "^2.4.1", @@ -1003,9 +990,12 @@ } }, "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } }, "content-type": { "version": "1.0.4", @@ -1065,6 +1055,7 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, "requires": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -1192,9 +1183,9 @@ } }, "dotenv": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz", - "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==" + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.0.0.tgz", + "integrity": "sha512-30xVGqjLjiUOArT4+M5q9sYdvuR4riM6yK9wMcas9Vbp6zZa+ocC9dp6QoftuhTPhFAiLK/0C5Ni2nou/Bk8lg==" }, "duplexer3": { "version": "0.1.4", @@ -1228,6 +1219,11 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, "enabled": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", @@ -1246,6 +1242,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, "requires": { "once": "^1.4.0" } @@ -1339,6 +1336,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, "requires": { "cross-spawn": "^6.0.0", "get-stream": "^4.0.0", @@ -1385,58 +1383,68 @@ } }, "express": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", - "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", "requires": { - "accepts": "~1.3.5", + "accepts": "~1.3.7", "array-flatten": "1.1.1", - "body-parser": "1.18.3", - "content-disposition": "0.5.2", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", "content-type": "~1.0.4", - "cookie": "0.3.1", + "cookie": "0.4.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "~1.1.2", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.1.1", + "finalhandler": "~1.1.2", "fresh": "0.5.2", "merge-descriptors": "1.0.1", "methods": "~1.1.2", "on-finished": "~2.3.0", - "parseurl": "~1.3.2", + "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.4", - "qs": "6.5.2", - "range-parser": "~1.2.0", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", "safe-buffer": "5.1.2", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "dependencies": { - "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" + "mime-types": "~2.1.24", + "negotiator": "0.6.2" } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" } } }, @@ -1592,16 +1600,16 @@ } }, "finalhandler": { - "version": "1.1.1", - "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", "unpipe": "~1.0.0" } }, @@ -1667,14 +1675,14 @@ "dev": true }, "fsevents": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", - "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" }, "dependencies": { "abbrev": { @@ -1752,12 +1760,12 @@ "optional": true }, "debug": { - "version": "2.6.9", + "version": "4.1.1", "bundled": true, "dev": true, "optional": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "deep-extend": { @@ -1928,24 +1936,24 @@ } }, "ms": { - "version": "2.0.0", + "version": "2.1.1", "bundled": true, "dev": true, "optional": true }, "needle": { - "version": "2.2.4", + "version": "2.3.0", "bundled": true, "dev": true, "optional": true, "requires": { - "debug": "^2.1.2", + "debug": "^4.1.0", "iconv-lite": "^0.4.4", "sax": "^1.2.4" } }, "node-pre-gyp": { - "version": "0.10.3", + "version": "0.12.0", "bundled": true, "dev": true, "optional": true, @@ -1973,13 +1981,13 @@ } }, "npm-bundled": { - "version": "1.0.5", + "version": "1.0.6", "bundled": true, "dev": true, "optional": true }, "npm-packlist": { - "version": "1.2.0", + "version": "1.4.1", "bundled": true, "dev": true, "optional": true, @@ -2118,7 +2126,7 @@ "optional": true }, "semver": { - "version": "5.6.0", + "version": "5.7.0", "bundled": true, "dev": true, "optional": true @@ -2217,12 +2225,14 @@ "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, "requires": { "pump": "^3.0.0" } @@ -2310,11 +2320,11 @@ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, "handlebars": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz", - "integrity": "sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", "requires": { - "async": "^2.5.0", + "neo-async": "^2.6.0", "optimist": "^0.6.1", "source-map": "^0.6.1", "uglify-js": "^3.1.4" @@ -2379,20 +2389,24 @@ } }, "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.2.tgz", + "integrity": "sha512-CyjlXII6LMsPMyUzxpTt8fzh5QwzGqPmQXgY/Jyf4Zfp27t/FvfhwoE/8laaMUcMy816CkWF20I7NeQhwwY88w==", + "dev": true, + "requires": { + "lru-cache": "^5.1.1" + } }, "http-errors": { - "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "requires": { "depd": "~1.1.2", "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" } }, "i18n": { @@ -2422,6 +2436,7 @@ "version": "0.4.23", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -2485,12 +2500,13 @@ "invert-kv": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true }, "ipaddr.js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", - "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" }, "is-accessor-descriptor": { "version": "0.1.6", @@ -2533,15 +2549,6 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, - "is-builtin-module": { - "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, - "requires": { - "builtin-modules": "^1.0.0" - } - }, "is-ci": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", @@ -2608,9 +2615,9 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -2691,7 +2698,8 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true }, "is-windows": { "version": "1.0.2", @@ -2708,7 +2716,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "isobject": { "version": "3.0.1", @@ -2732,11 +2741,11 @@ } }, "jsonwebtoken": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", - "integrity": "sha512-IqEycp0znWHNA11TpYi77bVgyBO/pGESDh7Ajhas+u0ttkGkKYIIAjniL4Bw5+oVejVF+SYkaI7XKfwCCyeTuA==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", "requires": { - "jws": "^3.2.1", + "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", @@ -2754,16 +2763,16 @@ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" } } }, "jwa": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.3.0.tgz", - "integrity": "sha512-SxObIyzv9a6MYuZYaSN6DhSm9j3+qkokwvCB0/OTSV5ylPq1wUQiygZQcHT5Qlux0I5kmISx3J86TxKhuefItg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", "requires": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -2771,11 +2780,11 @@ } }, "jws": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.1.tgz", - "integrity": "sha512-bGA2omSrFUkd72dhh05bIAN832znP4wOU3lfuXtRBuGTbsmNmDXMQg28f0Vsxaxgk4myF5YkKQpz6qeRpMgX9g==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", "requires": { - "jwa": "^1.2.0", + "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } }, @@ -2816,6 +2825,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, "requires": { "invert-kv": "^2.0.0" } @@ -2841,7 +2851,8 @@ "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true }, "lodash.includes": { "version": "4.3.0", @@ -2906,13 +2917,12 @@ "dev": true }, "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "yallist": "^3.0.2" } }, "make-dir": { @@ -2936,6 +2946,7 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, "requires": { "p-defer": "^1.0.0" } @@ -2988,13 +2999,14 @@ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "mem": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.0.0.tgz", - "integrity": "sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, "requires": { "map-age-cleaner": "^0.1.1", - "mimic-fn": "^1.0.0", - "p-is-promise": "^1.1.0" + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" } }, "memory-pager": { @@ -3074,14 +3086,14 @@ } }, "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.37.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", - "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" }, "mime-types": { "version": "2.1.21", @@ -3099,9 +3111,10 @@ } }, "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true }, "minimatch": { "version": "3.0.4", @@ -3117,9 +3130,9 @@ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -3143,20 +3156,20 @@ "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" }, "mongodb": { - "version": "3.1.13", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.1.13.tgz", - "integrity": "sha512-sz2dhvBZQWf3LRNDhbd30KHVzdjZx9IKC0L+kSZ/gzYquCF5zPOgGqRz6sSCqYZtKP2ekB4nfLxhGtzGHnIKxA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.2.7.tgz", + "integrity": "sha512-2YdWrdf1PJgxcCrT1tWoL6nHuk6hCxhddAAaEh8QJL231ci4+P9FLyqopbTm2Z2sAU6mhCri+wd9r1hOcHdoMw==", "requires": { - "mongodb-core": "3.1.11", + "mongodb-core": "3.2.7", "safe-buffer": "^5.1.2" } }, "mongodb-core": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.1.11.tgz", - "integrity": "sha512-rD2US2s5qk/ckbiiGFHeu+yKYDXdJ1G87F6CG3YdaZpzdOm5zpoAZd/EKbPmFO6cQZ+XVXBXBJ660sSI0gc6qg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.2.7.tgz", + "integrity": "sha512-WypKdLxFNPOH/Jy6i9z47IjG2wIldA54iDZBmHMINcgKOUcWJh8og+Wix76oGd7EyYkHJKssQ2FAOw5Su/n4XQ==", "requires": { - "bson": "^1.1.0", + "bson": "^1.1.1", "require_optional": "^1.0.1", "safe-buffer": "^5.1.2", "saslprep": "^1.0.0" @@ -3173,9 +3186,9 @@ "integrity": "sha1-QCj3d4sXcIpImTCm5SrDvKDaQdA=" }, "nan": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", - "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", "dev": true, "optional": true }, @@ -3203,10 +3216,16 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, + "neo-async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", + "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==" + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, "node-rsa": { "version": "1.0.5", @@ -3217,12 +3236,12 @@ } }, "nodemon": { - "version": "1.18.10", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.18.10.tgz", - "integrity": "sha512-we51yBb1TfEvZamFchRgcfLbVYgg0xlGbyXmOtbBzDwxwgewYS/YbZ5tnlnsH51+AoSTTsT3A2E/FloUbtH8cQ==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.1.tgz", + "integrity": "sha512-/DXLzd/GhiaDXXbGId5BzxP1GlsqtMGM9zTmkWrgXtSqjKmGSbLicM/oAy4FR0YWm14jCHRwnR31AHS2dYFHrg==", "dev": true, "requires": { - "chokidar": "^2.1.0", + "chokidar": "^2.1.5", "debug": "^3.1.0", "ignore-by-default": "^1.0.1", "minimatch": "^3.0.4", @@ -3244,9 +3263,9 @@ } }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "supports-color": { @@ -3270,13 +3289,13 @@ } }, "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==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, "requires": { "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", + "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } @@ -3291,6 +3310,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, "requires": { "path-key": "^2.0.0" } @@ -3298,7 +3318,8 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true }, "object-assign": { "version": "4.1.1", @@ -3368,9 +3389,9 @@ } }, "on-headers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" }, "once": { "version": "1.4.0", @@ -3406,6 +3427,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, "requires": { "execa": "^1.0.0", "lcid": "^2.0.0", @@ -3415,17 +3437,20 @@ "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true }, "p-is-promise": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", - "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true }, "p-limit": { "version": "2.1.0", @@ -3487,9 +3512,9 @@ } }, "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, "pascalcase": { "version": "0.1.1", @@ -3522,7 +3547,14 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true }, "path-to-regexp": { "version": "0.1.7", @@ -3536,9 +3568,9 @@ "dev": true }, "pngjs": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.3.3.tgz", - "integrity": "sha512-1n3Z4p3IOxArEs1VRXnZ/RXdfEniAUS9jb68g58FIXMNkPJeZd+Qh4Uq7/e0LVxAQGos1eIUrqrt4FpjdnEd+Q==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==" }, "posix-character-classes": { "version": "0.1.1", @@ -3559,12 +3591,12 @@ "dev": true }, "proxy-addr": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", - "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", "requires": { "forwarded": "~0.1.2", - "ipaddr.js": "1.8.0" + "ipaddr.js": "1.9.0" } }, "pseudomap": { @@ -3574,58 +3606,152 @@ "dev": true }, "pstree.remy": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.6.tgz", - "integrity": "sha512-NdF35+QsqD7EgNEI5mkI/X+UwaxVEbQaz9f4IooEmMUv6ZPmlTQYGjBPJGgrlzNdjSvIy4MWMg6Q6vCgBO2K+w==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz", + "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==", "dev": true }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "qrcode": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.3.3.tgz", - "integrity": "sha512-SH7V13AcJusH3GT8bMNOGz4w0L+LjcpNOU/NiOgtBhT/5DoWeZE6D5ntMJnJ84AMkoaM4kjJJoHoh9g++8lWFg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.1.tgz", + "integrity": "sha512-3JhHQJkKqJL4PfoM6t+B40f0GWv9eNJAJmuNx2X/sHEOLvMyvEPN8GfbdN1qmr19O8N2nLraOzeWjXocHz1S4w==", "requires": { - "can-promise": "0.0.1", "dijkstrajs": "^1.0.1", "isarray": "^2.0.1", "pngjs": "^3.3.0", - "yargs": "^12.0.5" + "yargs": "^13.2.4" }, "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, "isarray": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz", - "integrity": "sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } } }, "rc": { @@ -3693,9 +3819,9 @@ } }, "registry-auth-token": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", - "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", + "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", "dev": true, "requires": { "rc": "^1.1.6", @@ -3737,7 +3863,8 @@ "require-main-filename": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true }, "require_optional": { "version": "1.0.1", @@ -3748,6 +3875,15 @@ "semver": "^5.1.0" } }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, "resolve-from": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", @@ -3766,9 +3902,9 @@ "dev": true }, "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", + "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -3802,9 +3938,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "saslprep": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.2.tgz", - "integrity": "sha512-4cDsYuAjXssUSjxHKRe4DTZC0agDwsCqcMqtJAQPzC74nJ7LfAJflAtC1Zed5hMzEQKj82d3tuzqdGNRsLJ4Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", "optional": true, "requires": { "sparse-bitfield": "^3.0.3" @@ -3835,9 +3971,9 @@ } }, "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", "requires": { "debug": "2.6.9", "depd": "~1.1.2", @@ -3846,23 +3982,30 @@ "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } } }, "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" + "parseurl": "~1.3.3", + "send": "0.17.1" } }, "set-blocking": { @@ -3871,9 +4014,9 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -3894,14 +4037,15 @@ } }, "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, "requires": { "shebang-regex": "^1.0.0" } @@ -3909,12 +4053,14 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true }, "simple-swizzle": { "version": "0.2.2", @@ -4168,15 +4314,16 @@ } }, "spdx-license-ids": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", - "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, "speakeasy": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/speakeasy/-/speakeasy-2.0.0.tgz", "integrity": "sha1-hckaBxsJpcuGQlkNmDVmFl9XYTo=", + "dev": true, "requires": { "base32.js": "0.0.1" } @@ -4223,14 +4370,15 @@ } }, "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -4249,6 +4397,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, "requires": { "ansi-regex": "^3.0.0" } @@ -4256,7 +4405,8 @@ "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true }, "strip-json-comments": { "version": "2.0.1", @@ -4330,6 +4480,22 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true } } }, @@ -4392,6 +4558,11 @@ "repeat-string": "^1.6.1" } }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, "touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", @@ -4414,18 +4585,28 @@ "dev": true }, "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", "dev": true }, "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "requires": { "media-typer": "0.3.0", - "mime-types": "~2.1.18" + "mime-types": "~2.1.24" + }, + "dependencies": { + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + } } }, "typechecker": { @@ -4434,9 +4615,9 @@ "integrity": "sha1-0cIJOlT/ihn1jP+HfuqlTyJC04M=" }, "typescript": { - "version": "3.3.3333", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.3333.tgz", - "integrity": "sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", + "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", "dev": true }, "u2f": { @@ -4451,12 +4632,12 @@ "dev": true }, "uglify-js": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", - "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", + "version": "3.5.7", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.7.tgz", + "integrity": "sha512-GCgJx3BBuaf/QMvBBkhoHDh4SVsHCC3ILEzriPw4FgJJKCuxVBSYLRkDlmT3uhXyGWKs3VN5r0mCkBIZaHWu3w==", "optional": true, "requires": { - "commander": "~2.17.1", + "commander": "~2.20.0", "source-map": "~0.6.1" } }, @@ -4475,38 +4656,15 @@ } }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "unique-string": { @@ -4576,9 +4734,9 @@ "dev": true }, "upath": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", + "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", "dev": true }, "update-notifier": { @@ -4670,6 +4828,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -4688,11 +4847,6 @@ "string-width": "^2.1.1" } }, - "window-or-global": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/window-or-global/-/window-or-global-1.0.1.tgz", - "integrity": "sha1-2+RboqKRqrxW1iz2bEW3+jIpRt4=" - }, "winston": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", @@ -4742,6 +4896,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, "requires": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1" @@ -4750,12 +4905,14 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4764,6 +4921,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -4774,6 +4932,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4786,9 +4945,9 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", - "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", "dev": true, "requires": { "graceful-fs": "^4.1.11", @@ -4828,15 +4987,16 @@ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" }, "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "dev": true }, "yargs": { "version": "12.0.5", "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, "requires": { "cliui": "^4.0.0", "decamelize": "^1.2.0", @@ -4856,6 +5016,7 @@ "version": "11.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" diff --git a/package.json b/package.json index d256a8e..aad48e7 100644 --- a/package.json +++ b/package.json @@ -17,44 +17,44 @@ }, "devDependencies": { "@types/body-parser": "^1.17.0", - "@types/compression": "^0.0.36", + "@types/compression": "^1.0.0", "@types/cookie-parser": "^1.4.1", - "@types/dotenv": "^6.1.0", - "@types/express": "^4.16.1", + "@types/dotenv": "^6.1.1", + "@types/express": "^4.17.0", "@types/i18n": "^0.8.5", "@types/ini": "^1.3.30", "@types/jsonwebtoken": "^8.3.2", - "@types/mongodb": "^3.1.22", - "@types/node": "^11.11.3", + "@types/mongodb": "^3.1.31", + "@types/node": "^12.6.9", "@types/node-rsa": "^1.0.0", - "@types/qrcode": "^1.3.1", - "@types/speakeasy": "^2.0.4", - "@types/uuid": "^3.4.4", + "@types/qrcode": "^1.3.3", + "@types/speakeasy": "^2.0.5", + "@types/uuid": "^3.4.5", "apidoc": "^0.17.7", - "concurrently": "^4.1.0", - "nodemon": "^1.18.10", - "typescript": "^3.3.3333" + "concurrently": "^4.1.1", + "nodemon": "^1.19.1", + "speakeasy": "^2.0.0", + "typescript": "^3.5.3" }, "dependencies": { - "@hibas123/nodelogging": "^1.3.21", + "@hibas123/nodelogging": "^2.1.0", "@hibas123/nodeloggingserver_client": "^1.1.2", - "@hibas123/safe_mongo": "^1.5.3", - "body-parser": "^1.18.3", - "compression": "^1.7.3", + "@hibas123/safe_mongo": "^1.6.1", + "body-parser": "^1.19.0", + "compression": "^1.7.4", "cookie-parser": "^1.4.4", "cors": "^2.8.5", - "dotenv": "^7.0.0", - "express": "^4.16.4", - "handlebars": "^4.1.0", + "dotenv": "^8.0.0", + "express": "^4.17.1", + "handlebars": "^4.1.2", "i18n": "^0.8.3", "ini": "^1.3.5", - "jsonwebtoken": "^8.5.0", + "jsonwebtoken": "^8.5.1", "moment": "^2.24.0", - "mongodb": "^3.1.13", + "mongodb": "^3.2.7", "node-rsa": "^1.0.5", - "qrcode": "^1.3.3", + "qrcode": "^1.4.1", "reflect-metadata": "^0.1.13", - "speakeasy": "^2.0.0", "u2f": "^0.1.3", "uuid": "^3.3.2" } diff --git a/src/api/client/index.ts b/src/api/client/index.ts index 9fc2702..4e31c17 100644 --- a/src/api/client/index.ts +++ b/src/api/client/index.ts @@ -1,30 +1,40 @@ -import { Request, Response, Router } from "express" -import Stacker from "../middlewares/stacker"; -import { GetClientAuthMiddleware } from "../middlewares/client"; -import { GetUserMiddleware } from "../middlewares/user"; -import { createJWT } from "../../keys"; - - -const ClientRouter = Router(); -/** - * @api {get} /client/user - * @apiParam {String} redirect_uri URL to redirect to on success - * @apiParam {String} state A optional state, that will be included in the JWT and redirect_uri as parameter - * - * @apiName ClientUser - * @apiGroup client - * - * @apiPermission user_client Requires ClientID and Authenticated User - */ -ClientRouter.get("/user", Stacker(GetClientAuthMiddleware(false), GetUserMiddleware(false, false), async (req: Request, res: Response) => { - let { redirect_uri, state } = req.query; - let jwt = await createJWT({ - client: req.client.client_id, - uid: req.user.uid, - username: req.user.username, - state: state - }, 30); //after 30 seconds this token is invalid - res.redirect(redirect_uri + "?jwt=" + jwt + (state ? `&state=${state}` : "")); -})); - +import { Request, Response, Router } from "express" +import Stacker from "../middlewares/stacker"; +import { GetClientAuthMiddleware } from "../middlewares/client"; +import { GetUserMiddleware } from "../middlewares/user"; +import { createJWT } from "../../keys"; +import Client from "../../models/client"; +import RequestError, { HttpStatusCode } from "../../helper/request_error"; + + +const ClientRouter = Router(); + +/** + * @api {get} /client/user + * + * @apiDescription Can be used for simple authentication of user. It will redirect the user to the redirect URI with a very short lived jwt. + * + * @apiParam {String} redirect_uri URL to redirect to on success + * @apiParam {String} state A optional state, that will be included in the JWT and redirect_uri as parameter + * + * @apiName ClientUser + * @apiGroup client + * + * @apiPermission user_client Requires ClientID and Authenticated User + */ +ClientRouter.get("/user", Stacker(GetClientAuthMiddleware(false), GetUserMiddleware(false, false), async (req: Request, res: Response) => { + let { redirect_uri, state } = req.query; + + if (redirect_uri !== req.client.redirect_url) + throw new RequestError("Invalid redirect URI", HttpStatusCode.BAD_REQUEST); + + let jwt = await createJWT({ + client: req.client.client_id, + uid: req.user.uid, + username: req.user.username, + state: state + }, 30); //after 30 seconds this token is invalid + res.redirect(redirect_uri + "?jwt=" + jwt + (state ? `&state=${state}` : "")); +})); + export default ClientRouter; \ No newline at end of file diff --git a/src/api/middlewares/user.ts b/src/api/middlewares/user.ts index c55a14e..9f91d88 100644 --- a/src/api/middlewares/user.ts +++ b/src/api/middlewares/user.ts @@ -1,74 +1,79 @@ -import { NextFunction, Request, Response } from "express"; -import LoginToken, { CheckToken } from "../../models/login_token"; -import Logging from "@hibas123/nodelogging"; -import RequestError, { HttpStatusCode } from "../../helper/request_error"; -import User from "../../models/user"; -import promiseMiddleware from "../../helper/promiseMiddleware"; - -class Invalid extends Error { } - -/** - * Returns customized Middleware function, that could also be called directly - * by code and will return true or false depending on the token. In the false - * case it will also send error and redirect if json is not set - * @param json Checks if requests wants an json or html for returning errors - * @param redirect_uri Sets the uri to redirect, if json is not set and user not logged in - */ -export function GetUserMiddleware(json = false, special_required: boolean = false, redirect_uri?: string, validated = true) { - return promiseMiddleware(async function (req: Request, res: Response, next?: NextFunction) { - const invalid = () => { - throw new Invalid(); - } - try { - let { login, special } = req.cookies - if (!login) invalid() - - let token = await LoginToken.findOne({ token: login, valid: true }) - if (!await CheckToken(token, validated)) invalid(); - - let user = await User.findById(token.user); - if (!user) { - token.valid = false; - await LoginToken.save(token); - invalid(); - } - - let special_token; - if (special) { - Logging.debug("Special found") - special_token = await LoginToken.findOne({ token: special, special: true, valid: true, user: token.user }) - if (!await CheckToken(special_token, validated)) - invalid(); - req.special = true; - } - - if (special_required && !req.special) invalid(); - - req.user = user - req.isAdmin = user.admin; - req.token = { - login: token, - special: special_token - } - - if (next) - next() - return true; - } catch (e) { - if (e instanceof Invalid) { - if (req.method === "GET" && !json) { - res.status(HttpStatusCode.UNAUTHORIZED) - res.redirect("/login?base64=true&state=" + Buffer.from(redirect_uri ? redirect_uri : req.originalUrl).toString("base64")) - } else { - throw new RequestError(req.__("You are not logged in or your login is expired"), HttpStatusCode.UNAUTHORIZED) - } - } else { - if (next) next(e); - else throw e; - } - return false; - } - }); -} - +import { NextFunction, Request, Response } from "express"; +import LoginToken, { CheckToken } from "../../models/login_token"; +import Logging from "@hibas123/nodelogging"; +import RequestError, { HttpStatusCode } from "../../helper/request_error"; +import User from "../../models/user"; +import promiseMiddleware from "../../helper/promiseMiddleware"; + +class Invalid extends Error { } + +/** + * Returns customized Middleware function, that could also be called directly + * by code and will return true or false depending on the token. In the false + * case it will also send error and redirect if json is not set + * @param json Default false. Checks if requests wants an json or html for returning errors + * @param special_required Default false. If true, a special token is required + * @param redirect_uri Default current uri. Sets the uri to redirect, if json is not set and user not logged in + * @param validated Default true. If false, the token must not be validated + */ +export function GetUserMiddleware(json = false, special_required: boolean = false, redirect_uri?: string, validated = true) { + return promiseMiddleware(async function (req: Request, res: Response, next?: NextFunction) { + const invalid = (message: string) => { + throw new Invalid(req.__(message)); + } + try { + let { login, special } = req.cookies + if (!login) { + login = req.query.login; + special = req.query.special; + } + if (!login) invalid("No login token") + if (!special && special_required) invalid("No special token") + + let token = await LoginToken.findOne({ token: login, valid: true }) + if (!await CheckToken(token, validated)) invalid("Login token invalid"); + + let user = await User.findById(token.user); + if (!user) { + token.valid = false; + await LoginToken.save(token); + invalid("Login token invalid"); + } + + let special_token; + if (special) { + Logging.debug("Special found") + special_token = await LoginToken.findOne({ token: special, special: true, valid: true, user: token.user }) + if (!await CheckToken(special_token, validated)) + invalid("Special token invalid"); + req.special = true; + } + + req.user = user + req.isAdmin = user.admin; + req.token = { + login: token, + special: special_token + } + + if (next) + next() + return true; + } catch (e) { + if (e instanceof Invalid) { + if (req.method === "GET" && !json) { + res.status(HttpStatusCode.UNAUTHORIZED) + res.redirect("/login?base64=true&state=" + Buffer.from(redirect_uri ? redirect_uri : req.originalUrl).toString("base64")) + } else { + throw new RequestError(req.__("You are not logged in or your login is expired" + ` (${e.message})`), HttpStatusCode.UNAUTHORIZED, undefined, { auth: true }) + } + } else { + if (next) next(e); + else throw e; + } + return false; + } + }); +} + export const UserMiddleware = GetUserMiddleware(); \ No newline at end of file diff --git a/src/api/user/account.ts b/src/api/user/account.ts new file mode 100644 index 0000000..6dfb385 --- /dev/null +++ b/src/api/user/account.ts @@ -0,0 +1,16 @@ +import { Request, Response } from "express"; +import Stacker from "../middlewares/stacker"; +import { GetUserMiddleware } from "../middlewares/user"; +import LoginToken, { CheckToken } from "../../models/login_token"; +import RequestError, { HttpStatusCode } from "../../helper/request_error"; + +export const GetAccount = Stacker(GetUserMiddleware(true, true), async (req: Request, res: Response) => { + let user = { + id: req.user.uid, + name: req.user.name, + username: req.user.username, + birthday: req.user.birthday, + gender: req.user.gender, + }; + res.json({ user }); +}); diff --git a/src/api/user/index.ts b/src/api/user/index.ts index 4f58b6b..f3a359a 100644 --- a/src/api/user/index.ts +++ b/src/api/user/index.ts @@ -1,95 +1,113 @@ -import { Router } from "express"; -import Register from "./register"; -import Login from "./login"; -import TwoFactorRoute from "./twofactor"; -import { GetToken, DeleteToken } from "./token"; - -const UserRoute: Router = Router(); - -/** - * @api {post} /user/register - * @apiName UserRegister - * - * @apiGroup user - * @apiPermission none - * - * @apiParam {String} mail EMail linked to this Account - * @apiParam {String} username The new Username - * @apiParam {String} password Password hashed and salted like specification - * @apiParam {String} salt The Salt used for password hashing - * @apiParam {String} regcode The regcode, that should be used - * @apiParam {String} gender Gender can be: "male", "female", "other", "none" - * @apiParam {String} name The real name of the User - * - * @apiSuccess {Boolean} success - * - * @apiErrorExample {Object} Error-Response: - { - error: [ - { - message: "Some Error", - field: "username" - } - ], - status: 400 - } - */ -UserRoute.post("/register", Register); - -/** - * @api {post} /user/login?type=:type - * @apiName UserLogin - * - * @apiParam {String} type Type could be either "username" or "password" - * - * @apiGroup user - * @apiPermission none - * - * @apiParam {String} username Username (either username or uid required) - * @apiParam {String} uid (either username or uid required) - * @apiParam {String} password Password hashed and salted like specification (only on type password) - * - * @apiSuccess {String} uid On type = "username" - * @apiSuccess {String} salt On type = "username" - * - * @apiSuccess {String} login On type = "password". Login Token - * @apiSuccess {String} special On type = "password". Special Token - * @apiSuccess {Object[]} tfa Will be set when TwoFactorAuthentication is required - * @apiSuccess {String} tfa.id The ID of the TFA Method - * @apiSuccess {String} tfa.name The name of the TFA Method - * @apiSuccess {String} tfa.type The type of the TFA Method - */ -UserRoute.post("/login", Login) -UserRoute.use("/twofactor", TwoFactorRoute); - -/** - * @api {get} /user/token - * @apiName UserGetToken - * - * @apiGroup user - * @apiPermission user - * - * @apiSuccess {Object[]} token - * @apiSuccess {String} token.id The Token ID - * @apiSuccess {String} token.special Identifies Special Token - * @apiSuccess {String} token.ip IP the token was optained from - * @apiSuccess {String} token.browser The Browser the token was optained from (User Agent) - * @apiSuccess {Boolean} token.isthis Shows if it is token used by this session - */ -UserRoute.get("/token", GetToken); - -/** - * @api {delete} /user/token/:id - * @apiParam {String} id The id of the token to be deleted - * - * @apiName UserDeleteToken - * - * @apiParam {String} type Type could be either "username" or "password" - * - * @apiGroup user - * @apiPermission user - * - * @apiSuccess {Boolean} success - */ -UserRoute.delete("/token/:id", DeleteToken); +import { Router } from "express"; +import Register from "./register"; +import Login from "./login"; +import TwoFactorRoute from "./twofactor"; +import { GetToken, DeleteToken } from "./token"; +import { GetAccount } from "./account"; + +const UserRoute: Router = Router(); + +/** + * @api {post} /user/register + * @apiName UserRegister + * + * @apiGroup user + * @apiPermission none + * + * @apiParam {String} mail EMail linked to this Account + * @apiParam {String} username The new Username + * @apiParam {String} password Password hashed and salted like specification + * @apiParam {String} salt The Salt used for password hashing + * @apiParam {String} regcode The regcode, that should be used + * @apiParam {String} gender Gender can be: "male", "female", "other", "none" + * @apiParam {String} name The real name of the User + * + * @apiSuccess {Boolean} success + * + * @apiErrorExample {Object} Error-Response: + { + error: [ + { + message: "Some Error", + field: "username" + } + ], + status: 400 + } + */ +UserRoute.post("/register", Register); + +/** + * @api {post} /user/login?type=:type + * @apiName UserLogin + * + * @apiParam {String} type Type could be either "username" or "password" + * + * @apiGroup user + * @apiPermission none + * + * @apiParam {String} username Username (either username or uid required) + * @apiParam {String} uid (either username or uid required) + * @apiParam {String} password Password hashed and salted like specification (only on type password) + * + * @apiSuccess {String} uid On type = "username" + * @apiSuccess {String} salt On type = "username" + * + * @apiSuccess {String} login On type = "password". Login Token + * @apiSuccess {String} special On type = "password". Special Token + * @apiSuccess {Object[]} tfa Will be set when TwoFactorAuthentication is required + * @apiSuccess {String} tfa.id The ID of the TFA Method + * @apiSuccess {String} tfa.name The name of the TFA Method + * @apiSuccess {String} tfa.type The type of the TFA Method + */ +UserRoute.post("/login", Login) +UserRoute.use("/twofactor", TwoFactorRoute); + +/** + * @api {get} /user/token + * @apiName UserGetToken + * + * @apiGroup user + * @apiPermission user + * + * @apiSuccess {Object[]} token + * @apiSuccess {String} token.id The Token ID + * @apiSuccess {String} token.special Identifies Special Token + * @apiSuccess {String} token.ip IP the token was optained from + * @apiSuccess {String} token.browser The Browser the token was optained from (User Agent) + * @apiSuccess {Boolean} token.isthis Shows if it is token used by this session + */ +UserRoute.get("/token", GetToken); + +/** + * @api {delete} /user/token/:id + * @apiParam {String} id The id of the token to be deleted + * + * @apiName UserDeleteToken + * + * + * @apiGroup user + * @apiPermission user + * + * @apiSuccess {Boolean} success + */ +UserRoute.delete("/token/:id", DeleteToken); + + +/** + * @api {delete} /user/account + * @apiName UserGetAccount + * + * @apiGroup user + * @apiPermission user + * + * @apiSuccess {Boolean} success + * @apiSuccess {Object[]} user + * @apiSuccess {String} user.id User ID + * @apiSuccess {String} token.name Full name of the user + * @apiSuccess {String} token.username Username of user + * @apiSuccess {Date} token.birthday Birthday + * @apiSuccess {Number} token.gender Gender of user (none = 0, male = 1, female = 2, other = 3) + */ +UserRoute.get("/account", GetAccount); export default UserRoute; \ No newline at end of file diff --git a/src/api/user/login.ts b/src/api/user/login.ts index 81d086c..9d09697 100644 --- a/src/api/user/login.ts +++ b/src/api/user/login.ts @@ -1,98 +1,96 @@ -import { Request, Response } from "express" -import User, { IUser } from "../../models/user"; -import { randomBytes } from "crypto"; -import moment = require("moment"); -import LoginToken from "../../models/login_token"; -import promiseMiddleware from "../../helper/promiseMiddleware"; -import TwoFactor from "../../models/twofactor"; - -const Login = promiseMiddleware(async (req: Request, res: Response) => { - let type = req.query.type; - if (type === "username") { - let { username, uid } = req.query; - let user = await User.findOne(username ? { username: username.toLowerCase() } : { uid: uid }); - if (!user) { - res.json({ error: req.__("User not found") }) - } else { - res.json({ salt: user.salt, uid: user.uid }); - } - return; - } else if (type === "password") { - const sendToken = async (user: IUser, tfa?: any[]) => { - let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress - let client = { - ip: Array.isArray(ip) ? ip[0] : ip, - browser: req.headers["user-agent"] - } - - let token_str = randomBytes(16).toString("hex"); - let tfa_exp = moment().add(5, "minutes").toDate() - let token_exp = moment().add(6, "months").toDate() - let token = LoginToken.new({ - token: token_str, - valid: true, - validTill: tfa ? tfa_exp : token_exp, - user: user._id, - validated: tfa ? false : true, - ...client - }); - await LoginToken.save(token); - - let special_str = randomBytes(24).toString("hex"); - let special_exp = moment().add(30, "minutes").toDate() - let special = LoginToken.new({ - token: special_str, - valid: true, - validTill: tfa ? tfa_exp : special_exp, - special: true, - user: user._id, - validated: tfa ? false : true, - ...client - }); - await LoginToken.save(special); - - res.json({ - login: { token: token_str, expires: token.validTill.toUTCString() }, - special: { token: special_str, expires: special.validTill.toUTCString() }, - tfa - }); - } - - - - let { username, password, uid } = req.body; - - let user = await User.findOne(username ? { username: username.toLowerCase() } : { uid: uid }) - if (!user) { - res.json({ error: req.__("User not found") }) - } else { - if (user.password !== password) { - res.json({ error: req.__("Password or username wrong") }) - } else { - let twofactor = await TwoFactor.find({ user: user._id, valid: true }) - let expired = twofactor.filter(e => e.expires ? moment().isAfter(moment(e.expires)) : false) - await Promise.all(expired.map(e => { - e.valid = false; - return TwoFactor.save(e); - })); - twofactor = twofactor.filter(e => e.valid); - if (twofactor && twofactor.length > 0) { - let tfa = twofactor.map(e => { - return { - id: e._id, - name: e.name, - type: e.type - } - }) - await sendToken(user, tfa); - } else { - await sendToken(user); - } - } - } - } else { - res.json({ error: req.__("Invalid type!") }); - } -}); - +import { Request, Response } from "express" +import User, { IUser } from "../../models/user"; +import { randomBytes } from "crypto"; +import moment = require("moment"); +import LoginToken from "../../models/login_token"; +import promiseMiddleware from "../../helper/promiseMiddleware"; +import TwoFactor, { TFATypes, TFANames } from "../../models/twofactor"; + +const Login = promiseMiddleware(async (req: Request, res: Response) => { + let type = req.query.type; + if (type === "username") { + let { username, uid } = req.query; + let user = await User.findOne(username ? { username: username.toLowerCase() } : { uid: uid }); + if (!user) { + res.json({ error: req.__("User not found") }) + } else { + res.json({ salt: user.salt, uid: user.uid }); + } + return; + } else if (type === "password") { + const sendToken = async (user: IUser, tfa?: any[]) => { + let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress + let client = { + ip: Array.isArray(ip) ? ip[0] : ip, + browser: req.headers["user-agent"] + } + + let token_str = randomBytes(16).toString("hex"); + let tfa_exp = moment().add(5, "minutes").toDate() + let token_exp = moment().add(6, "months").toDate() + let token = LoginToken.new({ + token: token_str, + valid: true, + validTill: tfa ? tfa_exp : token_exp, + user: user._id, + validated: tfa ? false : true, + ...client + }); + await LoginToken.save(token); + + let special_str = randomBytes(24).toString("hex"); + let special_exp = moment().add(30, "minutes").toDate() + let special = LoginToken.new({ + token: special_str, + valid: true, + validTill: tfa ? tfa_exp : special_exp, + special: true, + user: user._id, + validated: tfa ? false : true, + ...client + }); + await LoginToken.save(special); + + res.json({ + login: { token: token_str, expires: token.validTill.toUTCString() }, + special: { token: special_str, expires: special.validTill.toUTCString() }, + tfa + }); + } + + let { username, password, uid } = req.body; + + let user = await User.findOne(username ? { username: username.toLowerCase() } : { uid: uid }) + if (!user) { + res.json({ error: req.__("User not found") }) + } else { + if (user.password !== password) { + res.json({ error: req.__("Password or username wrong") }) + } else { + let twofactor = await TwoFactor.find({ user: user._id, valid: true }) + let expired = twofactor.filter(e => e.expires ? moment().isAfter(moment(e.expires)) : false) + await Promise.all(expired.map(e => { + e.valid = false; + return TwoFactor.save(e); + })); + twofactor = twofactor.filter(e => e.valid); + if (twofactor && twofactor.length > 0) { + let tfa = twofactor.map(e => { + return { + id: e._id, + name: e.name || TFANames.get(e.type), + type: e.type + } + }) + await sendToken(user, tfa); + } else { + await sendToken(user); + } + } + } + } else { + res.json({ error: req.__("Invalid type!") }); + } +}); + export default Login; \ No newline at end of file diff --git a/src/api/user/token.ts b/src/api/user/token.ts index ea39bc1..76dc47b 100644 --- a/src/api/user/token.ts +++ b/src/api/user/token.ts @@ -1,29 +1,29 @@ -import { Request, Response } from "express"; -import Stacker from "../middlewares/stacker"; -import { GetUserMiddleware } from "../middlewares/user"; -import LoginToken, { CheckToken } from "../../models/login_token"; -import RequestError, { HttpStatusCode } from "../../helper/request_error"; - -export const GetToken = Stacker(GetUserMiddleware(true, true), async (req: Request, res: Response) => { - let raw_token = await LoginToken.find({ user: req.user._id, valid: true }); - let token = await Promise.all(raw_token.map(async token => { - await CheckToken(token); - return { - id: token._id, - special: token.special, - ip: token.ip, - browser: token.browser, - isthis: token._id.equals(token.special ? req.token.special._id : req.token.login._id) - } - }).filter(t => t !== undefined)); - res.json({ token }); -}); - -export const DeleteToken = Stacker(GetUserMiddleware(true, true), async (req: Request, res: Response) => { - let { id } = req.query; - let token = await LoginToken.findById(id); - if (!token || !token.user.equals(req.user._id)) throw new RequestError("Invalid ID", HttpStatusCode.BAD_REQUEST); - token.valid = false; - await LoginToken.save(token); - res.json({ success: true }); +import { Request, Response } from "express"; +import Stacker from "../middlewares/stacker"; +import { GetUserMiddleware } from "../middlewares/user"; +import LoginToken, { CheckToken } from "../../models/login_token"; +import RequestError, { HttpStatusCode } from "../../helper/request_error"; + +export const GetToken = Stacker(GetUserMiddleware(true, true), async (req: Request, res: Response) => { + let raw_token = await LoginToken.find({ user: req.user._id, valid: true }); + let token = await Promise.all(raw_token.map(async token => { + await CheckToken(token); + return { + id: token._id, + special: token.special, + ip: token.ip, + browser: token.browser, + isthis: token._id.equals(token.special ? req.token.special._id : req.token.login._id) + } + }).filter(t => t !== undefined)); + res.json({ token }); +}); + +export const DeleteToken = Stacker(GetUserMiddleware(true, true), async (req: Request, res: Response) => { + let { id } = req.params; + let token = await LoginToken.findById(id); + if (!token || !token.user.equals(req.user._id)) throw new RequestError("Invalid ID", HttpStatusCode.BAD_REQUEST); + token.valid = false; + await LoginToken.save(token); + res.json({ success: true }); }); \ No newline at end of file diff --git a/src/api/user/twofactor/backup/index.ts b/src/api/user/twofactor/backup/index.ts new file mode 100644 index 0000000..231f45b --- /dev/null +++ b/src/api/user/twofactor/backup/index.ts @@ -0,0 +1,74 @@ +import { Router } from "express" +import Stacker from "../../../middlewares/stacker"; +import { GetUserMiddleware } from "../../../middlewares/user"; +import TwoFactor, { TFATypes as TwoFATypes, IBackupCode } from "../../../../models/twofactor"; +import RequestError, { HttpStatusCode } from "../../../../helper/request_error"; +import moment = require("moment"); +import { upgradeToken } from "../helper"; +import * as crypto from "crypto"; +import Logging from "@hibas123/nodelogging"; + +const BackupCodeRoute = Router(); + +// TODO: Further checks if this is good enough randomness +function generateCode(length: number) { + let bytes = crypto.randomBytes(length); + let nrs = ""; + bytes.forEach((b, idx) => { + let nr = Math.floor((b / 255) * 9.9999) + if (nr > 9) nr = 9; + nrs += String(nr); + }) + return nrs; +} + +BackupCodeRoute.post("/", Stacker(GetUserMiddleware(true, true), async (req, res) => { + //Generating new + let codes = Array(10).map(() => generateCode(8)); + console.log(codes); + let twofactor = TwoFactor.new({ + user: req.user._id, + type: TwoFATypes.OTC, + valid: true, + data: codes, + name: "" + }) + await TwoFactor.save(twofactor); + res.json({ + codes, + id: twofactor._id + }) +})); + +BackupCodeRoute.put("/", Stacker(GetUserMiddleware(true, false, undefined, false), async (req, res) => { + let { login, special } = req.token; + let { id, code }: { id: string, code: string } = req.body; + + let twofactor: IBackupCode = await TwoFactor.findById(id); + + if (!twofactor || !twofactor.valid || !twofactor.user.equals(req.user._id) || twofactor.type !== TwoFATypes.OTC) { + throw new RequestError("Invalid Method!", HttpStatusCode.BAD_REQUEST); + } + + if (twofactor.expires && moment().isAfter(twofactor.expires)) { + twofactor.valid = false; + await TwoFactor.save(twofactor); + throw new RequestError("Invalid Method!", HttpStatusCode.BAD_REQUEST); + } + code = code.replace(/\s/g, ""); + let valid = twofactor.data.find(c => c === code); + + if (valid) { + twofactor.data = twofactor.data.filter(c => c !== code); + await TwoFactor.save(twofactor); + let [login_exp, special_exp] = await Promise.all([ + upgradeToken(login), + upgradeToken(special) + ]); + res.json({ success: true, login_exp, special_exp }) + } else { + throw new RequestError("Invalid or already used code!", HttpStatusCode.BAD_REQUEST); + } +})) + +export default BackupCodeRoute; diff --git a/src/api/user/twofactor/helper.ts b/src/api/user/twofactor/helper.ts index e7c4f05..d2f8896 100644 --- a/src/api/user/twofactor/helper.ts +++ b/src/api/user/twofactor/helper.ts @@ -1,79 +1,13 @@ -import LoginToken, { ILoginToken } from "../../../models/login_token"; -import moment = require("moment"); - -// export async function unlockToken() { -// let { type, code, login, special } = req.body; - -// let [login_t, special_t] = await Promise.all([LoginToken.findOne({ token: login }), LoginToken.findOne({ token: special })]); - -// if ((login && !login_t) || (special && !special_t)) { -// res.json({ error: req.__("Token not found!") }); -// } else { -// let atoken = special_t || login_t; - -// let user = await User.findById(atoken.user); - -// let tf = await TwoFactor.find({ user: user._id, valid: true }) - -// let valid = false; -// switch (type) { -// case TokenTypes.OTC: { -// let twofactor = await TwoFactor.findOne({ type, valid: true }) -// if (twofactor) { -// valid = speakeasy.totp.verify({ -// secret: twofactor.token, -// encoding: "base64", -// token: code -// }) -// } -// break; -// } - -// case TokenTypes.BACKUP_CODE: { -// let twofactor = await TwoFactor.findOne({ type, valid: true, token: code }) -// if (twofactor) { -// twofactor.valid = false; -// await TwoFactor.save(twofactor); -// valid = true; -// } -// break; -// } -// case TokenTypes.APP_ALLOW: -// case TokenTypes.YUBI_KEY: -// default: -// res.json({ error: req.__("Invalid twofactor!") }); -// return; - -// } - -// if (!valid) { -// res.json({ error: req.__("Invalid code!") }); -// return; -// } - -// let result: any = {}; -// if (login_t) { -// login_t.validated = true -// await LoginToken.save(login_t) -// result.login = { token: login_t.token, expires: login_t.validTill.toUTCString() } -// } - -// if (special_t) { -// special_t.validated = true; -// await LoginToken.save(special_t); -// result.special = { token: special_t.token, expires: special_t.validTill.toUTCString() } -// } -// res.json(result); -// } -// } - -export async function upgradeToken(token: ILoginToken) { - token.data = undefined; - token.valid = true; - token.validated = true; - //TODO durations from config - let expires = (token.special ? moment().add(30, "minute") : moment().add(6, "months")).toDate(); - token.validTill = expires; - await LoginToken.save(token); - return expires; +import LoginToken, { ILoginToken } from "../../../models/login_token"; +import moment = require("moment"); + +export async function upgradeToken(token: ILoginToken) { + token.data = undefined; + token.valid = true; + token.validated = true; + //TODO durations from config + let expires = (token.special ? moment().add(30, "minute") : moment().add(6, "months")).toDate(); + token.validTill = expires; + await LoginToken.save(token); + return expires; } \ No newline at end of file diff --git a/src/api/user/twofactor/index.ts b/src/api/user/twofactor/index.ts index 41ab54a..e3d4a2c 100644 --- a/src/api/user/twofactor/index.ts +++ b/src/api/user/twofactor/index.ts @@ -1,42 +1,46 @@ -import { Router } from "express"; -import YubiKeyRoute from "./yubikey"; -import { GetUserMiddleware } from "../../middlewares/user"; -import Stacker from "../../middlewares/stacker"; -import TwoFactor from "../../../models/twofactor"; -import * as moment from "moment" -import RequestError, { HttpStatusCode } from "../../../helper/request_error"; - -const TwoFactorRouter = Router(); - -TwoFactorRouter.get("/", Stacker(GetUserMiddleware(true, true), async (req, res) => { - let twofactor = await TwoFactor.find({ user: req.user._id, valid: true }) - let expired = twofactor.filter(e => e.expires ? moment().isAfter(moment(e.expires)) : false) - await Promise.all(expired.map(e => { - e.valid = false; - return TwoFactor.save(e); - })); - twofactor = twofactor.filter(e => e.valid); - let tfa = twofactor.map(e => { - return { - id: e._id, - name: e.name, - type: e.type - } - }) - res.json({ methods: tfa }); -})); - -TwoFactorRouter.delete("/", Stacker(GetUserMiddleware(true, true), async (req, res) => { - let { id } = req.query; - let tfa = await TwoFactor.findById(id); - if (!tfa || !tfa.user.equals(req.user._id)) { - throw new RequestError("Invalid id", HttpStatusCode.BAD_REQUEST); - } - tfa.valid = false; - await TwoFactor.save(tfa); - res.json({ success: true }); -})); - -TwoFactorRouter.use("/yubikey", YubiKeyRoute); - +import { Router } from "express"; +import YubiKeyRoute from "./yubikey"; +import { GetUserMiddleware } from "../../middlewares/user"; +import Stacker from "../../middlewares/stacker"; +import TwoFactor from "../../../models/twofactor"; +import * as moment from "moment" +import RequestError, { HttpStatusCode } from "../../../helper/request_error"; +import OTCRoute from "./otc"; +import BackupCodeRoute from "./backup"; + +const TwoFactorRouter = Router(); + +TwoFactorRouter.get("/", Stacker(GetUserMiddleware(true, true), async (req, res) => { + let twofactor = await TwoFactor.find({ user: req.user._id, valid: true }) + let expired = twofactor.filter(e => e.expires ? moment().isAfter(moment(e.expires)) : false) + await Promise.all(expired.map(e => { + e.valid = false; + return TwoFactor.save(e); + })); + twofactor = twofactor.filter(e => e.valid); + let tfa = twofactor.map(e => { + return { + id: e._id, + name: e.name, + type: e.type + } + }) + res.json({ methods: tfa }); +})); + +TwoFactorRouter.delete("/:id", Stacker(GetUserMiddleware(true, true), async (req, res) => { + let { id } = req.params; + let tfa = await TwoFactor.findById(id); + if (!tfa || !tfa.user.equals(req.user._id)) { + throw new RequestError("Invalid id", HttpStatusCode.BAD_REQUEST); + } + tfa.valid = false; + await TwoFactor.save(tfa); + res.json({ success: true }); +})); + +TwoFactorRouter.use("/yubikey", YubiKeyRoute); +TwoFactorRouter.use("/otc", OTCRoute); +TwoFactorRouter.use("/backup", BackupCodeRoute); + export default TwoFactorRouter; \ No newline at end of file diff --git a/src/api/user/twofactor/otc/index.ts b/src/api/user/twofactor/otc/index.ts new file mode 100644 index 0000000..ad85ac7 --- /dev/null +++ b/src/api/user/twofactor/otc/index.ts @@ -0,0 +1,105 @@ +import { Router } from "express" +import Stacker from "../../../middlewares/stacker"; +import { GetUserMiddleware } from "../../../middlewares/user"; +import TwoFactor, { TFATypes as TwoFATypes, IOTC } from "../../../../models/twofactor"; +import RequestError, { HttpStatusCode } from "../../../../helper/request_error"; +import moment = require("moment"); +import { upgradeToken } from "../helper"; +import Logging from "@hibas123/nodelogging"; + +import * as speakeasy from "speakeasy"; +import * as qrcode from "qrcode"; +import config from "../../../../config"; + +const OTCRoute = Router(); + +OTCRoute.post("/", Stacker(GetUserMiddleware(true, true), async (req, res) => { + const { type } = req.query; + if (type === "create") { + //Generating new + let secret = speakeasy.generateSecret({ + name: config.core.name, + issuer: config.core.name + }); + let twofactor = TwoFactor.new({ + user: req.user._id, + type: TwoFATypes.OTC, + valid: false, + data: secret.base32 + }) + let dataurl = await qrcode.toDataURL(secret.otpauth_url); + await TwoFactor.save(twofactor); + res.json({ + image: dataurl, + id: twofactor._id + }) + } else if (type === "validate") { + // Checking code and marking as valid + const { code, id } = req.body; + Logging.debug(req.body, id); + let twofactor: IOTC = await TwoFactor.findById(id); + const err = () => { throw new RequestError("Invalid ID!", HttpStatusCode.BAD_REQUEST) }; + if (!twofactor || !twofactor.user.equals(req.user._id) || twofactor.type !== TwoFATypes.OTC || !twofactor.data || twofactor.valid) { + Logging.debug("Not found or wrong user", twofactor); + err(); + } + + if (twofactor.expires && moment().isAfter(moment(twofactor.expires))) { + await TwoFactor.delete(twofactor); + Logging.debug("Expired!", twofactor); + err(); + } + + let valid = speakeasy.totp.verify({ + secret: twofactor.data, + encoding: "base32", + token: code + }) + + if (valid) { + twofactor.expires = undefined; + twofactor.valid = true; + await TwoFactor.save(twofactor); + res.json({ success: true }); + } else { + throw new RequestError("Invalid Code!", HttpStatusCode.BAD_REQUEST); + } + } else { + throw new RequestError("Invalid type", HttpStatusCode.BAD_REQUEST); + } +})); + +OTCRoute.put("/", Stacker(GetUserMiddleware(true, false, undefined, false), async (req, res) => { + let { login, special } = req.token; + let { id, code } = req.body; + let twofactor: IOTC = await TwoFactor.findById(id); + + if (!twofactor || !twofactor.valid || !twofactor.user.equals(req.user._id) || twofactor.type !== TwoFATypes.OTC) { + throw new RequestError("Invalid Method!", HttpStatusCode.BAD_REQUEST); + } + + if (twofactor.expires && moment().isAfter(twofactor.expires)) { + twofactor.valid = false; + await TwoFactor.save(twofactor); + throw new RequestError("Invalid Method!", HttpStatusCode.BAD_REQUEST); + } + + + let valid = speakeasy.totp.verify({ + secret: twofactor.data, + encoding: "base32", + token: code + }) + + if (valid) { + let [login_exp, special_exp] = await Promise.all([ + upgradeToken(login), + upgradeToken(special) + ]); + res.json({ success: true, login_exp, special_exp }) + } else { + throw new RequestError("Invalid Code", HttpStatusCode.BAD_REQUEST); + } +})) + +export default OTCRoute; diff --git a/src/api/user/twofactor/yubikey/index.ts b/src/api/user/twofactor/yubikey/index.ts index c6e746b..981f3db 100644 --- a/src/api/user/twofactor/yubikey/index.ts +++ b/src/api/user/twofactor/yubikey/index.ts @@ -1,131 +1,134 @@ -import { Router, Request } from "express" -import Stacker from "../../../middlewares/stacker"; -import { UserMiddleware, GetUserMiddleware } from "../../../middlewares/user"; -import * as u2f from "u2f"; -import config from "../../../../config"; -import TwoFactor, { TFATypes as TwoFATypes, IYubiKey } from "../../../../models/twofactor"; -import RequestError, { HttpStatusCode } from "../../../../helper/request_error"; -import moment = require("moment"); -import LoginToken from "../../../../models/login_token"; -import { upgradeToken } from "../helper"; -import Logging from "@hibas123/nodelogging"; - -const U2FRoute = Router(); - -U2FRoute.post("/", Stacker(GetUserMiddleware(true, true), async (req, res) => { - const { type } = req.query; - if (type === "challenge") { - const registrationRequest = u2f.request(config.core.url); - - let twofactor = TwoFactor.new({ - user: req.user._id, - type: TwoFATypes.U2F, - valid: false, - data: { - registration: registrationRequest - } - }) - await TwoFactor.save(twofactor); - res.json({ request: registrationRequest, id: twofactor._id, appid: config.core.url }); - } else { - const { response, id } = req.body; - Logging.debug(req.body, id); - let twofactor: IYubiKey = await TwoFactor.findById(id); - const err = () => { throw new RequestError("Invalid ID!", HttpStatusCode.BAD_REQUEST) }; - if (!twofactor || !twofactor.user.equals(req.user._id) || twofactor.type !== TwoFATypes.U2F || !twofactor.data.registration || twofactor.valid) { - Logging.debug("Not found or wrong user", twofactor); - err(); - } - - if (twofactor.expires && moment().isAfter(moment(twofactor.expires))) { - await TwoFactor.delete(twofactor); - Logging.debug("Expired!", twofactor); - err(); - } - - const result = u2f.checkRegistration(twofactor.data.registration, response); - - if (result.successful) { - twofactor.data = { - keyHandle: result.keyHandle, - publicKey: result.publicKey - } - twofactor.expires = undefined; - twofactor.valid = true; - await TwoFactor.save(twofactor); - res.json({ success: true }); - } else { - throw new RequestError(result.errorMessage, HttpStatusCode.BAD_REQUEST); - } - } -})); - -U2FRoute.get("/", Stacker(GetUserMiddleware(true, false, undefined, false), async (req, res) => { - let { login, special } = req.token; - let twofactor: IYubiKey = await TwoFactor.findOne({ user: req.user._id, type: TwoFATypes.U2F, valid: true }) - - if (!twofactor) { - throw new RequestError("Invalid Method!", HttpStatusCode.BAD_REQUEST); - } - - if (twofactor.expires) { - if (moment().isAfter(twofactor.expires)) { - twofactor.valid = false; - await TwoFactor.save(twofactor); - throw new RequestError("Invalid Method!", HttpStatusCode.BAD_REQUEST); - } - } - - let request = u2f.request(config.core.url, twofactor.data.keyHandle); - login.data = { - type: "ykr", - request - }; - let r;; - if (special) { - special.data = login.data; - r = LoginToken.save(special); - } - - await Promise.all([r, LoginToken.save(login)]); - res.json({ request }); -})) - -U2FRoute.put("/", Stacker(GetUserMiddleware(true, false, undefined, false), async (req, res) => { - let { login, special } = req.token; - let twofactor: IYubiKey = await TwoFactor.findOne({ user: req.user._id, type: TwoFATypes.U2F, valid: true }) - - - let { response } = req.body; - if (!twofactor || !login.data || login.data.type !== "ykr" || special && (!special.data || special.data.type !== "ykr")) { - throw new RequestError("Invalid Method!", HttpStatusCode.BAD_REQUEST); - } - - if (twofactor.expires && moment().isAfter(twofactor.expires)) { - twofactor.valid = false; - await TwoFactor.save(twofactor); - throw new RequestError("Invalid Method!", HttpStatusCode.BAD_REQUEST); - } - - let login_exp; - let special_exp; - let result = u2f.checkSignature(login.data.request, response, twofactor.data.publicKey); - if (result.successful) { - if (special) { - let result = u2f.checkSignature(special.data.request, response, twofactor.data.publicKey); - if (result.successful) { - special_exp = await upgradeToken(special); - } - else { - throw new RequestError(result.errorMessage, HttpStatusCode.BAD_REQUEST); - } - } - login_exp = await upgradeToken(login); - } - else { - throw new RequestError(result.errorMessage, HttpStatusCode.BAD_REQUEST); - } - res.json({ success: true, login_exp, special_exp }) -})) - -export default U2FRoute; +import { Router, Request } from "express" +import Stacker from "../../../middlewares/stacker"; +import { UserMiddleware, GetUserMiddleware } from "../../../middlewares/user"; +import * as u2f from "u2f"; +import config from "../../../../config"; +import TwoFactor, { TFATypes as TwoFATypes, IYubiKey } from "../../../../models/twofactor"; +import RequestError, { HttpStatusCode } from "../../../../helper/request_error"; +import moment = require("moment"); +import LoginToken from "../../../../models/login_token"; +import { upgradeToken } from "../helper"; +import Logging from "@hibas123/nodelogging"; + +const U2FRoute = Router(); + +/** + * Registerinf a new YubiKey + */ +U2FRoute.post("/", Stacker(GetUserMiddleware(true, true), async (req, res) => { + const { type } = req.query; + if (type === "challenge") { + const registrationRequest = u2f.request(config.core.url); + + let twofactor = TwoFactor.new({ + user: req.user._id, + type: TwoFATypes.U2F, + valid: false, + data: { + registration: registrationRequest + } + }) + await TwoFactor.save(twofactor); + res.json({ request: registrationRequest, id: twofactor._id, appid: config.core.url }); + } else { + const { response, id } = req.body; + Logging.debug(req.body, id); + let twofactor: IYubiKey = await TwoFactor.findById(id); + const err = () => { throw new RequestError("Invalid ID!", HttpStatusCode.BAD_REQUEST) }; + if (!twofactor || !twofactor.user.equals(req.user._id) || twofactor.type !== TwoFATypes.U2F || !twofactor.data.registration || twofactor.valid) { + Logging.debug("Not found or wrong user", twofactor); + err(); + } + + if (twofactor.expires && moment().isAfter(moment(twofactor.expires))) { + await TwoFactor.delete(twofactor); + Logging.debug("Expired!", twofactor); + err(); + } + + const result = u2f.checkRegistration(twofactor.data.registration, response); + + if (result.successful) { + twofactor.data = { + keyHandle: result.keyHandle, + publicKey: result.publicKey + } + twofactor.expires = undefined; + twofactor.valid = true; + await TwoFactor.save(twofactor); + res.json({ success: true }); + } else { + throw new RequestError(result.errorMessage, HttpStatusCode.BAD_REQUEST); + } + } +})); + +U2FRoute.get("/", Stacker(GetUserMiddleware(true, false, undefined, false), async (req, res) => { + let { login, special } = req.token; + let twofactor: IYubiKey = await TwoFactor.findOne({ user: req.user._id, type: TwoFATypes.U2F, valid: true }) + + if (!twofactor) { + throw new RequestError("Invalid Method!", HttpStatusCode.BAD_REQUEST); + } + + if (twofactor.expires) { + if (moment().isAfter(twofactor.expires)) { + twofactor.valid = false; + await TwoFactor.save(twofactor); + throw new RequestError("Invalid Method!", HttpStatusCode.BAD_REQUEST); + } + } + + let request = u2f.request(config.core.url, twofactor.data.keyHandle); + login.data = { + type: "ykr", + request + }; + let r;; + if (special) { + special.data = login.data; + r = LoginToken.save(special); + } + + await Promise.all([r, LoginToken.save(login)]); + res.json({ request }); +})) + +U2FRoute.put("/", Stacker(GetUserMiddleware(true, false, undefined, false), async (req, res) => { + let { login, special } = req.token; + let twofactor: IYubiKey = await TwoFactor.findOne({ user: req.user._id, type: TwoFATypes.U2F, valid: true }) + + + let { response } = req.body; + if (!twofactor || !login.data || login.data.type !== "ykr" || special && (!special.data || special.data.type !== "ykr")) { + throw new RequestError("Invalid Method!", HttpStatusCode.BAD_REQUEST); + } + + if (twofactor.expires && moment().isAfter(twofactor.expires)) { + twofactor.valid = false; + await TwoFactor.save(twofactor); + throw new RequestError("Invalid Method!", HttpStatusCode.BAD_REQUEST); + } + + let login_exp; + let special_exp; + let result = u2f.checkSignature(login.data.request, response, twofactor.data.publicKey); + if (result.successful) { + if (special) { + let result = u2f.checkSignature(special.data.request, response, twofactor.data.publicKey); + if (result.successful) { + special_exp = await upgradeToken(special); + } + else { + throw new RequestError(result.errorMessage, HttpStatusCode.BAD_REQUEST); + } + } + login_exp = await upgradeToken(login); + } + else { + throw new RequestError(result.errorMessage, HttpStatusCode.BAD_REQUEST); + } + res.json({ success: true, login_exp, special_exp }) +})) + +export default U2FRoute; diff --git a/src/helper/request_error.ts b/src/helper/request_error.ts index 504a15e..8bf38c9 100644 --- a/src/helper/request_error.ts +++ b/src/helper/request_error.ts @@ -1,388 +1,388 @@ - -/** - * Hypertext Transfer Protocol (HTTP) response status codes. - * @see {@link https://en.wikipedia.org/wiki/List_of_HTTP_status_codes} - */ -export enum HttpStatusCode { - - /** - * The server has received the request headers and the client should proceed to send the request body - * (in the case of a request for which a body needs to be sent; for example, a POST request). - * Sending a large request body to a server after a request has been rejected for inappropriate headers would be inefficient. - * To have a server check the request's headers, a client must send Expect: 100-continue as a header in its initial request - * and receive a 100 Continue status code in response before sending the body. The response 417 Expectation Failed indicates the request should not be continued. - */ - CONTINUE = 100, - - /** - * The requester has asked the server to switch protocols and the server has agreed to do so. - */ - SWITCHING_PROTOCOLS = 101, - - /** - * A WebDAV request may contain many sub-requests involving file operations, requiring a long time to complete the request. - * This code indicates that the server has received and is processing the request, but no response is available yet. - * This prevents the client from timing out and assuming the request was lost. - */ - PROCESSING = 102, - - /** - * Standard response for successful HTTP requests. - * The actual response will depend on the request method used. - * In a GET request, the response will contain an entity corresponding to the requested resource. - * In a POST request, the response will contain an entity describing or containing the result of the action. - */ - OK = 200, - - /** - * The request has been fulfilled, resulting in the creation of a new resource. - */ - CREATED = 201, - - /** - * The request has been accepted for processing, but the processing has not been completed. - * The request might or might not be eventually acted upon, and may be disallowed when processing occurs. - */ - ACCEPTED = 202, - - /** - * SINCE HTTP/1.1 - * The server is a transforming proxy that received a 200 OK from its origin, - * but is returning a modified version of the origin's response. - */ - NON_AUTHORITATIVE_INFORMATION = 203, - - /** - * The server successfully processed the request and is not returning any content. - */ - NO_CONTENT = 204, - - /** - * The server successfully processed the request, but is not returning any content. - * Unlike a 204 response, this response requires that the requester reset the document view. - */ - RESET_CONTENT = 205, - - /** - * The server is delivering only part of the resource (byte serving) due to a range header sent by the client. - * The range header is used by HTTP clients to enable resuming of interrupted downloads, - * or split a download into multiple simultaneous streams. - */ - PARTIAL_CONTENT = 206, - - /** - * The message body that follows is an XML message and can contain a number of separate response codes, - * depending on how many sub-requests were made. - */ - MULTI_STATUS = 207, - - /** - * The members of a DAV binding have already been enumerated in a preceding part of the (multistatus) response, - * and are not being included again. - */ - ALREADY_REPORTED = 208, - - /** - * The server has fulfilled a request for the resource, - * and the response is a representation of the result of one or more instance-manipulations applied to the current instance. - */ - IM_USED = 226, - - /** - * Indicates multiple options for the resource from which the client may choose (via agent-driven content negotiation). - * For example, this code could be used to present multiple video format options, - * to list files with different filename extensions, or to suggest word-sense disambiguation. - */ - MULTIPLE_CHOICES = 300, - - /** - * This and all future requests should be directed to the given URI. - */ - MOVED_PERMANENTLY = 301, - - /** - * This is an example of industry practice contradicting the standard. - * The HTTP/1.0 specification (RFC 1945) required the client to perform a temporary redirect - * (the original describing phrase was "Moved Temporarily"), but popular browsers implemented 302 - * with the functionality of a 303 See Other. Therefore, HTTP/1.1 added status codes 303 and 307 - * to distinguish between the two behaviours. However, some Web applications and frameworks - * use the 302 status code as if it were the 303. - */ - FOUND = 302, - - /** - * SINCE HTTP/1.1 - * The response to the request can be found under another URI using a GET method. - * When received in response to a POST (or PUT/DELETE), the client should presume that - * the server has received the data and should issue a redirect with a separate GET message. - */ - SEE_OTHER = 303, - - /** - * Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-None-Match. - * In such case, there is no need to retransmit the resource since the client still has a previously-downloaded copy. - */ - NOT_MODIFIED = 304, - - /** - * SINCE HTTP/1.1 - * The requested resource is available only through a proxy, the address for which is provided in the response. - * Many HTTP clients (such as Mozilla and Internet Explorer) do not correctly handle responses with this status code, primarily for security reasons. - */ - USE_PROXY = 305, - - /** - * No longer used. Originally meant "Subsequent requests should use the specified proxy." - */ - SWITCH_PROXY = 306, - - /** - * SINCE HTTP/1.1 - * In this case, the request should be repeated with another URI; however, future requests should still use the original URI. - * In contrast to how 302 was historically implemented, the request method is not allowed to be changed when reissuing the original request. - * For example, a POST request should be repeated using another POST request. - */ - TEMPORARY_REDIRECT = 307, - - /** - * The request and all future requests should be repeated using another URI. - * 307 and 308 parallel the behaviors of 302 and 301, but do not allow the HTTP method to change. - * So, for example, submitting a form to a permanently redirected resource may continue smoothly. - */ - PERMANENT_REDIRECT = 308, - - /** - * The server cannot or will not process the request due to an apparent client error - * (e.g., malformed request syntax, too large size, invalid request message framing, or deceptive request routing). - */ - BAD_REQUEST = 400, - - /** - * Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet - * been provided. The response must include a WWW-Authenticate header field containing a challenge applicable to the - * requested resource. See Basic access authentication and Digest access authentication. 401 semantically means - * "unauthenticated",i.e. the user does not have the necessary credentials. - */ - UNAUTHORIZED = 401, - - /** - * Reserved for future use. The original intention was that this code might be used as part of some form of digital - * cash or micro payment scheme, but that has not happened, and this code is not usually used. - * Google Developers API uses this status if a particular developer has exceeded the daily limit on requests. - */ - PAYMENT_REQUIRED = 402, - - /** - * The request was valid, but the server is refusing action. - * The user might not have the necessary permissions for a resource. - */ - FORBIDDEN = 403, - - /** - * The requested resource could not be found but may be available in the future. - * Subsequent requests by the client are permissible. - */ - NOT_FOUND = 404, - - /** - * A request method is not supported for the requested resource; - * for example, a GET request on a form that requires data to be presented via POST, or a PUT request on a read-only resource. - */ - METHOD_NOT_ALLOWED = 405, - - /** - * The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request. - */ - NOT_ACCEPTABLE = 406, - - /** - * The client must first authenticate itself with the proxy. - */ - PROXY_AUTHENTICATION_REQUIRED = 407, - - /** - * The server timed out waiting for the request. - * According to HTTP specifications: - * "The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time." - */ - REQUEST_TIMEOUT = 408, - - /** - * Indicates that the request could not be processed because of conflict in the request, - * such as an edit conflict between multiple simultaneous updates. - */ - CONFLICT = 409, - - /** - * Indicates that the resource requested is no longer available and will not be available again. - * This should be used when a resource has been intentionally removed and the resource should be purged. - * Upon receiving a 410 status code, the client should not request the resource in the future. - * Clients such as search engines should remove the resource from their indices. - * Most use cases do not require clients and search engines to purge the resource, and a "404 Not Found" may be used instead. - */ - GONE = 410, - - /** - * The request did not specify the length of its content, which is required by the requested resource. - */ - LENGTH_REQUIRED = 411, - - /** - * The server does not meet one of the preconditions that the requester put on the request. - */ - PRECONDITION_FAILED = 412, - - /** - * The request is larger than the server is willing or able to process. Previously called "Request Entity Too Large". - */ - PAYLOAD_TOO_LARGE = 413, - - /** - * The URI provided was too long for the server to process. Often the result of too much data being encoded as a query-string of a GET request, - * in which case it should be converted to a POST request. - * Called "Request-URI Too Long" previously. - */ - URI_TOO_LONG = 414, - - /** - * The request entity has a media type which the server or resource does not support. - * For example, the client uploads an image as image/svg+xml, but the server requires that images use a different format. - */ - UNSUPPORTED_MEDIA_TYPE = 415, - - /** - * The client has asked for a portion of the file (byte serving), but the server cannot supply that portion. - * For example, if the client asked for a part of the file that lies beyond the end of the file. - * Called "Requested Range Not Satisfiable" previously. - */ - RANGE_NOT_SATISFIABLE = 416, - - /** - * The server cannot meet the requirements of the Expect request-header field. - */ - EXPECTATION_FAILED = 417, - - /** - * This code was defined in 1998 as one of the traditional IETF April Fools' jokes, in RFC 2324, Hyper Text Coffee Pot Control Protocol, - * and is not expected to be implemented by actual HTTP servers. The RFC specifies this code should be returned by - * teapots requested to brew coffee. This HTTP status is used as an Easter egg in some websites, including Google.com. - */ - I_AM_A_TEAPOT = 418, - - /** - * The request was directed at a server that is not able to produce a response (for example because a connection reuse). - */ - MISDIRECTED_REQUEST = 421, - - /** - * The request was well-formed but was unable to be followed due to semantic errors. - */ - UNPROCESSABLE_ENTITY = 422, - - /** - * The resource that is being accessed is locked. - */ - LOCKED = 423, - - /** - * The request failed due to failure of a previous request (e.g., a PROPPATCH). - */ - FAILED_DEPENDENCY = 424, - - /** - * The client should switch to a different protocol such as TLS/1.0, given in the Upgrade header field. - */ - UPGRADE_REQUIRED = 426, - - /** - * The origin server requires the request to be conditional. - * Intended to prevent "the 'lost update' problem, where a client - * GETs a resource's state, modifies it, and PUTs it back to the server, - * when meanwhile a third party has modified the state on the server, leading to a conflict." - */ - PRECONDITION_REQUIRED = 428, - - /** - * The user has sent too many requests in a given amount of time. Intended for use with rate-limiting schemes. - */ - TOO_MANY_REQUESTS = 429, - - /** - * The server is unwilling to process the request because either an individual header field, - * or all the header fields collectively, are too large. - */ - REQUEST_HEADER_FIELDS_TOO_LARGE = 431, - - /** - * A server operator has received a legal demand to deny access to a resource or to a set of resources - * that includes the requested resource. The code 451 was chosen as a reference to the novel Fahrenheit 451. - */ - UNAVAILABLE_FOR_LEGAL_REASONS = 451, - - /** - * A generic error message, given when an unexpected condition was encountered and no more specific message is suitable. - */ - INTERNAL_SERVER_ERROR = 500, - - /** - * The server either does not recognize the request method, or it lacks the ability to fulfill the request. - * Usually this implies future availability (e.g., a new feature of a web-service API). - */ - NOT_IMPLEMENTED = 501, - - /** - * The server was acting as a gateway or proxy and received an invalid response from the upstream server. - */ - BAD_GATEWAY = 502, - - /** - * The server is currently unavailable (because it is overloaded or down for maintenance). - * Generally, this is a temporary state. - */ - SERVICE_UNAVAILABLE = 503, - - /** - * The server was acting as a gateway or proxy and did not receive a timely response from the upstream server. - */ - GATEWAY_TIMEOUT = 504, - - /** - * The server does not support the HTTP protocol version used in the request - */ - HTTP_VERSION_NOT_SUPPORTED = 505, - - /** - * Transparent content negotiation for the request results in a circular reference. - */ - VARIANT_ALSO_NEGOTIATES = 506, - - /** - * The server is unable to store the representation needed to complete the request. - */ - INSUFFICIENT_STORAGE = 507, - - /** - * The server detected an infinite loop while processing the request. - */ - LOOP_DETECTED = 508, - - /** - * Further extensions to the request are required for the server to fulfill it. - */ - NOT_EXTENDED = 510, - - /** - * The client needs to authenticate to gain network access. - * Intended for use by intercepting proxies used to control access to the network (e.g., "captive portals" used - * to require agreement to Terms of Service before granting full Internet access via a Wi-Fi hotspot). - */ - NETWORK_AUTHENTICATION_REQUIRED = 511 -} - - -export default class RequestError extends Error { - constructor(message: any, public status: HttpStatusCode, public nolog: boolean = false) { - super("") - this.message = message; - } + +/** + * Hypertext Transfer Protocol (HTTP) response status codes. + * @see {@link https://en.wikipedia.org/wiki/List_of_HTTP_status_codes} + */ +export enum HttpStatusCode { + + /** + * The server has received the request headers and the client should proceed to send the request body + * (in the case of a request for which a body needs to be sent; for example, a POST request). + * Sending a large request body to a server after a request has been rejected for inappropriate headers would be inefficient. + * To have a server check the request's headers, a client must send Expect: 100-continue as a header in its initial request + * and receive a 100 Continue status code in response before sending the body. The response 417 Expectation Failed indicates the request should not be continued. + */ + CONTINUE = 100, + + /** + * The requester has asked the server to switch protocols and the server has agreed to do so. + */ + SWITCHING_PROTOCOLS = 101, + + /** + * A WebDAV request may contain many sub-requests involving file operations, requiring a long time to complete the request. + * This code indicates that the server has received and is processing the request, but no response is available yet. + * This prevents the client from timing out and assuming the request was lost. + */ + PROCESSING = 102, + + /** + * Standard response for successful HTTP requests. + * The actual response will depend on the request method used. + * In a GET request, the response will contain an entity corresponding to the requested resource. + * In a POST request, the response will contain an entity describing or containing the result of the action. + */ + OK = 200, + + /** + * The request has been fulfilled, resulting in the creation of a new resource. + */ + CREATED = 201, + + /** + * The request has been accepted for processing, but the processing has not been completed. + * The request might or might not be eventually acted upon, and may be disallowed when processing occurs. + */ + ACCEPTED = 202, + + /** + * SINCE HTTP/1.1 + * The server is a transforming proxy that received a 200 OK from its origin, + * but is returning a modified version of the origin's response. + */ + NON_AUTHORITATIVE_INFORMATION = 203, + + /** + * The server successfully processed the request and is not returning any content. + */ + NO_CONTENT = 204, + + /** + * The server successfully processed the request, but is not returning any content. + * Unlike a 204 response, this response requires that the requester reset the document view. + */ + RESET_CONTENT = 205, + + /** + * The server is delivering only part of the resource (byte serving) due to a range header sent by the client. + * The range header is used by HTTP clients to enable resuming of interrupted downloads, + * or split a download into multiple simultaneous streams. + */ + PARTIAL_CONTENT = 206, + + /** + * The message body that follows is an XML message and can contain a number of separate response codes, + * depending on how many sub-requests were made. + */ + MULTI_STATUS = 207, + + /** + * The members of a DAV binding have already been enumerated in a preceding part of the (multistatus) response, + * and are not being included again. + */ + ALREADY_REPORTED = 208, + + /** + * The server has fulfilled a request for the resource, + * and the response is a representation of the result of one or more instance-manipulations applied to the current instance. + */ + IM_USED = 226, + + /** + * Indicates multiple options for the resource from which the client may choose (via agent-driven content negotiation). + * For example, this code could be used to present multiple video format options, + * to list files with different filename extensions, or to suggest word-sense disambiguation. + */ + MULTIPLE_CHOICES = 300, + + /** + * This and all future requests should be directed to the given URI. + */ + MOVED_PERMANENTLY = 301, + + /** + * This is an example of industry practice contradicting the standard. + * The HTTP/1.0 specification (RFC 1945) required the client to perform a temporary redirect + * (the original describing phrase was "Moved Temporarily"), but popular browsers implemented 302 + * with the functionality of a 303 See Other. Therefore, HTTP/1.1 added status codes 303 and 307 + * to distinguish between the two behaviours. However, some Web applications and frameworks + * use the 302 status code as if it were the 303. + */ + FOUND = 302, + + /** + * SINCE HTTP/1.1 + * The response to the request can be found under another URI using a GET method. + * When received in response to a POST (or PUT/DELETE), the client should presume that + * the server has received the data and should issue a redirect with a separate GET message. + */ + SEE_OTHER = 303, + + /** + * Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-None-Match. + * In such case, there is no need to retransmit the resource since the client still has a previously-downloaded copy. + */ + NOT_MODIFIED = 304, + + /** + * SINCE HTTP/1.1 + * The requested resource is available only through a proxy, the address for which is provided in the response. + * Many HTTP clients (such as Mozilla and Internet Explorer) do not correctly handle responses with this status code, primarily for security reasons. + */ + USE_PROXY = 305, + + /** + * No longer used. Originally meant "Subsequent requests should use the specified proxy." + */ + SWITCH_PROXY = 306, + + /** + * SINCE HTTP/1.1 + * In this case, the request should be repeated with another URI; however, future requests should still use the original URI. + * In contrast to how 302 was historically implemented, the request method is not allowed to be changed when reissuing the original request. + * For example, a POST request should be repeated using another POST request. + */ + TEMPORARY_REDIRECT = 307, + + /** + * The request and all future requests should be repeated using another URI. + * 307 and 308 parallel the behaviors of 302 and 301, but do not allow the HTTP method to change. + * So, for example, submitting a form to a permanently redirected resource may continue smoothly. + */ + PERMANENT_REDIRECT = 308, + + /** + * The server cannot or will not process the request due to an apparent client error + * (e.g., malformed request syntax, too large size, invalid request message framing, or deceptive request routing). + */ + BAD_REQUEST = 400, + + /** + * Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet + * been provided. The response must include a WWW-Authenticate header field containing a challenge applicable to the + * requested resource. See Basic access authentication and Digest access authentication. 401 semantically means + * "unauthenticated",i.e. the user does not have the necessary credentials. + */ + UNAUTHORIZED = 401, + + /** + * Reserved for future use. The original intention was that this code might be used as part of some form of digital + * cash or micro payment scheme, but that has not happened, and this code is not usually used. + * Google Developers API uses this status if a particular developer has exceeded the daily limit on requests. + */ + PAYMENT_REQUIRED = 402, + + /** + * The request was valid, but the server is refusing action. + * The user might not have the necessary permissions for a resource. + */ + FORBIDDEN = 403, + + /** + * The requested resource could not be found but may be available in the future. + * Subsequent requests by the client are permissible. + */ + NOT_FOUND = 404, + + /** + * A request method is not supported for the requested resource; + * for example, a GET request on a form that requires data to be presented via POST, or a PUT request on a read-only resource. + */ + METHOD_NOT_ALLOWED = 405, + + /** + * The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request. + */ + NOT_ACCEPTABLE = 406, + + /** + * The client must first authenticate itself with the proxy. + */ + PROXY_AUTHENTICATION_REQUIRED = 407, + + /** + * The server timed out waiting for the request. + * According to HTTP specifications: + * "The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time." + */ + REQUEST_TIMEOUT = 408, + + /** + * Indicates that the request could not be processed because of conflict in the request, + * such as an edit conflict between multiple simultaneous updates. + */ + CONFLICT = 409, + + /** + * Indicates that the resource requested is no longer available and will not be available again. + * This should be used when a resource has been intentionally removed and the resource should be purged. + * Upon receiving a 410 status code, the client should not request the resource in the future. + * Clients such as search engines should remove the resource from their indices. + * Most use cases do not require clients and search engines to purge the resource, and a "404 Not Found" may be used instead. + */ + GONE = 410, + + /** + * The request did not specify the length of its content, which is required by the requested resource. + */ + LENGTH_REQUIRED = 411, + + /** + * The server does not meet one of the preconditions that the requester put on the request. + */ + PRECONDITION_FAILED = 412, + + /** + * The request is larger than the server is willing or able to process. Previously called "Request Entity Too Large". + */ + PAYLOAD_TOO_LARGE = 413, + + /** + * The URI provided was too long for the server to process. Often the result of too much data being encoded as a query-string of a GET request, + * in which case it should be converted to a POST request. + * Called "Request-URI Too Long" previously. + */ + URI_TOO_LONG = 414, + + /** + * The request entity has a media type which the server or resource does not support. + * For example, the client uploads an image as image/svg+xml, but the server requires that images use a different format. + */ + UNSUPPORTED_MEDIA_TYPE = 415, + + /** + * The client has asked for a portion of the file (byte serving), but the server cannot supply that portion. + * For example, if the client asked for a part of the file that lies beyond the end of the file. + * Called "Requested Range Not Satisfiable" previously. + */ + RANGE_NOT_SATISFIABLE = 416, + + /** + * The server cannot meet the requirements of the Expect request-header field. + */ + EXPECTATION_FAILED = 417, + + /** + * This code was defined in 1998 as one of the traditional IETF April Fools' jokes, in RFC 2324, Hyper Text Coffee Pot Control Protocol, + * and is not expected to be implemented by actual HTTP servers. The RFC specifies this code should be returned by + * teapots requested to brew coffee. This HTTP status is used as an Easter egg in some websites, including Google.com. + */ + I_AM_A_TEAPOT = 418, + + /** + * The request was directed at a server that is not able to produce a response (for example because a connection reuse). + */ + MISDIRECTED_REQUEST = 421, + + /** + * The request was well-formed but was unable to be followed due to semantic errors. + */ + UNPROCESSABLE_ENTITY = 422, + + /** + * The resource that is being accessed is locked. + */ + LOCKED = 423, + + /** + * The request failed due to failure of a previous request (e.g., a PROPPATCH). + */ + FAILED_DEPENDENCY = 424, + + /** + * The client should switch to a different protocol such as TLS/1.0, given in the Upgrade header field. + */ + UPGRADE_REQUIRED = 426, + + /** + * The origin server requires the request to be conditional. + * Intended to prevent "the 'lost update' problem, where a client + * GETs a resource's state, modifies it, and PUTs it back to the server, + * when meanwhile a third party has modified the state on the server, leading to a conflict." + */ + PRECONDITION_REQUIRED = 428, + + /** + * The user has sent too many requests in a given amount of time. Intended for use with rate-limiting schemes. + */ + TOO_MANY_REQUESTS = 429, + + /** + * The server is unwilling to process the request because either an individual header field, + * or all the header fields collectively, are too large. + */ + REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + + /** + * A server operator has received a legal demand to deny access to a resource or to a set of resources + * that includes the requested resource. The code 451 was chosen as a reference to the novel Fahrenheit 451. + */ + UNAVAILABLE_FOR_LEGAL_REASONS = 451, + + /** + * A generic error message, given when an unexpected condition was encountered and no more specific message is suitable. + */ + INTERNAL_SERVER_ERROR = 500, + + /** + * The server either does not recognize the request method, or it lacks the ability to fulfill the request. + * Usually this implies future availability (e.g., a new feature of a web-service API). + */ + NOT_IMPLEMENTED = 501, + + /** + * The server was acting as a gateway or proxy and received an invalid response from the upstream server. + */ + BAD_GATEWAY = 502, + + /** + * The server is currently unavailable (because it is overloaded or down for maintenance). + * Generally, this is a temporary state. + */ + SERVICE_UNAVAILABLE = 503, + + /** + * The server was acting as a gateway or proxy and did not receive a timely response from the upstream server. + */ + GATEWAY_TIMEOUT = 504, + + /** + * The server does not support the HTTP protocol version used in the request + */ + HTTP_VERSION_NOT_SUPPORTED = 505, + + /** + * Transparent content negotiation for the request results in a circular reference. + */ + VARIANT_ALSO_NEGOTIATES = 506, + + /** + * The server is unable to store the representation needed to complete the request. + */ + INSUFFICIENT_STORAGE = 507, + + /** + * The server detected an infinite loop while processing the request. + */ + LOOP_DETECTED = 508, + + /** + * Further extensions to the request are required for the server to fulfill it. + */ + NOT_EXTENDED = 510, + + /** + * The client needs to authenticate to gain network access. + * Intended for use by intercepting proxies used to control access to the network (e.g., "captive portals" used + * to require agreement to Terms of Service before granting full Internet access via a Wi-Fi hotspot). + */ + NETWORK_AUTHENTICATION_REQUIRED = 511 +} + + +export default class RequestError extends Error { + constructor(message: any, public status: HttpStatusCode, public nolog: boolean = false, public additional: any = undefined) { + super("") + this.message = message; + } } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index af13718..53f991f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,73 +1,79 @@ -import Logging from "@hibas123/nodelogging"; -import config from "./config"; - -import NLS from "@hibas123/nodeloggingserver_client"; -if (config.logging) { - let s = NLS(Logging, config.logging.server, config.logging.appid, config.logging.token); - s.send(`[${new Date().toLocaleTimeString()}] Starting application`); -} - -// if (!config.database) { -// Logging.error("No database config set. Terminating.") -// process.exit(); -// } - -if (!config.web) { - Logging.error("No web config set. Terminating.") - process.exit(); -} - -import * as i18n from "i18n" -i18n.configure({ - locales: ["en", "de"], - directory: "./locales", -}) - -import Web from "./web"; -import TestData from "./testdata"; -import DB from "./database"; - -Logging.log("Connecting to Database") -if (config.dev) { - Logging.warning("Running in dev mode! Database will be cleared!") -} -DB.connect().then(async () => { - Logging.log("Database connected") - if (config.dev) - await TestData() - let web = new Web(config.web) - web.listen() - function print(path, layer) { - if (layer.route) { - layer.route.stack.forEach(print.bind(null, path.concat(split(layer.route.path)))) - } else if (layer.name === 'router' && layer.handle.stack) { - layer.handle.stack.forEach(print.bind(null, path.concat(split(layer.regexp)))) - } else if (layer.method) { - let me: string = layer.method.toUpperCase(); - me += " ".repeat(6 - me.length); - Logging.log(`${me} /${path.concat(split(layer.regexp)).filter(Boolean).join('/')}`); - } - } - - function split(thing) { - if (typeof thing === 'string') { - return thing.split('/') - } else if (thing.fast_slash) { - return '' - } else { - var match = thing.toString() - .replace('\\/?', '') - .replace('(?=\\/|$)', '$') - .match(/^\/\^((?:\\[.*+?^${}()|[\]\\\/]|[^.*+?^${}()|[\]\\\/])*)\$\//) - return match - ? match[1].replace(/\\(.)/g, '$1').split('/') - : '' - } - } - Logging.log("--- Endpoints: ---"); - web.server._router.stack.forEach(print.bind(null, [])) - Logging.log("--- Endpoints end ---") -}).catch(e => { - Logging.error(e) - process.exit(); +import Logging from "@hibas123/nodelogging"; +import config from "./config"; + +import NLS from "@hibas123/nodeloggingserver_client"; +if (config.logging) { + let s = NLS(Logging, config.logging.server, config.logging.appid, config.logging.token); + s.send(`[${new Date().toLocaleTimeString()}] Starting application`); +} + +// if (!config.database) { +// Logging.error("No database config set. Terminating.") +// process.exit(); +// } + +if (!config.web) { + Logging.error("No web config set. Terminating.") + process.exit(); +} + +import * as i18n from "i18n" +i18n.configure({ + locales: ["en", "de"], + directory: "./locales", +}) + +import Web from "./web"; +import TestData from "./testdata"; +import DB from "./database"; + +Logging.log("Connecting to Database") +if (config.dev) { + Logging.warning("Running in dev mode! Database will be cleared!") +} +DB.connect().then(async () => { + Logging.log("Database connected") + if (config.dev) + await TestData() + let web = new Web(config.web) + web.listen() + + let already = new Set(); + function print(path, layer) { + if (layer.route) { + layer.route.stack.forEach(print.bind(null, path.concat(split(layer.route.path)))) + } else if (layer.name === 'router' && layer.handle.stack) { + layer.handle.stack.forEach(print.bind(null, path.concat(split(layer.regexp)))) + } else if (layer.method) { + let me: string = layer.method.toUpperCase(); + me += " ".repeat(6 - me.length); + let msg = `${me} /${path.concat(split(layer.regexp)).filter(Boolean).join('/')}`; + if (!already.has(msg)) { + already.add(msg); + Logging.log(msg); + } + } + } + + function split(thing) { + if (typeof thing === 'string') { + return thing.split('/') + } else if (thing.fast_slash) { + return '' + } else { + var match = thing.toString() + .replace('\\/?', '') + .replace('(?=\\/|$)', '$') + .match(/^\/\^((?:\\[.*+?^${}()|[\]\\\/]|[^.*+?^${}()|[\]\\\/])*)\$\//) + return match + ? match[1].replace(/\\(.)/g, '$1').split('/') + : '' + } + } + // Logging.log("--- Endpoints: ---"); + // web.server._router.stack.forEach(print.bind(null, [])) + // Logging.log("--- Endpoints end ---") +}).catch(e => { + Logging.error(e) + process.exit(); }) \ No newline at end of file diff --git a/src/models/login_token.ts b/src/models/login_token.ts index 2b3f57c..36f37d2 100644 --- a/src/models/login_token.ts +++ b/src/models/login_token.ts @@ -1,65 +1,67 @@ -import DB from "../database"; -import { ModelDataBase } from "@hibas123/safe_mongo/lib/model"; -import { ObjectID } from "mongodb"; -import moment = require("moment"); - -export interface ILoginToken extends ModelDataBase { - token: string; - special: boolean; - user: ObjectID; - validTill: Date; - valid: boolean; - validated: boolean; - data: any; - ip: string; - browser: string; -} -const LoginToken = DB.addModel({ - name: "login_token", - versions: [{ - migration: () => { }, - schema: { - token: { type: String }, - special: { type: Boolean, default: () => false }, - user: { type: ObjectID }, - validTill: { type: Date }, - valid: { type: Boolean } - } - }, { - migration: (doc: ILoginToken) => { doc.validated = true; }, - schema: { - token: { type: String }, - special: { type: Boolean, default: () => false }, - user: { type: ObjectID }, - validTill: { type: Date }, - valid: { type: Boolean }, - validated: { type: Boolean, default: false } - } - }, { - migration: (doc: ILoginToken) => { doc.validated = true; }, - schema: { - token: { type: String }, - special: { type: Boolean, default: () => false }, - user: { type: ObjectID }, - validTill: { type: Date }, - valid: { type: Boolean }, - validated: { type: Boolean, default: false }, - data: { type: "any", optional: true }, - ip: { type: String, optional: true }, - browser: { type: String, optional: true } - } - }] -}) - -export async function CheckToken(token: ILoginToken, validated: boolean = true): Promise { - if (!token || !token.valid) return false; - if (validated && !token.validated) return false; - if (moment().isAfter(token.validTill)) { - token.valid = false; - await LoginToken.save(token) - return false; - } - return true; -} - +import DB from "../database"; +import { ModelDataBase } from "@hibas123/safe_mongo/lib/model"; +import { ObjectID } from "mongodb"; +import moment = require("moment"); + +export interface ILoginToken extends ModelDataBase { + token: string; + special: boolean; + user: ObjectID; + validTill: Date; + valid: boolean; + validated: boolean; + data: any; + ip: string; + browser: string; +} +const LoginToken = DB.addModel({ + name: "login_token", + versions: [{ + migration: () => { }, + schema: { + token: { type: String }, + special: { type: Boolean, default: () => false }, + user: { type: ObjectID }, + validTill: { type: Date }, + valid: { type: Boolean } + } + }, { + migration: (doc: ILoginToken) => { doc.validated = true; }, + schema: { + token: { type: String }, + special: { type: Boolean, default: () => false }, + user: { type: ObjectID }, + validTill: { type: Date }, + valid: { type: Boolean }, + validated: { type: Boolean, default: false } + } + }, { + migration: (doc: ILoginToken) => { doc.validated = true; }, + schema: { + token: { type: String }, + special: { type: Boolean, default: () => false }, + user: { type: ObjectID }, + validTill: { type: Date }, + valid: { type: Boolean }, + validated: { type: Boolean, default: false }, + data: { type: "any", optional: true }, + ip: { type: String, optional: true }, + browser: { type: String, optional: true } + } + }] +}) + +export async function CheckToken(token: ILoginToken, validated: boolean = true): Promise { + if (!token || !token.valid) + return false; + if (validated && !token.validated) + return false; + if (moment().isAfter(token.validTill)) { + token.valid = false; + await LoginToken.save(token) + return false; + } + return true; +} + export default LoginToken; \ No newline at end of file diff --git a/src/models/twofactor.ts b/src/models/twofactor.ts index 257f8ea..76abbca 100644 --- a/src/models/twofactor.ts +++ b/src/models/twofactor.ts @@ -1,57 +1,67 @@ -import DB from "../database"; -import { ModelDataBase } from "@hibas123/safe_mongo/lib/model"; -import { ObjectID } from "bson"; - -export enum TFATypes { - OTC, - BACKUP_CODE, - U2F, - APP_ALLOW -} - -export interface ITwoFactor extends ModelDataBase { - user: ObjectID - valid: boolean - expires?: Date; - name?: string; - type: TFATypes - data: any; -} - -export interface IOTP extends ITwoFactor { - data: string; -} - -export interface IYubiKey extends ITwoFactor { - data: { - registration?: any; - publicKey: string; - keyHandle: string; - } -} - -export interface IU2F extends ITwoFactor { - data: { - challenge?: string; - publicKey: string; - keyHandle: string; - registration?: string; - } -} - -const TwoFactor = DB.addModel({ - name: "twofactor", - versions: [{ - migration: (e) => { }, - schema: { - user: { type: ObjectID }, - valid: { type: Boolean }, - expires: { type: Date, optional: true }, - name: { type: String, optional: true }, - type: { type: Number }, - data: { type: "any" }, - } - }] -}); - +import DB from "../database"; +import { ModelDataBase } from "@hibas123/safe_mongo/lib/model"; +import { ObjectID } from "bson"; + +export enum TFATypes { + OTC, + BACKUP_CODE, + U2F, + APP_ALLOW +} + +export const TFANames = new Map(); +TFANames.set(TFATypes.OTC, "Authenticator"); +TFANames.set(TFATypes.BACKUP_CODE, "Backup Codes"); +TFANames.set(TFATypes.U2F, "Security Key (U2F)"); +TFANames.set(TFATypes.APP_ALLOW, "App Push"); + +export interface ITwoFactor extends ModelDataBase { + user: ObjectID + valid: boolean + expires?: Date; + name?: string; + type: TFATypes + data: any; +} + +export interface IOTC extends ITwoFactor { + data: string; +} + +export interface IYubiKey extends ITwoFactor { + data: { + registration?: any; + publicKey: string; + keyHandle: string; + } +} + +export interface IU2F extends ITwoFactor { + data: { + challenge?: string; + publicKey: string; + keyHandle: string; + registration?: string; + } +} + +export interface IBackupCode extends ITwoFactor { + data: string[]; +} + +const TwoFactor = DB.addModel({ + name: "twofactor", + versions: [{ + migration: (e) => { }, + schema: { + user: { type: ObjectID }, + valid: { type: Boolean }, + expires: { type: Date, optional: true }, + name: { type: String, optional: true }, + type: { type: Number }, + data: { type: "any" }, + } + }] +}); + export default TwoFactor; \ No newline at end of file diff --git a/src/testdata.ts b/src/testdata.ts index d887e8e..40287ac 100644 --- a/src/testdata.ts +++ b/src/testdata.ts @@ -1,80 +1,123 @@ -import User, { Gender } from "./models/user"; -import Client from "./models/client"; -import { Logging } from "@hibas123/nodelogging"; -import RegCode from "./models/regcodes"; -import * as moment from "moment"; -import Permission from "./models/permissions"; -import { ObjectID } from "bson"; -import DB from "./database"; -import TwoFactor from "./models/twofactor"; - -export default async function TestData() { - await DB.db.dropDatabase(); - let u = await User.findOne({ username: "test" }); - if (!u) { - Logging.log("Adding test user"); - u = User.new({ - username: "test", - birthday: new Date(), - gender: Gender.male, - name: "Test Test", - password: "125d6d03b32c84d492747f79cf0bf6e179d287f341384eb5d6d3197525ad6be8e6df0116032935698f99a09e265073d1d6c32c274591bf1d0a20ad67cba921bc", - salt: "test", - admin: true - }) - await User.save(u); - } - - let c = await Client.findOne({ client_id: "test001" }); - if (!c) { - Logging.log("Adding test client") - c = Client.new({ - client_id: "test001", - client_secret: "test001", - internal: true, - maintainer: u._id, - name: "Test Client", - website: "http://example.com", - redirect_url: "http://example.com" - }) - await Client.save(c); - } - - let perm = await Permission.findOne({ id: 0 }); - if (!perm) { - Logging.log("Adding test permission") - perm = Permission.new({ - _id: new ObjectID("507f1f77bcf86cd799439011"), - name: "TestPerm", - description: "Permission just for testing purposes", - client: c._id - }) - Permission.save(perm); - } - - let r = await RegCode.findOne({ token: "test" }); - if (!r) { - Logging.log("Adding test reg_code") - r = RegCode.new({ - token: "test", - valid: true, - validTill: moment().add("1", "year").toDate() - }) - await RegCode.save(r); - } - - let t = await TwoFactor.findOne({ user: u._id, type: 2 }) - if (!t) { - t = TwoFactor.new({ - user: u._id, - type: 2, - valid: true, - data: { - keyHandle: "tWSaMoHX2E96CoZOKOi_4aj6WVEh1e46FKXN0oDY2Z-laNOFcATlStNDo52HX7ygupW-v9qZOCX3J4d5nhOzWQ", - publicKey: "BPsgBxR8M7MyrknlFuvYZv0Z1lZxiJQJNrLDA1yi3XKD_lrhIpnAh2OY_TsFjASvn3JTtwlCh62QdMvN-ejQL78" - }, - expires: null - }) - TwoFactor.save(t); - } +import User, { Gender } from "./models/user"; +import Client from "./models/client"; +import { Logging } from "@hibas123/nodelogging"; +import RegCode from "./models/regcodes"; +import * as moment from "moment"; +import Permission from "./models/permissions"; +import { ObjectID } from "bson"; +import DB from "./database"; +import TwoFactor from "./models/twofactor"; + + +import * as speakeasy from "speakeasy"; +import LoginToken from "./models/login_token"; +import { log } from "handlebars"; + +export default async function TestData() { + await DB.db.dropDatabase(); + let u = await User.findOne({ username: "test" }); + if (!u) { + Logging.log("Adding test user"); + u = User.new({ + username: "test", + birthday: new Date(), + gender: Gender.male, + name: "Test Test", + password: "125d6d03b32c84d492747f79cf0bf6e179d287f341384eb5d6d3197525ad6be8e6df0116032935698f99a09e265073d1d6c32c274591bf1d0a20ad67cba921bc", + salt: "test", + admin: true + }) + await User.save(u); + } + + let c = await Client.findOne({ client_id: "test001" }); + if (!c) { + Logging.log("Adding test client") + c = Client.new({ + client_id: "test001", + client_secret: "test001", + internal: true, + maintainer: u._id, + name: "Test Client", + website: "http://example.com", + redirect_url: "http://example.com" + }) + await Client.save(c); + } + + let perm = await Permission.findOne({ id: 0 }); + if (!perm) { + Logging.log("Adding test permission") + perm = Permission.new({ + _id: new ObjectID("507f1f77bcf86cd799439011"), + name: "TestPerm", + description: "Permission just for testing purposes", + client: c._id + }) + Permission.save(perm); + } + + let r = await RegCode.findOne({ token: "test" }); + if (!r) { + Logging.log("Adding test reg_code") + r = RegCode.new({ + token: "test", + valid: true, + validTill: moment().add("1", "year").toDate() + }) + await RegCode.save(r); + } + + let t = await TwoFactor.findOne({ user: u._id, type: 0 }) + if (!t) { + t = TwoFactor.new({ + user: u._id, + type: 0, + valid: true, + data: "IIRW2P2UJRDDO2LDIRYW4LSREZLWMOKDNBJES2LLHRREK3R6KZJQ", + expires: null + }) + TwoFactor.save(t); + } + + let login_token = await LoginToken.findOne({ token: "test01" }); + if (login_token) + await LoginToken.delete(login_token); + + login_token = LoginToken.new({ + browser: "DEMO", + ip: "10.0.0.1", + special: false, + token: "test01", + valid: true, + validTill: moment().add("10", "years").toDate(), + user: u._id, + validated: true + }); + await LoginToken.save(login_token); + + let special_token = await LoginToken.findOne({ token: "test02" }); + if (special_token) + await LoginToken.delete(special_token); + + special_token = LoginToken.new({ + browser: "DEMO", + ip: "10.0.0.1", + special: true, + token: "test02", + valid: true, + validTill: moment().add("10", "years").toDate(), + user: u._id, + validated: true + }); + await LoginToken.save(special_token); + + + // setInterval(() => { + // let code = speakeasy.totp({ + // secret: t.data, + // encoding: "base32" + // }) + // Logging.debug("OTC Code is:", code); + // }, 1000) } \ No newline at end of file diff --git a/src/web.ts b/src/web.ts index 04675da..6538791 100644 --- a/src/web.ts +++ b/src/web.ts @@ -1,101 +1,101 @@ -import { WebConfig } from "./config"; -import * as express from "express" -import { Express } from "express" - -import Logging from "@hibas123/nodelogging" - -import * as bodyparser from "body-parser"; -import * as cookieparser from "cookie-parser" - -import * as i18n from "i18n" -import * as compression from "compression"; -import ApiRouter from "./api"; -import ViewRouter from "./views/views"; -import RequestError, { HttpStatusCode } from "./helper/request_error"; - -export default class Web { - server: Express - private port: number - - constructor(config: WebConfig) { - this.server = express() - this.port = Number(config.port); - this.registerMiddleware() - this.registerEndpoints() - this.registerErrorHandler() - } - - listen() { - this.server.listen(this.port, () => { - Logging.log(`Server listening on port ${this.port}`) - }) - } - - private registerMiddleware() { - this.server.use(cookieparser()) - this.server.use(bodyparser.json(), bodyparser.urlencoded({ extended: true })) - this.server.use(i18n.init) - - //Logging Middleware - this.server.use((req, res, next) => { - let start = process.hrtime() - let finished = false; - let to = false; - let listener = () => { - if (finished) return; - finished = true; - let td = process.hrtime(start); - let time = !to ? (td[0] * 1e3 + td[1] / 1e6).toFixed(2) : "--.--"; - let resColor = ""; - if (res.statusCode >= 200 && res.statusCode < 300) resColor = "\x1b[32m" //Green - else if (res.statusCode === 304 || res.statusCode === 302) resColor = "\x1b[33m" - else if (res.statusCode >= 400 && res.statusCode < 500) resColor = "\x1b[36m" //Cyan - else if (res.statusCode >= 500 && res.statusCode < 600) resColor = "\x1b[31m" //Red - let m = req.method; - while (m.length < 4) m += " "; - Logging.log(`${m} ${req.originalUrl} ${req.language} ${resColor}${res.statusCode}\x1b[0m - ${time}ms`) - res.removeListener("finish", listener) - } - res.on("finish", listener) - setTimeout(() => { - to = true; - listener(); - }, 2000) - next() - }) - - this.server.use(compression({ - filter: (req, res) => { - if (req.headers['x-no-compression']) { - return false - } - return compression.filter(req, res) - } - })); - } - - private registerEndpoints() { - this.server.use("/api", ApiRouter); - this.server.use("/", ViewRouter) - } - - private registerErrorHandler() { - this.server.use((error, req: express.Request, res, next) => { - if (!(error instanceof RequestError)) { - error = new RequestError(error.message, error.status || HttpStatusCode.INTERNAL_SERVER_ERROR, error.nolog || false); - } - - if (error.status === 500 && !(error).nolog) { - Logging.error(error); - } else { - Logging.log(typeof error.message === "string" ? error.message.split("\n", 1)[0] : error.message); - } - - if (req.accepts(["json"])) { - res.json_status = error.status || 500; - res.json({ error: error.message, status: error.status || 500 }) - } else - res.status(error.status || 500).send(error.message) - }) - } +import { WebConfig } from "./config"; +import * as express from "express" +import { Express } from "express" + +import Logging from "@hibas123/nodelogging" + +import * as bodyparser from "body-parser"; +import * as cookieparser from "cookie-parser" + +import * as i18n from "i18n" +import * as compression from "compression"; +import ApiRouter from "./api"; +import ViewRouter from "./views/views"; +import RequestError, { HttpStatusCode } from "./helper/request_error"; + +export default class Web { + server: Express + private port: number + + constructor(config: WebConfig) { + this.server = express() + this.port = Number(config.port); + this.registerMiddleware() + this.registerEndpoints() + this.registerErrorHandler() + } + + listen() { + this.server.listen(this.port, () => { + Logging.log(`Server listening on port ${this.port}`) + }) + } + + private registerMiddleware() { + this.server.use(cookieparser()) + this.server.use(bodyparser.json(), bodyparser.urlencoded({ extended: true })) + this.server.use(i18n.init) + + //Logging Middleware + this.server.use((req, res, next) => { + let start = process.hrtime() + let finished = false; + let to = false; + let listener = () => { + if (finished) return; + finished = true; + let td = process.hrtime(start); + let time = !to ? (td[0] * 1e3 + td[1] / 1e6).toFixed(2) : "--.--"; + let resColor = ""; + if (res.statusCode >= 200 && res.statusCode < 300) resColor = "\x1b[32m" //Green + else if (res.statusCode === 304 || res.statusCode === 302) resColor = "\x1b[33m" + else if (res.statusCode >= 400 && res.statusCode < 500) resColor = "\x1b[36m" //Cyan + else if (res.statusCode >= 500 && res.statusCode < 600) resColor = "\x1b[31m" //Red + let m = req.method; + while (m.length < 4) m += " "; + Logging.log(`${m} ${req.originalUrl} ${req.language} ${resColor}${res.statusCode}\x1b[0m - ${time}ms`) + res.removeListener("finish", listener) + } + res.on("finish", listener) + setTimeout(() => { + to = true; + listener(); + }, 2000) + next() + }) + + this.server.use(compression({ + filter: (req, res) => { + if (req.headers['x-no-compression']) { + return false + } + return compression.filter(req, res) + } + })); + } + + private registerEndpoints() { + this.server.use("/api", ApiRouter); + this.server.use("/", ViewRouter) + } + + private registerErrorHandler() { + this.server.use((error, req: express.Request, res, next) => { + if (!(error instanceof RequestError)) { + error = new RequestError(error.message, error.status || HttpStatusCode.INTERNAL_SERVER_ERROR, error.nolog || false); + } + + if (error.status === 500 && !(error).nolog) { + Logging.error(error); + } else { + Logging.log("Responded with Error:", typeof error.message === "string" ? error.message.split("\n", 1)[0] : error.message); + } + + if (req.accepts(["json"])) { + res.json_status = error.status || 500; + res.json({ error: error.message, status: error.status || 500, additional: error.additional }) + } else + res.status(error.status || 500).send(error.message) + }) + } } \ No newline at end of file