diff --git a/.gitignore b/.gitignore index 9d7995e..d19d68f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ logs/ *.sqlite yarn-error\.log config.ini -.env \ No newline at end of file +.env +doc/ \ No newline at end of file diff --git a/apidoc.json b/apidoc.json new file mode 100644 index 0000000..2ed2ebc --- /dev/null +++ b/apidoc.json @@ -0,0 +1,6 @@ +{ + "name": "openauth", + "description": "Open Auth REST API", + "title": "Open Auth REST", + "url": "/api" +} \ No newline at end of file diff --git a/locales/de.json b/locales/de.json index e80848a..28ad708 100644 --- a/locales/de.json +++ b/locales/de.json @@ -28,10 +28,10 @@ "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}} ist nicht deklariert", - "Field {{field}} has wrong type. It should be from type {{type}}": "Feld {{field}} hat den falschen Type. Es sollte vom Typ {{type}} sein", + "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ültiges Token", + "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 newline at end of file diff --git a/package-lock.json b/package-lock.json index 01d1d7b..7dcb890 100644 --- a/package-lock.json +++ b/package-lock.json @@ -385,6 +385,78 @@ } } }, + "apidoc": { + "version": "0.17.7", + "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.17.7.tgz", + "integrity": "sha512-9Wf4bRPwCuWOIOxR42dDnsXnFw+rhJg5VrMQK+KmNxJwyIh30UqX6gvjjXSG6YO74MqE87F18bbQXUENK9dPGg==", + "dev": true, + "requires": { + "apidoc-core": "~0.8.2", + "commander": "^2.19.0", + "fs-extra": "^7.0.0", + "lodash": "^4.17.10", + "markdown-it": "^8.3.1", + "winston": "^3.0.0" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + } + } + }, + "apidoc-core": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/apidoc-core/-/apidoc-core-0.8.3.tgz", + "integrity": "sha1-2dY1RYKd8lDSzKBJaDqH53U2S5Y=", + "dev": true, + "requires": { + "fs-extra": "^3.0.1", + "glob": "^7.1.1", + "iconv-lite": "^0.4.17", + "klaw-sync": "^2.1.0", + "lodash": "~4.17.4", + "semver": "~5.3.0" + }, + "dependencies": { + "fs-extra": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", + "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^3.0.0", + "universalify": "^0.1.0" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + } + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + } + } + }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -794,6 +866,16 @@ "object-visit": "^1.0.0" } }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -809,6 +891,38 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colornames": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", + "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=", + "dev": true + }, + "colors": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", + "dev": true + }, + "colorspace": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.1.tgz", + "integrity": "sha512-pI3btWyiuz7Ken0BWh9Elzsmv2bM9AhA7psXib4anUXy/orfZ/E0MbQwhSOG/9L8hLlalqrU0UhOuqxW1YjmVw==", + "dev": true, + "requires": { + "color": "3.0.x", + "text-hex": "1.0.x" + } + }, "commander": { "version": "2.17.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", @@ -1052,6 +1166,17 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "diagnostics": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", + "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", + "dev": true, + "requires": { + "colorspace": "1.1.x", + "enabled": "1.0.x", + "kuler": "1.0.x" + } + }, "dijkstrajs": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.1.tgz", @@ -1103,6 +1228,15 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "enabled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", + "dev": true, + "requires": { + "env-variable": "0.0.x" + } + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -1156,6 +1290,18 @@ "has-binary2": "~1.0.2" } }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "env-variable": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", + "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==", + "dev": true + }, "errlop": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/errlop/-/errlop-1.0.3.tgz", @@ -1410,6 +1556,18 @@ } } }, + "fast-safe-stringify": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz", + "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==", + "dev": true + }, + "fecha": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==", + "dev": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -1480,6 +1638,34 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "dependencies": { + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, "fsevents": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", @@ -2047,6 +2233,20 @@ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -2522,6 +2722,15 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, + "jsonfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", + "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, "jsonwebtoken": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", @@ -2576,6 +2785,24 @@ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, + "klaw-sync": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-2.1.0.tgz", + "integrity": "sha1-PTvNhgDnv971MjHHOf8FOu1WDkQ=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11" + } + }, + "kuler": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", + "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", + "dev": true, + "requires": { + "colornames": "^1.1.1" + } + }, "latest-version": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", @@ -2593,6 +2820,15 @@ "invert-kv": "^2.0.0" } }, + "linkify-it": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.1.0.tgz", + "integrity": "sha512-4REs8/062kV2DSHxNfq5183zrqXMl7WP0WzABH9IeJI+NLm429FgE1PDecltYfnOoFDFlZGh2T8PfZn0r+GTRg==", + "dev": true, + "requires": { + "uc.micro": "^1.0.1" + } + }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -2642,6 +2878,27 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, + "logform": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", + "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", + "dev": true, + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^2.3.3", + "ms": "^2.1.1", + "triple-beam": "^1.3.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==", + "dev": true + } + } + }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -2698,6 +2955,19 @@ "object-visit": "^1.0.0" } }, + "markdown-it": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", + "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "entities": "~1.1.1", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + } + }, "math-interval-parser": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-interval-parser/-/math-interval-parser-1.1.0.tgz", @@ -2706,6 +2976,12 @@ "xregexp": "^2.0.0" } }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "dev": true + }, "media-typer": { "version": "0.3.0", "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -3104,6 +3380,12 @@ "wrappy": "1" } }, + "one-time": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", + "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=", + "dev": true + }, "optimist": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", @@ -3634,6 +3916,23 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -3896,6 +4195,12 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=" }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -4028,6 +4333,12 @@ } } }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, "timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -4096,6 +4407,12 @@ "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", "dev": true }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==", + "dev": true + }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", @@ -4127,6 +4444,12 @@ "resolved": "https://registry.npmjs.org/u2f/-/u2f-0.1.3.tgz", "integrity": "sha512-/IaxeBqjo5o3D7plPkxdApbCpgGoI2bmTomS1kq5OjVflaE9UBJ0WfqoXqZryZKfFYBjQC7Tn1hA57WtRgh/Sg==" }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, "uglify-js": { "version": "3.4.9", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", @@ -4195,6 +4518,12 @@ "crypto-random-string": "^1.0.0" } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -4364,6 +4693,46 @@ "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", + "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", + "dev": true, + "requires": { + "async": "^2.6.1", + "diagnostics": "^1.1.1", + "is-stream": "^1.1.0", + "logform": "^2.1.1", + "one-time": "0.0.4", + "readable-stream": "^3.1.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.3.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.2.0.tgz", + "integrity": "sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "winston-transport": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", + "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", + "dev": true, + "requires": { + "readable-stream": "^2.3.6", + "triple-beam": "^1.2.0" + } + }, "wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", diff --git a/package.json b/package.json index 7396e34..491c5da 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,8 @@ "license": "MIT", "scripts": { "start": "node lib/index.js", + "build-doc": "apidoc -i src/ -p apidoc/", + "build-ts": "tsc", "build": "tsc && cd views && npm run build && cd ..", "watch-ts": "tsc -w", "watch-views": "cd views && npm run watch", @@ -27,6 +29,7 @@ "@types/qrcode": "^1.3.1", "@types/speakeasy": "^2.0.4", "@types/uuid": "^3.4.4", + "apidoc": "^0.17.7", "concurrently": "^4.1.0", "nodemon": "^1.18.10", "typescript": "^3.3.3333" @@ -54,4 +57,4 @@ "u2f": "^0.1.3", "uuid": "^3.3.2" } -} +} \ No newline at end of file diff --git a/src/api/admin/client.ts b/src/api/admin/client.ts index 18ae3f4..129c4a6 100644 --- a/src/api/admin/client.ts +++ b/src/api/admin/client.ts @@ -1,29 +1,60 @@ import { Router, Request } from "express"; -import { GetUserMiddleware } from "../middlewares/user"; import RequestError, { HttpStatusCode } from "../../helper/request_error"; import promiseMiddleware from "../../helper/promiseMiddleware"; import Client from "../../models/client"; -import User from "../../models/user"; import verify, { Types } from "../middlewares/verify"; import { randomBytes } from "crypto"; const ClientRouter: Router = Router(); -ClientRouter.use(GetUserMiddleware(true, true), (req: Request, res, next) => { - if (!req.isAdmin) res.sendStatus(HttpStatusCode.FORBIDDEN) - else next() -}); ClientRouter.route("/") + /** + * @api {get} /admin/client + * @apiName AdminGetClients + * + * @apiGroup admin_client + * @apiPermission admin + * + * @apiSuccess {Object[]} clients + * @apiSuccess {String} clients._id The internally used id + * @apiSuccess {String} clients.maintainer + * @apiSuccess {Boolean} clients.internal + * @apiSuccess {String} clients.name + * @apiSuccess {String} clients.redirect_url + * @apiSuccess {String} clients.website + * @apiSuccess {String} clients.logo + * @apiSuccess {String} clients.client_id Client ID used outside of DB + * @apiSuccess {String} clients.client_secret + */ .get(promiseMiddleware(async (req, res) => { let clients = await Client.find({}); //ToDo check if user is required! res.json(clients); })) - .delete(promiseMiddleware(async (req, res) => { - let { id } = req.query; - await Client.delete(id); - res.json({ success: true }); - })) + /** + * @api {get} /admin/client + * @apiName AdminAddClients + * + * @apiGroup admin_client + * @apiPermission admin + * + * @apiParam {Boolean} internal Is it an internal app + * @apiParam {String} name + * @apiParam {String} redirect_url + * @apiParam {String} website + * @apiParam {String} logo + * + * @apiSuccess {Object[]} clients + * @apiSuccess {String} clients._id The internally used id + * @apiSuccess {String} clients.maintainer + * @apiSuccess {Boolean} clients.internal + * @apiSuccess {String} clients.name + * @apiSuccess {String} clients.redirect_url + * @apiSuccess {String} clients.website + * @apiSuccess {String} clients.logo + * @apiSuccess {String} clients.client_id Client ID used outside of DB + * @apiSuccess {String} clients.client_secret + */ .post(verify({ internal: { type: Types.BOOLEAN, @@ -49,11 +80,48 @@ ClientRouter.route("/") await Client.save(client) res.json(client); })) + +ClientRouter.route("/:id") + /** + * @api {delete} /admin/client/:id + * @apiParam {String} id Client _id + * @apiName AdminDeleteClient + * + * @apiGroup admin_client + * @apiPermission admin + * + * @apiSuccess {Boolean} success + */ + .delete(promiseMiddleware(async (req, res) => { + let { id } = req.params; + await Client.delete(id); + res.json({ success: true }); + })) + /** + * @api {put} /admin/client/:id + * @apiParam {String} id Client _id + * @apiName AdminUpdateClient + * + * @apiGroup admin_client + * @apiPermission admin + * + * @apiParam {Boolean} internal Is it an internal app + * @apiParam {String} name + * @apiParam {String} redirect_url + * @apiParam {String} website + * @apiParam {String} logo + * + * @apiSuccess {String} _id The internally used id + * @apiSuccess {String} maintainer UserID of client maintainer + * @apiSuccess {Boolean} internal Defines if it is a internal client + * @apiSuccess {String} name The name of the Client + * @apiSuccess {String} redirect_url Redirect URL after login + * @apiSuccess {String} website Website of Client + * @apiSuccess {String} logo The Logo of the Client (optional) + * @apiSuccess {String} client_id Client ID used outside of DB + * @apiSuccess {String} client_secret The client secret, that can be used to obtain token + */ .put(verify({ - id: { - type: Types.STRING, - query: true - }, internal: { type: Types.BOOLEAN, optional: true @@ -85,4 +153,5 @@ ClientRouter.route("/") res.json(client); })) + export default ClientRouter; \ No newline at end of file diff --git a/src/api/admin/index.ts b/src/api/admin/index.ts index f97502b..b2dadc7 100644 --- a/src/api/admin/index.ts +++ b/src/api/admin/index.ts @@ -3,8 +3,16 @@ import ClientRoute from "./client"; import UserRoute from "./user"; import RegCodeRoute from "./regcode"; import PermissionRoute from "./permission"; +import { GetUserMiddleware } from "../middlewares/user"; +import RequestError, { HttpStatusCode } from "../../helper/request_error"; const AdminRoute: Router = Router(); + +AdminRoute.use(GetUserMiddleware(true, true), (req: Request, res, next) => { + if (!req.isAdmin) throw new RequestError("You have no permission to access this API", HttpStatusCode.FORBIDDEN); + else next() +}); + AdminRoute.use("/client", ClientRoute); AdminRoute.use("/regcode", RegCodeRoute) AdminRoute.use("/user", UserRoute) diff --git a/src/api/admin/permission.ts b/src/api/admin/permission.ts index 9ee422b..dba905b 100644 --- a/src/api/admin/permission.ts +++ b/src/api/admin/permission.ts @@ -8,13 +8,22 @@ import Client from "../../models/client"; import { ObjectID } from "bson"; const PermissionRoute: Router = Router(); -PermissionRoute.use(GetUserMiddleware(true, true), (req: Request, res, next) => { - if (!req.isAdmin) res.sendStatus(HttpStatusCode.FORBIDDEN) - else next() -}); - - PermissionRoute.route("/") + /** + * @api {get} /admin/permission + * @apiName AdminGetPermissions + * + * @apiParam client Optionally filter by client _id + * + * @apiGroup admin_permission + * @apiPermission admin + * + * @apiSuccess {Object[]} permissions + * @apiSuccess {String} permissions._id The ID + * @apiSuccess {String} permissions.name Permission name + * @apiSuccess {String} permissions.description A description, that makes it clear to the user, what this Permission allows to do + * @apiSuccess {String} permissions.client The ID of the owning client + */ .get(promiseMiddleware(async (req, res) => { let query = {}; if (req.query.client) { @@ -23,6 +32,23 @@ PermissionRoute.route("/") let permission = await Permission.find(query); res.json(permission); })) + /** + * @api {post} /admin/permission + * @apiName AdminAddPermission + * + * @apiParam client The ID of the owning client + * @apiParam name Permission name + * @apiParam description A description, that makes it clear to the user, what this Permission allows to do + * + * @apiGroup admin_permission + * @apiPermission admin + * + * @apiSuccess {Object[]} permissions + * @apiSuccess {String} permissions._id The ID + * @apiSuccess {String} permissions.name Permission name + * @apiSuccess {String} permissions.description A description, that makes it clear to the user, what this Permission allows to do + * @apiSuccess {String} permissions.client The ID of the owning client + */ .post(verify({ client: { type: Types.STRING @@ -45,7 +71,19 @@ PermissionRoute.route("/") }); await Permission.save(permission); res.json(permission); - })).delete(promiseMiddleware(async (req, res) => { + })) + /** + * @api {delete} /admin/permission + * @apiName AdminDeletePermission + * + * @apiParam id The permission ID + * + * @apiGroup admin_permission + * @apiPermission admin + * + * @apiSuccess {Boolean} success + */ + .delete(promiseMiddleware(async (req, res) => { let { id } = req.query; await Permission.delete(id); res.json({ success: true }); diff --git a/src/api/admin/regcode.ts b/src/api/admin/regcode.ts index b43feb3..e3105ee 100644 --- a/src/api/admin/regcode.ts +++ b/src/api/admin/regcode.ts @@ -7,20 +7,49 @@ import { GetUserMiddleware } from "../middlewares/user"; import { HttpStatusCode } from "../../helper/request_error"; const RegCodeRoute: Router = Router(); -RegCodeRoute.use(GetUserMiddleware(true, true), (req: Request, res, next) => { - if (!req.isAdmin) res.sendStatus(HttpStatusCode.FORBIDDEN) - else next() -}); RegCodeRoute.route("/") + /** + * @api {get} /admin/regcode + * @apiName AdminGetRegcodes + * + * @apiGroup admin_regcode + * @apiPermission admin + * + * @apiSuccess {Object[]} regcodes + * @apiSuccess {String} permissions._id The ID + * @apiSuccess {String} permissions.token The Regcode Token + * @apiSuccess {String} permissions.valid Defines if the Regcode is valid + * @apiSuccess {String} permissions.validTill Expiration date of RegCode + */ .get(promiseMiddleware(async (req, res) => { let regcodes = await RegCode.find({}); res.json(regcodes); })) + /** + * @api {delete} /admin/regcode + * @apiName AdminDeleteRegcode + * + * @apiParam {String} id The id of the RegCode + * + * @apiGroup admin_regcode + * @apiPermission admin + * + * @apiSuccess {Boolean} success + */ .delete(promiseMiddleware(async (req, res) => { let { id } = req.query; await RegCode.delete(id); res.json({ success: true }); })) + /** + * @api {post} /admin/regcode + * @apiName AdminAddRegcode + * + * @apiGroup admin_regcode + * @apiPermission admin + * + * @apiSuccess {String} code The newly created code + */ .post(promiseMiddleware(async (req, res) => { let regcode = RegCode.new({ token: randomBytes(10).toString("hex"), diff --git a/src/api/admin/user.ts b/src/api/admin/user.ts index 29c4c01..8c78305 100644 --- a/src/api/admin/user.ts +++ b/src/api/admin/user.ts @@ -14,10 +14,37 @@ UserRoute.use(GetUserMiddleware(true, true), (req: Request, res, next) => { }) UserRoute.route("/") + /** + * @api {get} /admin/user + * @apiName AdminGetUsers + * + * @apiGroup admin_user + * @apiPermission admin + * @apiSuccess {Object[]} user + * @apiSuccess {String} user._id The internal id of the user + * @apiSuccess {String} user.uid The public UID of the user + * @apiSuccess {String} user.username The username + * @apiSuccess {String} user.name The real name + * @apiSuccess {Date} user.birthday The birthday + * @apiSuccess {Number} user.gender 0 = none, 1 = male, 2 = female, 3 = other + * @apiSuccess {Boolean} user.admin Is admin or not + */ .get(promiseMiddleware(async (req, res) => { let users = await User.find({}); + users.forEach(e => delete e.password && delete e.salt && delete e.encryption_key); res.json(users); })) + /** + * @api {delete} /admin/user + * @apiName AdminDeleteUser + * + * @apiParam {String} id The User ID + * + * @apiGroup admin_user + * @apiPermission admin + * + * @apiSuccess {Boolean} success + */ .delete(promiseMiddleware(async (req, res) => { let { id } = req.query; let user = await User.findById(id); @@ -32,7 +59,23 @@ UserRoute.route("/") await User.delete(user); res.json({ success: true }); - })).put(promiseMiddleware(async (req, res) => { + })) + /** + * @api {put} /admin/user + * @apiName AdminChangeUser + * + * @apiParam {String} id The User ID + * + * @apiGroup admin_user + * @apiPermission admin + * + * @apiSuccess {Boolean} success + * + * @apiDescription Flipps the user role: + * admin -> user + * user -> admin + */ + .put(promiseMiddleware(async (req, res) => { let { id } = req.query; let user = await User.findById(id); user.admin = !user.admin; diff --git a/src/api/client/index.ts b/src/api/client/index.ts index e69de29..3234975 100644 --- a/src/api/client/index.ts +++ b/src/api/client/index.ts @@ -0,0 +1,27 @@ +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 + * @apiName ClientUser + * + * @apiGroup client + * @apiPermission user_client Requires ClientID and Authenticated User + * + * @apiParam {String} redirect_uri URL to redirect to on success + */ +ClientRouter.get("/user", Stacker(GetClientAuthMiddleware(false), GetUserMiddleware(true, false), async (req: Request, res: Response) => { + let jwt = await createJWT({ + client: req.client.client_id, + uid: req.user.uid, + username: req.user.username + }, 30); //after 30 seconds this token is invalid + res.redirect(req.query.redirect_uri + "?jwt=" + jwt) +})); + +export default ClientRouter; \ No newline at end of file diff --git a/src/api/client/user.ts b/src/api/client/user.ts deleted file mode 100644 index 6279326..0000000 --- a/src/api/client/user.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Request, Response } from "express" -import Stacker from "../middlewares/stacker"; -import { GetClientAuthMiddleware } from "../middlewares/client"; -import { GetUserMiddleware } from "../middlewares/user"; -import { createJWT } from "../../keys"; - -export const AuthGetUser = Stacker(GetClientAuthMiddleware(false), GetUserMiddleware(true, false), async (req: Request, res: Response) => { - let jwt = await createJWT({ - client: req.client.client_id, - uid: req.user.uid, - username: req.user.username - }, 30); //after 30 seconds this token is invalid - res.redirect(req.query.redirect_uri + "?jwt=" + jwt) -}); \ No newline at end of file diff --git a/src/api/index.ts b/src/api/index.ts index 8b6ed35..0503412 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -3,7 +3,7 @@ import AdminRoute from "./admin"; import UserRoute from "./user"; import InternalRoute from "./internal"; import Login from "./user/login"; -import { AuthGetUser } from "./client/user"; +import ClientRouter from "./client"; import * as cors from "cors"; import OAuthRoute from "./oauth"; @@ -14,10 +14,10 @@ ApiRouter.use("/user", UserRoute); ApiRouter.use("/internal", InternalRoute); ApiRouter.use("/oauth", OAuthRoute); -ApiRouter.use("/client/user", AuthGetUser); +ApiRouter.use("/client", ClientRouter); // Legacy reasons (deprecated) -ApiRouter.use("/user", AuthGetUser); +ApiRouter.use("/", ClientRouter); // Legacy reasons (deprecated) ApiRouter.post("/login", Login); diff --git a/src/api/internal/index.ts b/src/api/internal/index.ts index 3e16e14..2d6a515 100644 --- a/src/api/internal/index.ts +++ b/src/api/internal/index.ts @@ -3,6 +3,28 @@ import { OAuthInternalApp } from "./oauth"; import PasswordAuth from "./password"; const InternalRoute: Router = Router(); +/** + * @api {get} /internal/oauth + * @apiName ClientInteralOAuth + * + * @apiGroup client_internal + * @apiPermission client_internal Only ClientID + * + * @apiParam {String} redirect_uri Redirect URI called after success + * @apiParam {String} state State will be set in RedirectURI for the client to check + */ InternalRoute.get("/oauth", OAuthInternalApp); + +/** + * @api {post} /internal/password + * @apiName ClientInteralPassword + * + * @apiGroup client_internal + * @apiPermission client_internal Requires ClientID and Secret + * + * @apiParam {String} username Username (either username or UID) + * @apiParam {String} uid User ID (either username or UID) + * @apiParam {String} password Hashed and Salted according to specification + */ InternalRoute.post("/password", PasswordAuth) export default InternalRoute; \ No newline at end of file diff --git a/src/api/oauth/index.ts b/src/api/oauth/index.ts index bb12c04..220f025 100644 --- a/src/api/oauth/index.ts +++ b/src/api/oauth/index.ts @@ -5,9 +5,59 @@ import Public from "./public"; import RefreshTokenRoute from "./refresh"; const OAuthRoue: Router = Router(); +/** + * @api {post} /oauth/auth + * @apiName OAuthAuth + * + * @apiGroup oauth + * @apiPermission user Special required + * + * @apiParam {String} response_type must be "code" others are not supported + * @apiParam {String} client_id ClientID + * @apiParam {String} redirect_uri The URI to redirect with code + * @apiParam {String} scope Scope that contains the requested permissions (comma seperated list of permissions) + * @apiParam {String} state State, that will be passed to redirect_uri for client + * @apiParam {String} nored Deactivates the Redirect response from server and instead returns the redirect URI in JSON response + */ OAuthRoue.post("/auth", AuthRoute); + +/** + * @api {get} /oauth/jwt + * @apiName OAuthJwt + * + * @apiGroup oauth + * @apiPermission none + * + * @apiParam {String} refreshtoken + * + * @apiSuccess {String} token The JWT that allowes the application to access the recources granted for refresh token + */ OAuthRoue.get("/jwt", JWTRoute) + +/** + * @api {get} /oauth/public + * @apiName OAuthPublic + * + * @apiGroup oauth + * @apiPermission none + * + * @apiSuccess {String} public_key The applications public_key. Used to verify JWT. + */ OAuthRoue.get("/public", Public) + +/** + * @api {get} /oauth/refresh + * @apiName OAuthRefreshGet + * + * @apiGroup oauth + */ OAuthRoue.get("/refresh", RefreshTokenRoute); + +/** + * @api {post} /oauth/refresh + * @apiName OAuthRefreshPost + * + * @apiGroup oauth + */ OAuthRoue.post("/refresh", RefreshTokenRoute); export default OAuthRoue; \ No newline at end of file diff --git a/src/api/oauth/jwt.ts b/src/api/oauth/jwt.ts index 601dd3a..b799498 100644 --- a/src/api/oauth/jwt.ts +++ b/src/api/oauth/jwt.ts @@ -3,7 +3,6 @@ import promiseMiddleware from "../../helper/promiseMiddleware"; import RequestError, { HttpStatusCode } from "../../helper/request_error"; import RefreshToken from "../../models/refresh_token"; import User from "../../models/user"; -import Permission from "../../models/permissions"; import Client from "../../models/client"; import getOAuthJWT from "../../helper/jwt"; @@ -16,7 +15,9 @@ const JWTRoute = promiseMiddleware(async (req: Request, res: Response) => { let user = await User.findById(token.user); if (!user) { - //TODO handle error! + token.valid = false; + await RefreshToken.save(token); + throw new RequestError(req.__("Invalid token"), HttpStatusCode.BAD_REQUEST); } let client = await Client.findById(token.client); diff --git a/src/api/user/index.ts b/src/api/user/index.ts index 973ab9d..4f58b6b 100644 --- a/src/api/user/index.ts +++ b/src/api/user/index.ts @@ -5,10 +5,91 @@ 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); -UserRoute.delete("/token", DeleteToken); + +/** + * @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); export default UserRoute; \ No newline at end of file diff --git a/src/api/user/login.ts b/src/api/user/login.ts index 0a45cd2..81d086c 100644 --- a/src/api/user/login.ts +++ b/src/api/user/login.ts @@ -3,9 +3,7 @@ import User, { IUser } from "../../models/user"; import { randomBytes } from "crypto"; import moment = require("moment"); import LoginToken from "../../models/login_token"; -import RequestError, { HttpStatusCode } from "../../helper/request_error"; import promiseMiddleware from "../../helper/promiseMiddleware"; -import * as speakeasy from "speakeasy"; import TwoFactor from "../../models/twofactor"; const Login = promiseMiddleware(async (req: Request, res: Response) => { @@ -19,49 +17,49 @@ const Login = promiseMiddleware(async (req: Request, res: Response) => { 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"] + } - 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 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 - }); - } - - if (type === "password") { let { username, password, uid } = req.body; let user = await User.findOne(username ? { username: username.toLowerCase() } : { uid: uid }) diff --git a/src/api/user/twofactor/index.ts b/src/api/user/twofactor/index.ts index 51d618a..41ab54a 100644 --- a/src/api/user/twofactor/index.ts +++ b/src/api/user/twofactor/index.ts @@ -6,7 +6,6 @@ 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) => { diff --git a/src/web.ts b/src/web.ts index e7ffa83..04675da 100644 --- a/src/web.ts +++ b/src/web.ts @@ -82,9 +82,10 @@ export default class Web { private registerErrorHandler() { this.server.use((error, req: express.Request, res, next) => { if (!(error instanceof RequestError)) { - Logging.error(error); - error = new RequestError(error.message, HttpStatusCode.INTERNAL_SERVER_ERROR); - } else if (error.status === 500 && !(error).nolog) { + 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); diff --git a/views/src/admin/admin.js b/views/src/admin/admin.js index 71e9bd0..619b2a3 100644 --- a/views/src/admin/admin.js +++ b/views/src/admin/admin.js @@ -136,7 +136,7 @@ Handlebars.registerHelper("formatDate", function (datetime, format) { } window.deleteClient = (id) => { - request("/api/admin/client?id=" + id, "DELETE").then(() => loadList()).catch(catchError) + request("/api/admin/client/id=" + id, "DELETE").then(() => loadList()).catch(catchError) } window.createClientSubmit = (elm) => {