mirror of
https://github.com/xuthus83/pigallery2.git
synced 2025-01-14 14:43:17 +08:00
implementing csrf security for posts
This commit is contained in:
parent
19d3f10d35
commit
5b4f06e789
158
package-lock.json
generated
158
package-lock.json
generated
@ -3105,6 +3105,12 @@
|
|||||||
"defer-to-connect": "^1.0.1"
|
"defer-to-connect": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/bcrypt": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-nohgNyv+1ViVcubKBh0+XiNJ3dO8nYu///9aJ4cgSqv70gBL+94SNy/iC2NLzKPT2Zt/QavrOkBVbZRLZmw6NQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/bcryptjs": {
|
"@types/bcryptjs": {
|
||||||
"version": "2.4.2",
|
"version": "2.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz",
|
||||||
@ -3199,6 +3205,16 @@
|
|||||||
"@types/serve-static": "*"
|
"@types/serve-static": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/express-jwt": {
|
||||||
|
"version": "0.0.42",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.42.tgz",
|
||||||
|
"integrity": "sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/express": "*",
|
||||||
|
"@types/express-unless": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/express-serve-static-core": {
|
"@types/express-serve-static-core": {
|
||||||
"version": "4.17.0",
|
"version": "4.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.0.tgz",
|
||||||
@ -3209,6 +3225,15 @@
|
|||||||
"@types/range-parser": "*"
|
"@types/range-parser": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/express-unless": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.1.tgz",
|
||||||
|
"integrity": "sha512-5fuvg7C69lemNgl0+v+CUxDYWVPSfXHhJPst4yTLcqi4zKJpORCxnDrnnilk3k0DTq/WrAUdvXFs01+vUqUZHw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/express": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/fluent-ffmpeg": {
|
"@types/fluent-ffmpeg": {
|
||||||
"version": "2.1.11",
|
"version": "2.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/fluent-ffmpeg/-/fluent-ffmpeg-2.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/fluent-ffmpeg/-/fluent-ffmpeg-2.1.11.tgz",
|
||||||
@ -3973,6 +3998,15 @@
|
|||||||
"integrity": "sha512-kGCRI9oiCxFS6soGKlyzhMzDydfcPix9PpTkr7h11huxOxhWwP37Tg7DYBaQ18eQTNreZEuLkhpbGSqVNZPnnw==",
|
"integrity": "sha512-kGCRI9oiCxFS6soGKlyzhMzDydfcPix9PpTkr7h11huxOxhWwP37Tg7DYBaQ18eQTNreZEuLkhpbGSqVNZPnnw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/jsonwebtoken": {
|
||||||
|
"version": "8.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.3.5.tgz",
|
||||||
|
"integrity": "sha512-VGM1gb+LwsQ5EPevvbvdnKncajBdYqNcrvixBif1BsiDQiSF1q+j4bBTvKC6Bt9n2kqNSx+yNTY2TVJ360E7EQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/keygrip": {
|
"@types/keygrip": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.1.tgz",
|
||||||
@ -5592,6 +5626,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz",
|
||||||
"integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs="
|
"integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs="
|
||||||
},
|
},
|
||||||
|
"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="
|
||||||
|
},
|
||||||
"buffer-fill": {
|
"buffer-fill": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
|
||||||
@ -7731,6 +7770,14 @@
|
|||||||
"safer-buffer": "^2.1.0"
|
"safer-buffer": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ecdsa-sig-formatter": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"editorconfig": {
|
"editorconfig": {
|
||||||
"version": "0.15.3",
|
"version": "0.15.3",
|
||||||
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
|
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
|
||||||
@ -8419,6 +8466,34 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"express-jwt": {
|
||||||
|
"version": "5.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-5.3.1.tgz",
|
||||||
|
"integrity": "sha512-1C9RNq0wMp/JvsH/qZMlg3SIPvKu14YkZ4YYv7gJQ1Vq+Dv8LH9tLKenS5vMNth45gTlEUGx+ycp9IHIlaHP/g==",
|
||||||
|
"requires": {
|
||||||
|
"async": "^1.5.0",
|
||||||
|
"express-unless": "^0.3.0",
|
||||||
|
"jsonwebtoken": "^8.1.0",
|
||||||
|
"lodash.set": "^4.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"async": {
|
||||||
|
"version": "1.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
|
||||||
|
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
|
||||||
|
},
|
||||||
|
"express-unless": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-unless/-/express-unless-0.3.1.tgz",
|
||||||
|
"integrity": "sha1-JVfBRudb65A+LSR/m1ugFFJpbiA="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"express-unless": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-unless/-/express-unless-0.5.0.tgz",
|
||||||
|
"integrity": "sha1-wuzkd/QVUIkUPbuGnQfFfF62q5s="
|
||||||
|
},
|
||||||
"extend": {
|
"extend": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||||
@ -11900,6 +11975,30 @@
|
|||||||
"integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
|
"integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"jsonwebtoken": {
|
||||||
|
"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.2",
|
||||||
|
"lodash.includes": "^4.3.0",
|
||||||
|
"lodash.isboolean": "^3.0.3",
|
||||||
|
"lodash.isinteger": "^4.0.4",
|
||||||
|
"lodash.isnumber": "^3.0.3",
|
||||||
|
"lodash.isplainobject": "^4.0.6",
|
||||||
|
"lodash.isstring": "^4.0.1",
|
||||||
|
"lodash.once": "^4.0.0",
|
||||||
|
"ms": "^2.1.1",
|
||||||
|
"semver": "^5.6.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"jsprim": {
|
"jsprim": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||||
@ -11979,6 +12078,25 @@
|
|||||||
"tslib": "^1.9.0"
|
"tslib": "^1.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jwa": {
|
||||||
|
"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",
|
||||||
|
"safe-buffer": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jws": {
|
||||||
|
"version": "3.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
|
||||||
|
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
|
||||||
|
"requires": {
|
||||||
|
"jwa": "^1.4.1",
|
||||||
|
"safe-buffer": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"karma": {
|
"karma": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz",
|
||||||
@ -12571,6 +12689,11 @@
|
|||||||
"lodash._root": "^3.0.0"
|
"lodash._root": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lodash.includes": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||||
|
"integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
|
||||||
|
},
|
||||||
"lodash.isarguments": {
|
"lodash.isarguments": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
||||||
@ -12583,6 +12706,31 @@
|
|||||||
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
|
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"lodash.isboolean": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||||
|
"integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
|
||||||
|
},
|
||||||
|
"lodash.isinteger": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||||
|
"integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
|
||||||
|
},
|
||||||
|
"lodash.isnumber": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||||
|
"integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
|
||||||
|
},
|
||||||
|
"lodash.isplainobject": {
|
||||||
|
"version": "4.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||||
|
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
|
||||||
|
},
|
||||||
|
"lodash.isstring": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||||
|
"integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
|
||||||
|
},
|
||||||
"lodash.keys": {
|
"lodash.keys": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
|
||||||
@ -12594,12 +12742,22 @@
|
|||||||
"lodash.isarray": "^3.0.0"
|
"lodash.isarray": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lodash.once": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||||
|
"integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
|
||||||
|
},
|
||||||
"lodash.restparam": {
|
"lodash.restparam": {
|
||||||
"version": "3.6.1",
|
"version": "3.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz",
|
||||||
"integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=",
|
"integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"lodash.set": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
|
||||||
|
"integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM="
|
||||||
|
},
|
||||||
"lodash.template": {
|
"lodash.template": {
|
||||||
"version": "3.6.2",
|
"version": "3.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz",
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
"ejs": "3.0.1",
|
"ejs": "3.0.1",
|
||||||
"exifreader": "2.12.0",
|
"exifreader": "2.12.0",
|
||||||
"express": "4.17.1",
|
"express": "4.17.1",
|
||||||
|
"express-unless": "0.5.0",
|
||||||
"fluent-ffmpeg": "2.1.2",
|
"fluent-ffmpeg": "2.1.2",
|
||||||
"image-size": "0.8.3",
|
"image-size": "0.8.3",
|
||||||
"jimp": "0.9.3",
|
"jimp": "0.9.3",
|
||||||
@ -64,19 +65,22 @@
|
|||||||
"@angular/platform-browser-dynamic": "8.2.14",
|
"@angular/platform-browser-dynamic": "8.2.14",
|
||||||
"@angular/router": "8.2.14",
|
"@angular/router": "8.2.14",
|
||||||
"@ngx-translate/i18n-polyfill": "1.0.0",
|
"@ngx-translate/i18n-polyfill": "1.0.0",
|
||||||
|
"@types/bcrypt": "^3.0.0",
|
||||||
"@types/bcryptjs": "2.4.2",
|
"@types/bcryptjs": "2.4.2",
|
||||||
"@types/chai": "4.2.6",
|
"@types/chai": "4.2.6",
|
||||||
"@types/cookie-parser": "1.4.2",
|
"@types/cookie-parser": "1.4.2",
|
||||||
"@types/cookie-session": "2.0.37",
|
"@types/cookie-session": "2.0.37",
|
||||||
"@types/csurf": "1.9.36",
|
"@types/csurf": "^1.9.36",
|
||||||
"@types/ejs": "3.0.0",
|
"@types/ejs": "3.0.0",
|
||||||
"@types/express": "4.17.2",
|
"@types/express": "4.17.2",
|
||||||
|
"@types/express-jwt": "0.0.42",
|
||||||
"@types/fluent-ffmpeg": "2.1.11",
|
"@types/fluent-ffmpeg": "2.1.11",
|
||||||
"@types/gm": "1.18.6",
|
"@types/gm": "1.18.6",
|
||||||
"@types/gulp": "4.0.6",
|
"@types/gulp": "4.0.6",
|
||||||
"@types/gulp-zip": "4.0.1",
|
"@types/gulp-zip": "4.0.1",
|
||||||
"@types/image-size": "0.8.0",
|
"@types/image-size": "0.8.0",
|
||||||
"@types/jasmine": "3.5.0",
|
"@types/jasmine": "3.5.0",
|
||||||
|
"@types/jsonwebtoken": "8.3.5",
|
||||||
"@types/node": "12.12.14",
|
"@types/node": "12.12.14",
|
||||||
"@types/rimraf": "2.0.3",
|
"@types/rimraf": "2.0.3",
|
||||||
"@types/sharp": "0.23.1",
|
"@types/sharp": "0.23.1",
|
||||||
|
@ -24,7 +24,6 @@ export class GalleryMWs {
|
|||||||
public static async listDirectory(req: Request, res: Response, next: NextFunction) {
|
public static async listDirectory(req: Request, res: Response, next: NextFunction) {
|
||||||
const directoryName = req.params.directory || '/';
|
const directoryName = req.params.directory || '/';
|
||||||
const absoluteDirectoryName = path.join(ProjectPath.ImageFolder, directoryName);
|
const absoluteDirectoryName = path.join(ProjectPath.ImageFolder, directoryName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ((await fsp.stat(absoluteDirectoryName)).isDirectory() === false) {
|
if ((await fsp.stat(absoluteDirectoryName)).isDirectory() === false) {
|
||||||
return next();
|
return next();
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import {NextFunction, Request, Response} from 'express';
|
import {NextFunction, Request, Response} from 'express';
|
||||||
import {ErrorCodes, ErrorDTO} from '../../common/entities/Error';
|
import {ErrorCodes, ErrorDTO} from '../../common/entities/Error';
|
||||||
import {Utils} from '../../common/Utils';
|
|
||||||
import {Message} from '../../common/entities/Message';
|
import {Message} from '../../common/entities/Message';
|
||||||
import {SharingDTO} from '../../common/entities/SharingDTO';
|
|
||||||
import {Config} from '../../common/config/private/Config';
|
import {Config} from '../../common/config/private/Config';
|
||||||
import {ConfigClass} from '../../common/config/private/ConfigClass';
|
import {ConfigClass} from '../../common/config/private/ConfigClass';
|
||||||
import {UserRoles} from '../../common/entities/UserDTO';
|
import {UserDTO, UserRoles} from '../../common/entities/UserDTO';
|
||||||
import {NotificationManager} from '../model/NotifocationManager';
|
import {NotificationManager} from '../model/NotifocationManager';
|
||||||
import {Logger} from '../Logger';
|
import {Logger} from '../Logger';
|
||||||
|
|
||||||
@ -25,8 +23,19 @@ export class RenderingMWs {
|
|||||||
return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, 'User not exists'));
|
return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, 'User not exists'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = Utils.clone(req.session.user);
|
const user = <UserDTO>{
|
||||||
delete user.password;
|
id: req.session.user.id,
|
||||||
|
name: req.session.user.name,
|
||||||
|
csrfToken: req.session.user.csrfToken || req.csrfToken(),
|
||||||
|
role: req.session.user.role,
|
||||||
|
usedSharingKey: req.session.user.usedSharingKey,
|
||||||
|
permissions: req.session.user.permissions
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!user.csrfToken && req.csrfToken) {
|
||||||
|
user.csrfToken = req.csrfToken();
|
||||||
|
}
|
||||||
|
|
||||||
RenderingMWs.renderMessage(res, user);
|
RenderingMWs.renderMessage(res, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,8 +44,7 @@ export class RenderingMWs {
|
|||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
const sharing = Utils.clone<SharingDTO>(req.resultPipe);
|
const {password, creator, ...sharing} = req.resultPipe;
|
||||||
delete sharing.password;
|
|
||||||
RenderingMWs.renderMessage(res, sharing);
|
RenderingMWs.renderMessage(res, sharing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ export class SharingMWs {
|
|||||||
if (Config.Client.Sharing.enabled === false) {
|
if (Config.Client.Sharing.enabled === false) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
const sharingKey = req.params[QueryParams.gallery.sharingKey_long];
|
const sharingKey = req.params[QueryParams.gallery.sharingKey_params];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.resultPipe = await ObjectManagers.getInstance().SharingManager.findOne({sharingKey: sharingKey});
|
req.resultPipe = await ObjectManagers.getInstance().SharingManager.findOne({sharingKey: sharingKey});
|
||||||
@ -37,7 +37,6 @@ export class SharingMWs {
|
|||||||
let sharingKey = SharingMWs.generateKey();
|
let sharingKey = SharingMWs.generateKey();
|
||||||
|
|
||||||
// create one not yet used
|
// create one not yet used
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
await ObjectManagers.getInstance().SharingManager.findOne({sharingKey: sharingKey});
|
await ObjectManagers.getInstance().SharingManager.findOne({sharingKey: sharingKey});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {LoginCredential} from '../../../common/entities/LoginCredential';
|
import {LoginCredential} from '../../../common/entities/LoginCredential';
|
||||||
import {UserEntity} from '../../model/database/sql/enitites/UserEntity';
|
import {UserDTO} from '../../../common/entities/UserDTO';
|
||||||
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@ -18,7 +18,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Session {
|
interface Session {
|
||||||
user?: UserEntity;
|
user?: UserDTO;
|
||||||
rememberMe?: boolean;
|
rememberMe?: boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
///<reference path="../customtypings/ExtendedRequest.d.ts"/>
|
|
||||||
import {NextFunction, Request, Response} from 'express';
|
import {NextFunction, Request, Response} from 'express';
|
||||||
import {ErrorCodes, ErrorDTO} from '../../../common/entities/Error';
|
import {ErrorCodes, ErrorDTO} from '../../../common/entities/Error';
|
||||||
import {UserDTO, UserRoles} from '../../../common/entities/UserDTO';
|
import {UserDTO, UserRoles} from '../../../common/entities/UserDTO';
|
||||||
@ -30,11 +29,16 @@ export class AuthenticationMWs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async authenticate(req: Request, res: Response, next: NextFunction) {
|
public static async authenticate(req: Request, res: Response, next: NextFunction) {
|
||||||
|
|
||||||
if (Config.Client.authenticationRequired === false) {
|
if (Config.Client.authenticationRequired === false) {
|
||||||
req.session.user = <UserDTO>{name: UserRoles[Config.Client.unAuthenticatedUserRole], role: Config.Client.unAuthenticatedUserRole};
|
req.session.user = <UserDTO>{name: UserRoles[Config.Client.unAuthenticatedUserRole], role: Config.Client.unAuthenticatedUserRole};
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if already authenticated, do not try to use sharing authentication
|
||||||
|
if (typeof req.session.user !== 'undefined') {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const user = await AuthenticationMWs.getSharingUser(req);
|
const user = await AuthenticationMWs.getSharingUser(req);
|
||||||
if (!!user) {
|
if (!!user) {
|
||||||
@ -45,12 +49,8 @@ export class AuthenticationMWs {
|
|||||||
return next(new ErrorDTO(ErrorCodes.CREDENTIAL_NOT_FOUND, null, err));
|
return next(new ErrorDTO(ErrorCodes.CREDENTIAL_NOT_FOUND, null, err));
|
||||||
}
|
}
|
||||||
if (typeof req.session.user === 'undefined') {
|
if (typeof req.session.user === 'undefined') {
|
||||||
return next(new ErrorDTO(ErrorCodes.NOT_AUTHENTICATED));
|
res.status(401);
|
||||||
}
|
return next(new ErrorDTO(ErrorCodes.NOT_AUTHENTICATED, 'Not authenticated'));
|
||||||
if (req.session.rememberMe === true) {
|
|
||||||
req.sessionOptions.expires = new Date(Date.now() + Config.Server.sessionTimeout);
|
|
||||||
} else {
|
|
||||||
delete (req.sessionOptions.expires);
|
|
||||||
}
|
}
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ export class AuthenticationMWs {
|
|||||||
p = path.dirname(p);
|
p = path.dirname(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!UserDTO.isDirectoryPathAvailable(p, req.session.user.permissions, path.sep)) {
|
if (!UserDTO.isDirectoryPathAvailable(p, req.session.user.permissions)) {
|
||||||
return res.sendStatus(403);
|
return res.sendStatus(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,21 +94,22 @@ export class AuthenticationMWs {
|
|||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
// not enough parameter
|
// not enough parameter
|
||||||
if ((!req.query[QueryParams.gallery.sharingKey_short] && !req.params[QueryParams.gallery.sharingKey_long])) {
|
if ((!req.query[QueryParams.gallery.sharingKey_query] && !req.params[QueryParams.gallery.sharingKey_params])) {
|
||||||
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'no sharing key provided'));
|
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'no sharing key provided'));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const password = (req.body ? req.body.password : null) || null;
|
const password = (req.body ? req.body.password : null) || null;
|
||||||
|
const sharingKey: string = req.query[QueryParams.gallery.sharingKey_query] || req.params[QueryParams.gallery.sharingKey_params];
|
||||||
const sharing = await ObjectManagers.getInstance().SharingManager.findOne({
|
const sharing = await ObjectManagers.getInstance().SharingManager.findOne({
|
||||||
sharingKey: req.query[QueryParams.gallery.sharingKey_short] || req.params[QueryParams.gallery.sharingKey_long]
|
sharingKey: sharingKey
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!sharing || sharing.expires < Date.now() ||
|
if (!sharing || sharing.expires < Date.now() ||
|
||||||
(Config.Client.Sharing.passwordProtected === true
|
(Config.Client.Sharing.passwordProtected === true
|
||||||
&& (sharing.password)
|
&& (sharing.password)
|
||||||
&& !PasswordHelper.comparePassword(password, sharing.password))) {
|
&& !PasswordHelper.comparePassword(password, sharing.password))) {
|
||||||
|
res.status(401);
|
||||||
return next(new ErrorDTO(ErrorCodes.CREDENTIAL_NOT_FOUND));
|
return next(new ErrorDTO(ErrorCodes.CREDENTIAL_NOT_FOUND));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +118,12 @@ export class AuthenticationMWs {
|
|||||||
sharingPath += '*';
|
sharingPath += '*';
|
||||||
}
|
}
|
||||||
|
|
||||||
req.session.user = <UserDTO>{name: 'Guest', role: UserRoles.LimitedGuest, permissions: [sharingPath]};
|
req.session.user = <UserDTO>{
|
||||||
|
name: 'Guest',
|
||||||
|
role: UserRoles.LimitedGuest,
|
||||||
|
permissions: [sharingPath],
|
||||||
|
usedSharingKey: sharing.sharingKey
|
||||||
|
};
|
||||||
return next();
|
return next();
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -135,12 +141,16 @@ export class AuthenticationMWs {
|
|||||||
|
|
||||||
public static async login(req: Request, res: Response, next: NextFunction) {
|
public static async login(req: Request, res: Response, next: NextFunction) {
|
||||||
|
|
||||||
|
if (Config.Client.authenticationRequired === false) {
|
||||||
|
return res.sendStatus(404);
|
||||||
|
}
|
||||||
|
|
||||||
// not enough parameter
|
// not enough parameter
|
||||||
if ((typeof req.body === 'undefined') ||
|
if ((typeof req.body === 'undefined') ||
|
||||||
(typeof req.body.loginCredential === 'undefined') ||
|
(typeof req.body.loginCredential === 'undefined') ||
|
||||||
(typeof req.body.loginCredential.username === 'undefined') ||
|
(typeof req.body.loginCredential.username === 'undefined') ||
|
||||||
(typeof req.body.loginCredential.password === 'undefined')) {
|
(typeof req.body.loginCredential.password === 'undefined')) {
|
||||||
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'not all parameters are included, got' + JSON.stringify(req.body)));
|
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'not all parameters are included for loginCredential'));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// lets find the user
|
// lets find the user
|
||||||
@ -156,7 +166,8 @@ export class AuthenticationMWs {
|
|||||||
return next();
|
return next();
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return next(new ErrorDTO(ErrorCodes.CREDENTIAL_NOT_FOUND));
|
console.error(err);
|
||||||
|
return next(new ErrorDTO(ErrorCodes.CREDENTIAL_NOT_FOUND, 'credentials not found during login'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -164,21 +175,21 @@ export class AuthenticationMWs {
|
|||||||
|
|
||||||
public static logout(req: Request, res: Response, next: NextFunction) {
|
public static logout(req: Request, res: Response, next: NextFunction) {
|
||||||
delete req.session.user;
|
delete req.session.user;
|
||||||
delete req.session.rememberMe;
|
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async getSharingUser(req: Request) {
|
private static async getSharingUser(req: Request) {
|
||||||
if (Config.Client.Sharing.enabled === true &&
|
if (Config.Client.Sharing.enabled === true &&
|
||||||
(!!req.params[QueryParams.gallery.sharingKey_short] || !!req.params[QueryParams.gallery.sharingKey_long])) {
|
(!!req.query[QueryParams.gallery.sharingKey_query] || !!req.params[QueryParams.gallery.sharingKey_params])) {
|
||||||
|
const sharingKey: string = req.query[QueryParams.gallery.sharingKey_query] || req.params[QueryParams.gallery.sharingKey_params];
|
||||||
const sharing = await ObjectManagers.getInstance().SharingManager.findOne({
|
const sharing = await ObjectManagers.getInstance().SharingManager.findOne({
|
||||||
sharingKey: req.query[QueryParams.gallery.sharingKey_short] || req.params[QueryParams.gallery.sharingKey_long],
|
sharingKey: sharingKey
|
||||||
});
|
});
|
||||||
if (!sharing || sharing.expires < Date.now()) {
|
if (!sharing || sharing.expires < Date.now()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.Client.Sharing.passwordProtected === true && (sharing.password)) {
|
if (Config.Client.Sharing.passwordProtected === true && sharing.password) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ export class UserRequestConstrainsMWs {
|
|||||||
if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')) {
|
if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
if (req.session.user.id !== req.params.id) {
|
if (req.session.user.id !== parseInt(req.params.id, 10)) {
|
||||||
return next(new ErrorDTO(ErrorCodes.NOT_AUTHORISED));
|
return next(new ErrorDTO(ErrorCodes.NOT_AUTHORISED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ export class UserRequestConstrainsMWs {
|
|||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.session.user.id === req.params.id) {
|
if (req.session.user.id === parseInt(req.params.id, 10)) {
|
||||||
return next(new ErrorDTO(ErrorCodes.NOT_AUTHORISED));
|
return next(new ErrorDTO(ErrorCodes.NOT_AUTHORISED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ export class UserRequestConstrainsMWs {
|
|||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.session.user.id !== req.params.id) {
|
if (req.session.user.id !== parseInt(req.params.id, 10)) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,12 +6,16 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PasswordHelper {
|
export class PasswordHelper {
|
||||||
public static cryptPassword(password: string) {
|
public static cryptPassword(password: string): string {
|
||||||
const salt = bcrypt.genSaltSync(9);
|
const salt = bcrypt.genSaltSync(9);
|
||||||
return bcrypt.hashSync(password, salt);
|
return bcrypt.hashSync(password, salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static comparePassword(password: string, encryptedPassword: string) {
|
public static comparePassword(password: string, encryptedPassword: string): boolean {
|
||||||
return bcrypt.compareSync(password, encryptedPassword);
|
try {
|
||||||
|
return bcrypt.compareSync(password, encryptedPassword);
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ export class UserManager implements IUserManager {
|
|||||||
delete filter.password;
|
delete filter.password;
|
||||||
const user = (await connection.getRepository(UserEntity).findOne(filter));
|
const user = (await connection.getRepository(UserEntity).findOne(filter));
|
||||||
|
|
||||||
|
|
||||||
if (pass && !PasswordHelper.comparePassword(pass, user.password)) {
|
if (pass && !PasswordHelper.comparePassword(pass, user.password)) {
|
||||||
throw new Error('No entry found');
|
throw new Error('No entry found');
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,6 @@ export class FileEntity implements FileDTO {
|
|||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@ManyToOne(type => DirectoryEntity, directory => directory.metaFile, {onDelete: 'CASCADE'})
|
@ManyToOne(type => DirectoryEntity, directory => directory.metaFile, {onDelete: 'CASCADE', nullable: false})
|
||||||
directory: DirectoryEntity;
|
directory: DirectoryEntity;
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ export abstract class MediaEntity implements MediaDTO {
|
|||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@ManyToOne(type => DirectoryEntity, directory => directory.media, {onDelete: 'CASCADE'})
|
@ManyToOne(type => DirectoryEntity, directory => directory.media, {onDelete: 'CASCADE', nullable: false})
|
||||||
directory: DirectoryEntity;
|
directory: DirectoryEntity;
|
||||||
|
|
||||||
@Column(type => MediaMetadataEntity)
|
@Column(type => MediaMetadataEntity)
|
||||||
|
@ -36,6 +36,6 @@ export class SharingEntity implements SharingDTO {
|
|||||||
@Column()
|
@Column()
|
||||||
includeSubfolders: boolean;
|
includeSubfolders: boolean;
|
||||||
|
|
||||||
@ManyToOne(type => UserEntity)
|
@ManyToOne(type => UserEntity, {onDelete: 'CASCADE', nullable: false})
|
||||||
creator: UserDTO;
|
creator: UserDTO;
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,17 @@ export class ErrorRouter {
|
|||||||
|
|
||||||
private static addGenericHandler(app: Express) {
|
private static addGenericHandler(app: Express) {
|
||||||
app.use((err: any, req: Request, res: Response, next: Function) => {
|
app.use((err: any, req: Request, res: Response, next: Function) => {
|
||||||
|
|
||||||
|
if (err.name === 'UnauthorizedError') {
|
||||||
|
// jwt authentication error
|
||||||
|
res.status(401);
|
||||||
|
return next(new ErrorDTO(ErrorCodes.NOT_AUTHENTICATED, 'Invalid token'));
|
||||||
|
}
|
||||||
|
|
||||||
// Flush out the stack to the console
|
// Flush out the stack to the console
|
||||||
Logger.error('Unexpected error:');
|
Logger.error('Unexpected error:');
|
||||||
console.error(err);
|
console.error(err);
|
||||||
next(new ErrorDTO(ErrorCodes.SERVER_ERROR, 'Unknown server side error', err));
|
return next(new ErrorDTO(ErrorCodes.SERVER_ERROR, 'Unknown server side error', err));
|
||||||
},
|
},
|
||||||
RenderingMWs.renderError
|
RenderingMWs.renderError
|
||||||
);
|
);
|
||||||
|
@ -2,12 +2,12 @@ import {Express, NextFunction, Request, Response} from 'express';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as ejs from 'ejs';
|
import * as ejs from 'ejs';
|
||||||
import {Utils} from '../../common/Utils';
|
|
||||||
import {Config} from '../../common/config/private/Config';
|
import {Config} from '../../common/config/private/Config';
|
||||||
import {ProjectPath} from '../ProjectPath';
|
import {ProjectPath} from '../ProjectPath';
|
||||||
import {AuthenticationMWs} from '../middlewares/user/AuthenticationMWs';
|
import {AuthenticationMWs} from '../middlewares/user/AuthenticationMWs';
|
||||||
import {CookieNames} from '../../common/CookieNames';
|
import {CookieNames} from '../../common/CookieNames';
|
||||||
import {ErrorCodes, ErrorDTO} from '../../common/entities/Error';
|
import {ErrorCodes, ErrorDTO} from '../../common/entities/Error';
|
||||||
|
import {UserDTO} from '../../common/entities/UserDTO';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace Express {
|
namespace Express {
|
||||||
@ -69,9 +69,18 @@ export class PublicRouter {
|
|||||||
|
|
||||||
res.tpl.user = null;
|
res.tpl.user = null;
|
||||||
if (req.session.user) {
|
if (req.session.user) {
|
||||||
const user = Utils.clone(req.session.user);
|
res.tpl.user = <UserDTO>{
|
||||||
delete user.password;
|
id: req.session.user.id,
|
||||||
res.tpl.user = user;
|
name: req.session.user.name,
|
||||||
|
csrfToken: req.session.user.csrfToken,
|
||||||
|
role: req.session.user.role,
|
||||||
|
usedSharingKey: req.session.user.usedSharingKey,
|
||||||
|
permissions: req.session.user.permissions
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!res.tpl.user.csrfToken && req.csrfToken) {
|
||||||
|
res.tpl.user.csrfToken = req.csrfToken();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
res.tpl.clientConfig = Config.Client;
|
res.tpl.clientConfig = Config.Client;
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ export class SharingRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static addGetSharing(app: express.Express) {
|
private static addGetSharing(app: express.Express) {
|
||||||
app.get('/api/share/:' + QueryParams.gallery.sharingKey_long,
|
app.get('/api/share/:' + QueryParams.gallery.sharingKey_params,
|
||||||
AuthenticationMWs.authenticate,
|
AuthenticationMWs.authenticate,
|
||||||
AuthenticationMWs.authorise(UserRoles.LimitedGuest),
|
AuthenticationMWs.authorise(UserRoles.LimitedGuest),
|
||||||
SharingMWs.getSharing,
|
SharingMWs.getSharing,
|
||||||
|
@ -36,7 +36,7 @@ export class UserRouter {
|
|||||||
|
|
||||||
|
|
||||||
private static addGetSessionUser(app: Express) {
|
private static addGetSessionUser(app: Express) {
|
||||||
app.get('/api/user/login',
|
app.get('/api/user/me',
|
||||||
AuthenticationMWs.authenticate,
|
AuthenticationMWs.authenticate,
|
||||||
RenderingMWs.renderSessionUser
|
RenderingMWs.renderSessionUser
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as _express from 'express';
|
import * as _express from 'express';
|
||||||
|
import {Request} from 'express';
|
||||||
import * as _bodyParser from 'body-parser';
|
import * as _bodyParser from 'body-parser';
|
||||||
import * as cookieParser from 'cookie-parser';
|
import * as cookieParser from 'cookie-parser';
|
||||||
import * as _http from 'http';
|
import * as _http from 'http';
|
||||||
@ -17,7 +18,9 @@ import {Router} from './routes/Router';
|
|||||||
import {ServerConfig} from '../common/config/private/IPrivateConfig';
|
import {ServerConfig} from '../common/config/private/IPrivateConfig';
|
||||||
import {PhotoProcessing} from './model/fileprocessing/PhotoProcessing';
|
import {PhotoProcessing} from './model/fileprocessing/PhotoProcessing';
|
||||||
import * as _csrf from 'csurf';
|
import * as _csrf from 'csurf';
|
||||||
|
import * as unless from 'express-unless';
|
||||||
import {Event} from '../common/event/Event';
|
import {Event} from '../common/event/Event';
|
||||||
|
import {QueryParams} from '../common/QueryParams';
|
||||||
|
|
||||||
const _session = require('cookie-session');
|
const _session = require('cookie-session');
|
||||||
|
|
||||||
@ -75,7 +78,18 @@ export class Server {
|
|||||||
// for parsing application/json
|
// for parsing application/json
|
||||||
this.app.use(_bodyParser.json());
|
this.app.use(_bodyParser.json());
|
||||||
this.app.use(cookieParser());
|
this.app.use(cookieParser());
|
||||||
// this.app.use(_csrf({cookie: true}));
|
const csuf: any = _csrf();
|
||||||
|
csuf.unless = unless;
|
||||||
|
this.app.use(csuf.unless((req: Request) => {
|
||||||
|
return Config.Client.authenticationRequired === false ||
|
||||||
|
['/api/user/login', '/api/user/logout', '/api/share/login'].indexOf(req.originalUrl) !== -1 ||
|
||||||
|
(Config.Client.Sharing.enabled === true && !!req.query[QueryParams.gallery.sharingKey_query]);
|
||||||
|
}));
|
||||||
|
|
||||||
|
// enable token generation but do not check it
|
||||||
|
this.app.post(['/api/user/login', '/api/share/login'], _csrf({ignoreMethods: ['POST']}));
|
||||||
|
this.app.get(['/api/user/me', '/api/share/:' + QueryParams.gallery.sharingKey_params], _csrf({ignoreMethods: ['GET']}));
|
||||||
|
|
||||||
|
|
||||||
DiskManager.init();
|
DiskManager.init();
|
||||||
PhotoProcessing.init();
|
PhotoProcessing.init();
|
||||||
|
@ -1 +1 @@
|
|||||||
export const DataStructureVersion = 15;
|
export const DataStructureVersion = 16;
|
||||||
|
@ -13,11 +13,11 @@ export const QueryParams = {
|
|||||||
type: 'type'
|
type: 'type'
|
||||||
},
|
},
|
||||||
photo: 'p',
|
photo: 'p',
|
||||||
sharingKey_short: 'sk',
|
sharingKey_query: 'sk',
|
||||||
sharingKey_long: 'sharingKey',
|
sharingKey_params: 'sharingKey',
|
||||||
searchText: 'searchText',
|
searchText: 'searchText',
|
||||||
directory: 'directory',
|
directory: 'directory',
|
||||||
knownLastModified: 'knownLastModified',
|
knownLastModified: 'klm',
|
||||||
knownLastScanned: 'knownLastScanned'
|
knownLastScanned: 'kls'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -93,6 +93,12 @@ export class Utils {
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static canonizePath(path: string) {
|
||||||
|
return path
|
||||||
|
.replace(new RegExp('\\\\', 'g'), '/')
|
||||||
|
.replace(new RegExp('/+', 'g'), '/');
|
||||||
|
}
|
||||||
|
|
||||||
static concatUrls(...args: Array<string>) {
|
static concatUrls(...args: Array<string>) {
|
||||||
let url = '';
|
let url = '';
|
||||||
for (let i = 0; i < args.length; i++) {
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
@ -122,7 +122,7 @@ export module ServerConfig {
|
|||||||
Threading: ThreadingConfig;
|
Threading: ThreadingConfig;
|
||||||
Database: DataBaseConfig;
|
Database: DataBaseConfig;
|
||||||
Sharing: SharingConfig;
|
Sharing: SharingConfig;
|
||||||
sessionTimeout: number;
|
sessionTimeout: number; // in ms
|
||||||
Indexing: IndexingConfig;
|
Indexing: IndexingConfig;
|
||||||
photoMetadataSize: number; // only this many bites will be loaded when scanning photo for metadata
|
photoMetadataSize: number; // only this many bites will be loaded when scanning photo for metadata
|
||||||
Duplicates: DuplicatesConfig;
|
Duplicates: DuplicatesConfig;
|
||||||
|
@ -27,7 +27,7 @@ export class ErrorDTO {
|
|||||||
public detailsStr: string;
|
public detailsStr: string;
|
||||||
|
|
||||||
constructor(public code: ErrorCodes, public message?: string, public details?: any) {
|
constructor(public code: ErrorCodes, public message?: string, public details?: any) {
|
||||||
this.detailsStr = (this.details ? this.details.toString() : '');
|
this.detailsStr = (this.details ? this.details.toString() : '') || ErrorCodes[code];
|
||||||
}
|
}
|
||||||
|
|
||||||
toString(): string {
|
toString(): string {
|
||||||
|
@ -15,29 +15,35 @@ export interface UserDTO {
|
|||||||
name: string;
|
name: string;
|
||||||
password: string;
|
password: string;
|
||||||
role: UserRoles;
|
role: UserRoles;
|
||||||
|
csrfToken?: string;
|
||||||
usedSharingKey?: string;
|
usedSharingKey?: string;
|
||||||
permissions: string[]; // user can only see these permissions. if ends with *, its recursive
|
permissions: string[]; // user can only see these permissions. if ends with *, its recursive
|
||||||
}
|
}
|
||||||
|
|
||||||
export module UserDTO {
|
export module UserDTO {
|
||||||
|
|
||||||
export const isDirectoryPathAvailable = (path: string, permissions: string[], separator = '/'): boolean => {
|
export const isDirectoryPathAvailable = (path: string, permissions: string[]): boolean => {
|
||||||
if (permissions == null || permissions.length === 0 || permissions[0] === separator + '*') {
|
if (permissions == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
permissions = permissions.map(p => Utils.canonizePath(p));
|
||||||
|
path = Utils.canonizePath(path);
|
||||||
|
if (permissions.length === 0 || permissions[0] === '/*') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < permissions.length; i++) {
|
for (let i = 0; i < permissions.length; i++) {
|
||||||
let permission = permissions[i];
|
let permission = permissions[i];
|
||||||
if (permissions[i] === separator + '*') {
|
if (permissions[i] === '/*') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (permission[permission.length - 1] === '*') {
|
if (permission[permission.length - 1] === '*') {
|
||||||
permission = permission.slice(0, -1);
|
permission = permission.slice(0, -1);
|
||||||
if (path.startsWith(permission) && (!path[permission.length] || path[permission.length] === separator)) {
|
if (path.startsWith(permission) && (!path[permission.length] || path[permission.length] === '/')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (path === permission) {
|
} else if (path === permission) {
|
||||||
return true;
|
return true;
|
||||||
} else if (path === '.' && permission === separator) {
|
} else if (path === '.' && permission === '/') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,6 +52,7 @@ export module UserDTO {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const isDirectoryAvailable = (directory: DirectoryDTO, permissions: string[]): boolean => {
|
export const isDirectoryAvailable = (directory: DirectoryDTO, permissions: string[]): boolean => {
|
||||||
return isDirectoryPathAvailable(Utils.concatUrls(directory.path, directory.name), permissions);
|
return isDirectoryPathAvailable(
|
||||||
|
Utils.concatUrls(directory.path, directory.name), permissions);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,12 @@ import {Title} from '@angular/platform-browser';
|
|||||||
import {ShareService} from './ui/gallery/share.service';
|
import {ShareService} from './ui/gallery/share.service';
|
||||||
import 'hammerjs';
|
import 'hammerjs';
|
||||||
import {Subscription} from 'rxjs';
|
import {Subscription} from 'rxjs';
|
||||||
|
import {QueryParams} from '../../common/QueryParams';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-pi-gallery2',
|
selector: 'app-pi-gallery2',
|
||||||
template: `<router-outlet></router-outlet>`
|
template: `
|
||||||
|
<router-outlet></router-outlet>`
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit, OnDestroy {
|
export class AppComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
@ -51,7 +53,9 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
private toLogin() {
|
private toLogin() {
|
||||||
if (this._shareService.isSharing()) {
|
if (this._shareService.isSharing()) {
|
||||||
return this._router.navigate(['shareLogin'], {queryParams: {sk: this._shareService.getSharingKey()}});
|
const q: any = {};
|
||||||
|
q[QueryParams.gallery.sharingKey_query] = this._shareService.getSharingKey();
|
||||||
|
return this._router.navigate(['shareLogin'], {queryParams: q});
|
||||||
} else {
|
} else {
|
||||||
return this._router.navigate(['login']);
|
return this._router.navigate(['login']);
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ import {SettingsService} from './ui/settings/settings.service';
|
|||||||
import {ShareSettingsComponent} from './ui/settings/share/share.settings.component';
|
import {ShareSettingsComponent} from './ui/settings/share/share.settings.component';
|
||||||
import {BasicSettingsComponent} from './ui/settings/basic/basic.settings.component';
|
import {BasicSettingsComponent} from './ui/settings/basic/basic.settings.component';
|
||||||
import {OtherSettingsComponent} from './ui/settings/other/other.settings.component';
|
import {OtherSettingsComponent} from './ui/settings/other/other.settings.component';
|
||||||
import {HttpClientModule} from '@angular/common/http';
|
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
|
||||||
import {DefaultUrlSerializer, UrlSerializer, UrlTree} from '@angular/router';
|
import {DefaultUrlSerializer, UrlSerializer, UrlTree} from '@angular/router';
|
||||||
import {IndexingSettingsComponent} from './ui/settings/indexing/indexing.settings.component';
|
import {IndexingSettingsComponent} from './ui/settings/indexing/indexing.settings.component';
|
||||||
import {LanguageComponent} from './ui/language/language.component';
|
import {LanguageComponent} from './ui/language/language.component';
|
||||||
@ -90,6 +90,8 @@ import {JobsSettingsComponent} from './ui/settings/jobs/jobs.settings.component'
|
|||||||
import {ScheduledJobsService} from './ui/settings/scheduled-jobs.service';
|
import {ScheduledJobsService} from './ui/settings/scheduled-jobs.service';
|
||||||
import {BackendtextService} from './model/backendtext.service';
|
import {BackendtextService} from './model/backendtext.service';
|
||||||
import {JobButtonComponent} from './ui/settings/jobs/button/job-button.settings.component';
|
import {JobButtonComponent} from './ui/settings/jobs/button/job-button.settings.component';
|
||||||
|
import {ErrorInterceptor} from './model/network/helper/error.interceptor';
|
||||||
|
import {CSRFInterceptor} from './model/network/helper/csrf.interceptor';
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -212,6 +214,8 @@ export function translationsFactory(locale: string) {
|
|||||||
FileSizePipe
|
FileSizePipe
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
{provide: HTTP_INTERCEPTORS, useClass: CSRFInterceptor, multi: true},
|
||||||
|
{provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true},
|
||||||
{provide: UrlSerializer, useClass: CustomUrlSerializer},
|
{provide: UrlSerializer, useClass: CustomUrlSerializer},
|
||||||
{provide: HAMMER_GESTURE_CONFIG, useClass: MyHammerConfig},
|
{provide: HAMMER_GESTURE_CONFIG, useClass: MyHammerConfig},
|
||||||
NetworkService,
|
NetworkService,
|
||||||
|
@ -7,6 +7,7 @@ import {ShareLoginComponent} from './ui/sharelogin/share-login.component';
|
|||||||
import {QueryParams} from '../../common/QueryParams';
|
import {QueryParams} from '../../common/QueryParams';
|
||||||
import {DuplicateComponent} from './ui/duplicates/duplicates.component';
|
import {DuplicateComponent} from './ui/duplicates/duplicates.component';
|
||||||
import {FacesComponent} from './ui/faces/faces.component';
|
import {FacesComponent} from './ui/faces/faces.component';
|
||||||
|
import {AuthGuard} from './model/network/helper/auth.guard';
|
||||||
|
|
||||||
export function galleryMatcherFunction(
|
export function galleryMatcherFunction(
|
||||||
segments: UrlSegment[]): UrlMatchResult | null {
|
segments: UrlSegment[]): UrlMatchResult | null {
|
||||||
@ -32,13 +33,13 @@ export function galleryMatcherFunction(
|
|||||||
}
|
}
|
||||||
if (path === 'share') {
|
if (path === 'share') {
|
||||||
if (segments.length > 1) {
|
if (segments.length > 1) {
|
||||||
posParams[QueryParams.gallery.sharingKey_long] = segments[1];
|
posParams[QueryParams.gallery.sharingKey_params] = segments[1];
|
||||||
}
|
}
|
||||||
return {consumed: segments.slice(0, Math.min(segments.length, 2)), posParams};
|
return {consumed: segments.slice(0, Math.min(segments.length, 2)), posParams};
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// Todo: authguard - canActivate https://angular.io/api/router/CanActivate
|
|
||||||
const ROUTES: Routes = [
|
const ROUTES: Routes = [
|
||||||
{
|
{
|
||||||
path: 'login',
|
path: 'login',
|
||||||
@ -50,19 +51,23 @@ const ROUTES: Routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'admin',
|
path: 'admin',
|
||||||
component: AdminComponent
|
component: AdminComponent,
|
||||||
|
canActivate: [AuthGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'duplicates',
|
path: 'duplicates',
|
||||||
component: DuplicateComponent
|
component: DuplicateComponent,
|
||||||
|
canActivate: [AuthGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'faces',
|
path: 'faces',
|
||||||
component: FacesComponent
|
component: FacesComponent,
|
||||||
|
canActivate: [AuthGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
matcher: galleryMatcherFunction,
|
matcher: galleryMatcherFunction,
|
||||||
component: GalleryComponent
|
component: GalleryComponent,
|
||||||
|
canActivate: [AuthGuard]
|
||||||
},
|
},
|
||||||
{path: '', redirectTo: '/login', pathMatch: 'full'},
|
{path: '', redirectTo: '/login', pathMatch: 'full'},
|
||||||
{path: '**', redirectTo: '/login', pathMatch: 'full'}
|
{path: '**', redirectTo: '/login', pathMatch: 'full'}
|
||||||
|
@ -14,16 +14,16 @@ declare module ServerInject {
|
|||||||
export let user: UserDTO;
|
export let user: UserDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable({providedIn: 'root'})
|
||||||
export class AuthenticationService {
|
export class AuthenticationService {
|
||||||
|
|
||||||
public user: BehaviorSubject<UserDTO>;
|
public readonly user: BehaviorSubject<UserDTO>;
|
||||||
|
|
||||||
constructor(private _userService: UserService,
|
constructor(private _userService: UserService,
|
||||||
private _networkService: NetworkService,
|
private _networkService: NetworkService,
|
||||||
private shareService: ShareService) {
|
private shareService: ShareService) {
|
||||||
this.user = new BehaviorSubject(null);
|
this.user = new BehaviorSubject(JSON.parse(localStorage.getItem('currentUser')));
|
||||||
this.shareService.setUserObs(this.user);
|
|
||||||
// picking up session..
|
// picking up session..
|
||||||
if (this.isAuthenticated() === false && Cookie.get(CookieNames.session) != null) {
|
if (this.isAuthenticated() === false && Cookie.get(CookieNames.session) != null) {
|
||||||
if (typeof ServerInject !== 'undefined' && typeof ServerInject.user !== 'undefined') {
|
if (typeof ServerInject !== 'undefined' && typeof ServerInject.user !== 'undefined') {
|
||||||
@ -48,6 +48,17 @@ export class AuthenticationService {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO: refactor architecture remove shareService dependency
|
||||||
|
window.setTimeout(() => {
|
||||||
|
this.user.subscribe((u) => {
|
||||||
|
this.shareService.onNewUser(u);
|
||||||
|
if (u !== null) {
|
||||||
|
localStorage.setItem('currentUser', JSON.stringify(u));
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('currentUser');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async login(credential: LoginCredential): Promise<UserDTO> {
|
public async login(credential: LoginCredential): Promise<UserDTO> {
|
||||||
@ -82,9 +93,8 @@ export class AuthenticationService {
|
|||||||
try {
|
try {
|
||||||
this.user.next(await this._userService.getSessionUser());
|
this.user.next(await this._userService.getSessionUser());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
21
src/frontend/app/model/network/helper/auth.guard.ts
Normal file
21
src/frontend/app/model/network/helper/auth.guard.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
|
||||||
|
import {AuthenticationService} from '../authentication.service';
|
||||||
|
import {NavigationService} from '../../navigation.service';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable({providedIn: 'root'})
|
||||||
|
export class AuthGuard implements CanActivate {
|
||||||
|
constructor(private authenticationService: AuthenticationService,
|
||||||
|
private navigationService: NavigationService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||||
|
if (this.authenticationService.isAuthenticated() === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.navigationService.toLogin().catch(console.error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
24
src/frontend/app/model/network/helper/csrf.interceptor.ts
Normal file
24
src/frontend/app/model/network/helper/csrf.interceptor.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
|
||||||
|
import {Observable} from 'rxjs';
|
||||||
|
import {AuthenticationService} from '../authentication.service';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CSRFInterceptor implements HttpInterceptor {
|
||||||
|
constructor(private authenticationService: AuthenticationService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
|
// add authorization header with jwt token if available
|
||||||
|
const currentUser = this.authenticationService.user.value;
|
||||||
|
if (currentUser && currentUser.csrfToken) {
|
||||||
|
request = request.clone({
|
||||||
|
setHeaders: {
|
||||||
|
'CSRF-Token': `${currentUser.csrfToken}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return next.handle(request);
|
||||||
|
}
|
||||||
|
}
|
23
src/frontend/app/model/network/helper/error.interceptor.ts
Normal file
23
src/frontend/app/model/network/helper/error.interceptor.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
|
||||||
|
import {Observable, throwError} from 'rxjs';
|
||||||
|
import {catchError} from 'rxjs/operators';
|
||||||
|
import {AuthenticationService} from '../authentication.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ErrorInterceptor implements HttpInterceptor {
|
||||||
|
constructor(private authenticationService: AuthenticationService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
|
return next.handle(request).pipe(catchError(err => {
|
||||||
|
if (err.status === 401) {
|
||||||
|
// auto logout if 401 response returned from api
|
||||||
|
this.authenticationService.logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
const error = err.error.message || err.statusText;
|
||||||
|
return throwError(error);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ import {VersionService} from '../version.service';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class NetworkService {
|
export class NetworkService {
|
||||||
|
|
||||||
_apiBaseUrl = Utils.concatUrls(Config.Client.urlBase, '/api');
|
readonly _apiBaseUrl = Utils.concatUrls(Config.Client.urlBase, '/api');
|
||||||
private globalErrorHandlers: Array<(error: ErrorDTO) => boolean> = [];
|
private globalErrorHandlers: Array<(error: ErrorDTO) => boolean> = [];
|
||||||
|
|
||||||
constructor(private _http: HttpClient,
|
constructor(private _http: HttpClient,
|
||||||
@ -67,8 +67,8 @@ export class NetworkService {
|
|||||||
return this.callJson('put', url, data);
|
return this.callJson('put', url, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getJson<T>(url: string, data?: { [key: string]: any }): Promise<T> {
|
public getJson<T>(url: string, query?: { [key: string]: any }): Promise<T> {
|
||||||
return this.callJson('get', NetworkService.buildUrl(url, data));
|
return this.callJson('get', NetworkService.buildUrl(url, query));
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteJson<T>(url: string): Promise<T> {
|
public deleteJson<T>(url: string): Promise<T> {
|
||||||
|
@ -4,35 +4,38 @@ import {NetworkService} from './network.service';
|
|||||||
import {UserDTO} from '../../../../common/entities/UserDTO';
|
import {UserDTO} from '../../../../common/entities/UserDTO';
|
||||||
import {Config} from '../../../../common/config/public/Config';
|
import {Config} from '../../../../common/config/public/Config';
|
||||||
import {ShareService} from '../../ui/gallery/share.service';
|
import {ShareService} from '../../ui/gallery/share.service';
|
||||||
|
import {QueryParams} from '../../../../common/QueryParams';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
|
|
||||||
// Todo use JWT instead of costume cookie
|
|
||||||
constructor(private _networkService: NetworkService,
|
constructor(private _networkService: NetworkService,
|
||||||
private _shareService: ShareService) {
|
private _shareService: ShareService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public logout(): Promise<string> {
|
public async logout(): Promise<string> {
|
||||||
return this._networkService.postJson('/user/logout');
|
return this._networkService.postJson('/user/logout');
|
||||||
}
|
}
|
||||||
|
|
||||||
public login(credential: LoginCredential): Promise<UserDTO> {
|
public async login(credential: LoginCredential): Promise<UserDTO> {
|
||||||
return this._networkService.postJson<UserDTO>('/user/login', {'loginCredential': credential});
|
return this._networkService.postJson<UserDTO>('/user/login', {loginCredential: credential});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async shareLogin(password: string): Promise<UserDTO> {
|
public async shareLogin(password: string): Promise<UserDTO> {
|
||||||
return this._networkService.postJson<UserDTO>('/share/login?sk=' + this._shareService.getSharingKey(), {'password': password});
|
return this._networkService.postJson<UserDTO>('/share/login?' + QueryParams.gallery.sharingKey_query
|
||||||
|
+ '=' + this._shareService.getSharingKey(), {'password': password});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getSessionUser(): Promise<UserDTO> {
|
public async getSessionUser(): Promise<UserDTO> {
|
||||||
await this._shareService.wait();
|
await this._shareService.wait();
|
||||||
if (Config.Client.Sharing.enabled === true) {
|
if (Config.Client.Sharing.enabled === true) {
|
||||||
if (this._shareService.isSharing()) {
|
if (this._shareService.isSharing()) {
|
||||||
return this._networkService.getJson<UserDTO>('/user/login', {sk: this._shareService.getSharingKey()});
|
const query: any = {};
|
||||||
|
query[QueryParams.gallery.sharingKey_query] = this._shareService.getSharingKey();
|
||||||
|
return this._networkService.getJson<UserDTO>('/user/me', query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this._networkService.getJson<UserDTO>('/user/login');
|
return this._networkService.getJson<UserDTO>('/user/me');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ export class QueryService {
|
|||||||
}
|
}
|
||||||
if (Config.Client.Sharing.enabled === true) {
|
if (Config.Client.Sharing.enabled === true) {
|
||||||
if (this.shareService.isSharing()) {
|
if (this.shareService.isSharing()) {
|
||||||
query[QueryParams.gallery.sharingKey_short] = this.shareService.getSharingKey();
|
query[QueryParams.gallery.sharingKey_query] = this.shareService.getSharingKey();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return query;
|
return query;
|
||||||
@ -40,7 +40,7 @@ export class QueryService {
|
|||||||
const params: { [key: string]: any } = {};
|
const params: { [key: string]: any } = {};
|
||||||
if (Config.Client.Sharing.enabled === true) {
|
if (Config.Client.Sharing.enabled === true) {
|
||||||
if (this.shareService.isSharing()) {
|
if (this.shareService.isSharing()) {
|
||||||
params[QueryParams.gallery.sharingKey_short] = this.shareService.getSharingKey();
|
params[QueryParams.gallery.sharingKey_query] = this.shareService.getSharingKey();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (directory && directory.lastModified && directory.lastScanned &&
|
if (directory && directory.lastModified && directory.lastScanned &&
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="menuitem" *ngIf="authenticationRequired">
|
<li role="menuitem" *ngIf="authenticationRequired">
|
||||||
<a class="dropdown-item" href="#" (click)="logout()">
|
<a class="dropdown-item" (click)="logout()">
|
||||||
<span class="oi oi-account-logout"></span>
|
<span class="oi oi-account-logout"></span>
|
||||||
<ng-container i18n>Logout</ng-container>
|
<ng-container i18n>Logout</ng-container>
|
||||||
</a>
|
</a>
|
||||||
|
@ -263,7 +263,9 @@ export class GalleryCacheService {
|
|||||||
|
|
||||||
private reset() {
|
private reset() {
|
||||||
try {
|
try {
|
||||||
|
const currentUserStr = localStorage.getItem('currentUser');
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
|
localStorage.setItem('currentUser', currentUserStr);
|
||||||
localStorage.setItem(GalleryCacheService.VERSION, this.versionService.version.value);
|
localStorage.setItem(GalleryCacheService.VERSION, this.versionService.version.value);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import {SortingMethods} from '../../../../common/entities/SortingMethods';
|
|||||||
import {PhotoDTO} from '../../../../common/entities/PhotoDTO';
|
import {PhotoDTO} from '../../../../common/entities/PhotoDTO';
|
||||||
import {QueryParams} from '../../../../common/QueryParams';
|
import {QueryParams} from '../../../../common/QueryParams';
|
||||||
import {SeededRandomService} from '../../model/seededRandom.service';
|
import {SeededRandomService} from '../../model/seededRandom.service';
|
||||||
|
import {take} from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-gallery',
|
selector: 'app-gallery',
|
||||||
@ -57,10 +58,10 @@ export class GalleryComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateTimer(t: number) {
|
updateTimer(t: number) {
|
||||||
if (this.shareService.sharing.value == null) {
|
if (this.shareService.sharingSubject.value == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
t = Math.floor((this.shareService.sharing.value.expires - Date.now()) / 1000);
|
t = Math.floor((this.shareService.sharingSubject.value.expires - Date.now()) / 1000);
|
||||||
this.countDown = <any>{};
|
this.countDown = <any>{};
|
||||||
this.countDown.day = Math.floor(t / 86400);
|
this.countDown.day = Math.floor(t / 86400);
|
||||||
t -= this.countDown.day * 86400;
|
t -= this.countDown.day * 86400;
|
||||||
@ -125,10 +126,10 @@ export class GalleryComponent implements OnInit, OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params[QueryParams.gallery.sharingKey_long] && params[QueryParams.gallery.sharingKey_long] !== '') {
|
if (params[QueryParams.gallery.sharingKey_params] && params[QueryParams.gallery.sharingKey_params] !== '') {
|
||||||
const sharing = await this.shareService.getSharing();
|
const sharing = await this.shareService.currentSharing.pipe(take(1)).toPromise();
|
||||||
const qParams: { [key: string]: any } = {};
|
const qParams: { [key: string]: any } = {};
|
||||||
qParams[QueryParams.gallery.sharingKey_short] = this.shareService.getSharingKey();
|
qParams[QueryParams.gallery.sharingKey_query] = this.shareService.getSharingKey();
|
||||||
this._router.navigate(['/gallery', sharing.path], {queryParams: qParams}).catch(console.error);
|
this._router.navigate(['/gallery', sharing.path], {queryParams: qParams}).catch(console.error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO';
|
|||||||
import {SearchTypes} from '../../../../common/entities/AutoCompleteItem';
|
import {SearchTypes} from '../../../../common/entities/AutoCompleteItem';
|
||||||
import {GalleryCacheService} from './cache.gallery.service';
|
import {GalleryCacheService} from './cache.gallery.service';
|
||||||
import {BehaviorSubject} from 'rxjs';
|
import {BehaviorSubject} from 'rxjs';
|
||||||
import {SharingDTO} from '../../../../common/entities/SharingDTO';
|
|
||||||
import {Config} from '../../../../common/config/public/Config';
|
import {Config} from '../../../../common/config/public/Config';
|
||||||
import {ShareService} from './share.service';
|
import {ShareService} from './share.service';
|
||||||
import {NavigationService} from '../../model/navigation.service';
|
import {NavigationService} from '../../model/navigation.service';
|
||||||
@ -79,7 +78,7 @@ export class GalleryService {
|
|||||||
const params: { [key: string]: any } = {};
|
const params: { [key: string]: any } = {};
|
||||||
if (Config.Client.Sharing.enabled === true) {
|
if (Config.Client.Sharing.enabled === true) {
|
||||||
if (this._shareService.isSharing()) {
|
if (this._shareService.isSharing()) {
|
||||||
params[QueryParams.gallery.sharingKey_short] = this._shareService.getSharingKey();
|
params[QueryParams.gallery.sharingKey_query] = this._shareService.getSharingKey();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,9 +198,6 @@ export class GalleryService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getSharing(sharingKey: string): Promise<SharingDTO> {
|
|
||||||
return this.networkService.getJson<SharingDTO>('/share/' + sharingKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
isSearchResult(): boolean {
|
isSearchResult(): boolean {
|
||||||
|
@ -2,25 +2,27 @@ import {Injectable} from '@angular/core';
|
|||||||
import {NetworkService} from '../../model/network/network.service';
|
import {NetworkService} from '../../model/network/network.service';
|
||||||
import {CreateSharingDTO, SharingDTO} from '../../../../common/entities/SharingDTO';
|
import {CreateSharingDTO, SharingDTO} from '../../../../common/entities/SharingDTO';
|
||||||
import {Router, RoutesRecognized} from '@angular/router';
|
import {Router, RoutesRecognized} from '@angular/router';
|
||||||
import {BehaviorSubject, Observable} from 'rxjs';
|
import {BehaviorSubject} from 'rxjs';
|
||||||
|
import {distinctUntilChanged, filter} from 'rxjs/operators';
|
||||||
import {QueryParams} from '../../../../common/QueryParams';
|
import {QueryParams} from '../../../../common/QueryParams';
|
||||||
import {UserDTO} from '../../../../common/entities/UserDTO';
|
import {UserDTO} from '../../../../common/entities/UserDTO';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ShareService {
|
export class ShareService {
|
||||||
|
|
||||||
public sharing: BehaviorSubject<SharingDTO>;
|
|
||||||
param: string = null;
|
param: string = null;
|
||||||
queryParam: string = null;
|
queryParam: string = null;
|
||||||
sharingKey: string = null;
|
sharingKey: string = null;
|
||||||
inited = false;
|
inited = false;
|
||||||
public ReadyPR: Promise<void>;
|
public ReadyPR: Promise<void>;
|
||||||
private resolve: () => void;
|
public sharingSubject: BehaviorSubject<SharingDTO> = new BehaviorSubject(null);
|
||||||
|
public currentSharing = this.sharingSubject
|
||||||
|
.asObservable().pipe(filter(s => s !== null)).pipe(distinctUntilChanged());
|
||||||
|
|
||||||
|
private resolve: () => void;
|
||||||
|
|
||||||
constructor(private networkService: NetworkService,
|
constructor(private networkService: NetworkService,
|
||||||
private router: Router) {
|
private router: Router) {
|
||||||
this.sharing = new BehaviorSubject(null);
|
|
||||||
this.ReadyPR = new Promise((resolve: () => void) => {
|
this.ReadyPR = new Promise((resolve: () => void) => {
|
||||||
if (this.inited === true) {
|
if (this.inited === true) {
|
||||||
return resolve();
|
return resolve();
|
||||||
@ -28,15 +30,15 @@ export class ShareService {
|
|||||||
this.resolve = resolve;
|
this.resolve = resolve;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.router.events.subscribe(val => {
|
this.router.events.subscribe(async val => {
|
||||||
if (val instanceof RoutesRecognized) {
|
if (val instanceof RoutesRecognized) {
|
||||||
this.param = val.state.root.firstChild.params[QueryParams.gallery.sharingKey_long] || null;
|
this.param = val.state.root.firstChild.params[QueryParams.gallery.sharingKey_params] || null;
|
||||||
this.queryParam = val.state.root.firstChild.queryParams[QueryParams.gallery.sharingKey_short] || null;
|
this.queryParam = val.state.root.firstChild.queryParams[QueryParams.gallery.sharingKey_query] || null;
|
||||||
|
|
||||||
const changed = this.sharingKey !== (this.param || this.queryParam);
|
const changed = this.sharingKey !== (this.param || this.queryParam);
|
||||||
if (changed) {
|
if (changed) {
|
||||||
this.sharingKey = this.param || this.queryParam || this.sharingKey;
|
this.sharingKey = this.param || this.queryParam || this.sharingKey;
|
||||||
this.getSharing();
|
await this.getSharing();
|
||||||
}
|
}
|
||||||
if (this.resolve) {
|
if (this.resolve) {
|
||||||
this.resolve();
|
this.resolve();
|
||||||
@ -50,22 +52,21 @@ export class ShareService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setUserObs(userOB: Observable<UserDTO>) {
|
|
||||||
|
|
||||||
userOB.subscribe((user) => {
|
onNewUser = async (user: UserDTO) => {
|
||||||
if (user && !!user.usedSharingKey) {
|
if (user && !!user.usedSharingKey) {
|
||||||
if (user.usedSharingKey !== this.sharingKey) {
|
if (user.usedSharingKey !== this.sharingKey ||
|
||||||
this.sharingKey = user.usedSharingKey;
|
this.sharingSubject.value == null) {
|
||||||
this.getSharing();
|
this.sharingKey = user.usedSharingKey;
|
||||||
}
|
await this.getSharing();
|
||||||
if (this.resolve) {
|
|
||||||
this.resolve();
|
|
||||||
this.resolve = null;
|
|
||||||
this.inited = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
if (this.resolve) {
|
||||||
}
|
this.resolve();
|
||||||
|
this.resolve = null;
|
||||||
|
this.inited = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
public wait(): Promise<void> {
|
public wait(): Promise<void> {
|
||||||
@ -104,9 +105,13 @@ export class ShareService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getSharing(): Promise<SharingDTO> {
|
private async getSharing(): Promise<void> {
|
||||||
const sharing = await this.networkService.getJson<SharingDTO>('/share/' + this.getSharingKey());
|
try {
|
||||||
this.sharing.next(sharing);
|
this.sharingSubject.next(null);
|
||||||
return sharing;
|
const sharing = await this.networkService.getJson<SharingDTO>('/share/' + this.getSharingKey());
|
||||||
|
this.sharingSubject.next(sharing);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,91 +13,101 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="row">
|
<form #shareForm="ngForm" class="form-horizontal">
|
||||||
<div class="col-7 col-sm-9">
|
<div class="row">
|
||||||
<input id="shareLink"
|
<div class="col-7 col-sm-9">
|
||||||
name="shareLink"
|
<input id="shareLink"
|
||||||
placeholder="link"
|
name="shareLink"
|
||||||
class="form-control input-md"
|
placeholder="link"
|
||||||
type="text"
|
class="form-control input-md"
|
||||||
[ngModel]="url">
|
type="text"
|
||||||
|
[ngModel]="url">
|
||||||
|
</div>
|
||||||
|
<div class="col-5 col-sm-3">
|
||||||
|
<button id="copyButton" name="copyButton"
|
||||||
|
ngxClipboard
|
||||||
|
[cbContent]="url"
|
||||||
|
(cbOnSuccess)="onCopy()"
|
||||||
|
[disabled]="!shareForm.form.valid"
|
||||||
|
class="btn btn-primary btn-block" i18n>Copy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-5 col-sm-3">
|
<hr/>
|
||||||
<button id="copyButton" name="copyButton"
|
<div class="row">
|
||||||
ngxClipboard [cbContent]="url"
|
<div class="col-4">
|
||||||
(cbOnSuccess)="onCopy()"
|
<label class="control-label" for="sharing-dir" i18n>Sharing:</label>
|
||||||
class="btn btn-primary btn-block" i18n>Copy
|
</div>
|
||||||
</button>
|
<div class="col-8">
|
||||||
|
<input disabled type="text"
|
||||||
|
name="sharing-dir"
|
||||||
|
id="sharing-dir"
|
||||||
|
class="full-width form-control"
|
||||||
|
[ngModel]="currentDir">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<hr/>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-4">
|
|
||||||
<label class="control-label" i18n>Sharing:</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8">
|
|
||||||
<input disabled type="text"
|
|
||||||
class="full-width form-control"
|
|
||||||
[ngModel]="currentDir">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label class="control-label" i18n>Include subfolders:</label>
|
<label class="control-label" for="includeSubfolders" i18n>Include subfolders:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<bSwitch
|
||||||
|
class="switch"
|
||||||
|
name="includeSubfolders"
|
||||||
|
id="includeSubfolders"
|
||||||
|
[switch-on-color]="'success'"
|
||||||
|
[switch-inverse]="true"
|
||||||
|
[switch-off-text]="text.No"
|
||||||
|
[switch-on-text]="text.Yes"
|
||||||
|
[switch-handle-width]="100"
|
||||||
|
[switch-label-width]="20"
|
||||||
|
(change)="update()"
|
||||||
|
[(ngModel)]="input.includeSubfolders">
|
||||||
|
</bSwitch>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8">
|
|
||||||
<bSwitch
|
|
||||||
class="switch"
|
|
||||||
name="includeSubfolders"
|
|
||||||
[switch-on-color]="'success'"
|
|
||||||
[switch-inverse]="'inverse'"
|
|
||||||
[switch-off-text]="text.No"
|
|
||||||
[switch-on-text]="text.Yes"
|
|
||||||
[switch-handle-width]="'100'"
|
|
||||||
[switch-label-width]="'20'"
|
|
||||||
(change)="update()"
|
|
||||||
[(ngModel)]="input.includeSubfolders">
|
|
||||||
</bSwitch>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label class="control-label">
|
<label class="control-label" for="share-password">
|
||||||
<ng-container i18n>Password</ng-container>
|
<ng-container i18n>Password</ng-container>*:
|
||||||
:
|
</label>
|
||||||
</label>
|
</div>
|
||||||
|
<div class="col-8" *ngIf="passwordProtection">
|
||||||
|
<input id="share-password"
|
||||||
|
class="form-control"
|
||||||
|
name="share-password"
|
||||||
|
type="password"
|
||||||
|
(change)="update()"
|
||||||
|
[(ngModel)]="input.password"
|
||||||
|
i18n-placeholder
|
||||||
|
placeholder="Password"
|
||||||
|
required>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8">
|
|
||||||
<input id="password"
|
|
||||||
class="form-control"
|
|
||||||
type="password"
|
|
||||||
(change)="update()"
|
|
||||||
[(ngModel)]="input.password"
|
|
||||||
i18n-placeholder
|
|
||||||
placeholder="Password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label class="control-label" i18n>Valid:</label>
|
<label class="control-label" for="valid-from" i18n>Valid:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-4" style="padding-right: 1px">
|
||||||
|
<input class="form-control" [(ngModel)]="input.valid.amount" (change)="update()"
|
||||||
|
name="valid-from"
|
||||||
|
id="valid-from"
|
||||||
|
type="number" min="0" step="1"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-4" style="padding-left: 1px">
|
||||||
|
<select class="form-control"
|
||||||
|
[(ngModel)]="input.valid.type" (change)="update()" name="valid-to"
|
||||||
|
required>
|
||||||
|
<option [ngValue]="ValidityTypes.Minutes" i18n>Minutes</option>
|
||||||
|
<option [ngValue]="ValidityTypes.Hours" i18n>Hours</option>
|
||||||
|
<option [ngValue]="ValidityTypes.Days" i18n>Days</option>
|
||||||
|
<option [ngValue]="ValidityTypes.Months" i18n>Months</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4" style="padding-right: 1px">
|
</form>
|
||||||
<input class="form-control" [(ngModel)]="input.valid.amount" (change)="update()"
|
|
||||||
name="validAmount"
|
|
||||||
type="number" min="0" step="1"/>
|
|
||||||
</div>
|
|
||||||
<div class="col-4" style="padding-left: 1px">
|
|
||||||
<select class="form-control" [(ngModel)]="input.valid.type" (change)="update()" name="validType"
|
|
||||||
required>
|
|
||||||
<option [ngValue]="ValidityTypes.Minutes" i18n>Minutes</option>
|
|
||||||
<option [ngValue]="ValidityTypes.Hours" i18n>Hours</option>
|
|
||||||
<option [ngValue]="ValidityTypes.Days" i18n>Days</option>
|
|
||||||
<option [ngValue]="ValidityTypes.Months" i18n>Months</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -29,13 +29,13 @@ export class GalleryShareComponent implements OnInit, OnDestroy {
|
|||||||
amount: 30,
|
amount: 30,
|
||||||
type: ValidityTypes.Days
|
type: ValidityTypes.Days
|
||||||
},
|
},
|
||||||
password: ''
|
password: <string>null
|
||||||
};
|
};
|
||||||
currentDir = '';
|
currentDir = '';
|
||||||
sharing: SharingDTO = null;
|
sharing: SharingDTO = null;
|
||||||
contentSubscription: Subscription = null;
|
contentSubscription: Subscription = null;
|
||||||
passwordProtection = false;
|
readonly passwordProtection = Config.Client.Sharing.passwordProtected;
|
||||||
ValidityTypes: any;
|
readonly ValidityTypes = ValidityTypes;
|
||||||
|
|
||||||
modalRef: BsModalRef;
|
modalRef: BsModalRef;
|
||||||
|
|
||||||
@ -49,7 +49,6 @@ export class GalleryShareComponent implements OnInit, OnDestroy {
|
|||||||
private _notification: NotificationService,
|
private _notification: NotificationService,
|
||||||
public i18n: I18n,
|
public i18n: I18n,
|
||||||
private modalService: BsModalService) {
|
private modalService: BsModalService) {
|
||||||
this.ValidityTypes = ValidityTypes;
|
|
||||||
|
|
||||||
this.text.Yes = i18n('Yes');
|
this.text.Yes = i18n('Yes');
|
||||||
this.text.No = i18n('No');
|
this.text.No = i18n('No');
|
||||||
@ -64,7 +63,6 @@ export class GalleryShareComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
this.currentDir = Utils.concatUrls((<DirectoryDTO>content.directory).path, (<DirectoryDTO>content.directory).name);
|
this.currentDir = Utils.concatUrls((<DirectoryDTO>content.directory).path, (<DirectoryDTO>content.directory).name);
|
||||||
});
|
});
|
||||||
this.passwordProtection = Config.Client.Sharing.passwordProtected;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
@ -155,7 +155,7 @@ export abstract class SettingsComponent<T extends { [key: string]: any }, S exte
|
|||||||
this.inProgress = false;
|
this.inProgress = false;
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.error(err);
|
||||||
if (err.message) {
|
if (err.message) {
|
||||||
this.error = (<ErrorDTO>err).message;
|
this.error = (<ErrorDTO>err).message;
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ export class JobButtonComponent {
|
|||||||
this.notification.info(this.i18n('Stopping job') + ': ' + this.backendTextService.getJobName(this.jobName));
|
this.notification.info(this.i18n('Stopping job') + ': ' + this.backendTextService.getJobName(this.jobName));
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.error(err);
|
||||||
if (err.message) {
|
if (err.message) {
|
||||||
this.error.emit((<ErrorDTO>err).message);
|
this.error.emit((<ErrorDTO>err).message);
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ export class UserMangerSettingsComponent implements OnInit, ISettingsComponent {
|
|||||||
this.notification.success(this.i18n('Password protection disabled'), this.i18n('Success'));
|
this.notification.success(this.i18n('Password protection disabled'), this.i18n('Success'));
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.error(err);
|
||||||
if (err.message) {
|
if (err.message) {
|
||||||
this.error = (<ErrorDTO>err).message;
|
this.error = (<ErrorDTO>err).message;
|
||||||
}
|
}
|
||||||
|
33
test/backend/integration/routers/RouteTestingHelper.ts
Normal file
33
test/backend/integration/routers/RouteTestingHelper.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import {SharingDTO} from '../../../../src/common/entities/SharingDTO';
|
||||||
|
import {ObjectManagers} from '../../../../src/backend/model/ObjectManagers';
|
||||||
|
import {UserDTO, UserRoles} from '../../../../src/common/entities/UserDTO';
|
||||||
|
import {Utils} from '../../../../src/common/Utils';
|
||||||
|
|
||||||
|
export class RouteTestingHelper {
|
||||||
|
|
||||||
|
|
||||||
|
static async createSharing(testUser: UserDTO, password: string = null): Promise<SharingDTO> {
|
||||||
|
const sharing = <SharingDTO>{
|
||||||
|
sharingKey: 'sharing_test_key_' + Date.now(),
|
||||||
|
path: 'test',
|
||||||
|
expires: Date.now() + 1000,
|
||||||
|
timeStamp: Date.now(),
|
||||||
|
includeSubfolders: false,
|
||||||
|
creator: testUser
|
||||||
|
};
|
||||||
|
if (password) {
|
||||||
|
sharing.password = password;
|
||||||
|
}
|
||||||
|
await ObjectManagers.getInstance().SharingManager.createSharing(Utils.clone(sharing)); // do not rewrite password
|
||||||
|
return sharing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getExpectedSharingUser(sharing: SharingDTO): UserDTO {
|
||||||
|
return <UserDTO>{
|
||||||
|
name: 'Guest',
|
||||||
|
role: UserRoles.LimitedGuest,
|
||||||
|
permissions: [sharing.path],
|
||||||
|
usedSharingKey: sharing.sharingKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
121
test/backend/integration/routers/SharingRouter.ts
Normal file
121
test/backend/integration/routers/SharingRouter.ts
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import {Config} from '../../../../src/common/config/private/Config';
|
||||||
|
import {ServerConfig} from '../../../../src/common/config/private/IPrivateConfig';
|
||||||
|
import {Server} from '../../../../src/backend/server';
|
||||||
|
import {LoginCredential} from '../../../../src/common/entities/LoginCredential';
|
||||||
|
import {UserDTO, UserRoles} from '../../../../src/common/entities/UserDTO';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as util from 'util';
|
||||||
|
import * as rimraf from 'rimraf';
|
||||||
|
import {SQLConnection} from '../../../../src/backend/model/database/sql/SQLConnection';
|
||||||
|
import {ObjectManagers} from '../../../../src/backend/model/ObjectManagers';
|
||||||
|
import {Utils} from '../../../../src/common/Utils';
|
||||||
|
import {SuperAgentStatic} from 'superagent';
|
||||||
|
import {RouteTestingHelper} from './RouteTestingHelper';
|
||||||
|
import {QueryParams} from '../../../../src/common/QueryParams';
|
||||||
|
import {ErrorCodes} from '../../../../src/common/entities/Error';
|
||||||
|
|
||||||
|
|
||||||
|
process.env.NODE_ENV = 'test';
|
||||||
|
const chai: any = require('chai');
|
||||||
|
const chaiHttp = require('chai-http');
|
||||||
|
const should = chai.should();
|
||||||
|
chai.use(chaiHttp);
|
||||||
|
|
||||||
|
const rimrafPR = util.promisify(rimraf);
|
||||||
|
describe('Sharing', () => {
|
||||||
|
|
||||||
|
const testUser: UserDTO = {
|
||||||
|
id: 1,
|
||||||
|
name: 'test',
|
||||||
|
password: 'test',
|
||||||
|
role: UserRoles.User,
|
||||||
|
permissions: null
|
||||||
|
};
|
||||||
|
const {password: _pass, ...expectedUser} = testUser;
|
||||||
|
const tempDir = path.join(__dirname, '../../tmp');
|
||||||
|
let server: Server;
|
||||||
|
const setUp = async () => {
|
||||||
|
await rimrafPR(tempDir);
|
||||||
|
Config.Client.authenticationRequired = true;
|
||||||
|
Config.Server.Threading.enabled = false;
|
||||||
|
Config.Client.Sharing.enabled = true;
|
||||||
|
Config.Server.Database.type = ServerConfig.DatabaseType.sqlite;
|
||||||
|
Config.Server.Database.dbFolder = tempDir;
|
||||||
|
|
||||||
|
server = new Server();
|
||||||
|
await server.onStarted.wait();
|
||||||
|
|
||||||
|
await ObjectManagers.InitSQLManagers();
|
||||||
|
await ObjectManagers.getInstance().UserManager.createUser(Utils.clone(testUser));
|
||||||
|
await SQLConnection.close();
|
||||||
|
};
|
||||||
|
const tearDown = async () => {
|
||||||
|
await SQLConnection.close();
|
||||||
|
await rimrafPR(tempDir);
|
||||||
|
};
|
||||||
|
|
||||||
|
const shouldBeValidUser = (result: any, user: any) => {
|
||||||
|
|
||||||
|
result.should.have.status(200);
|
||||||
|
result.body.should.be.a('object');
|
||||||
|
should.equal(result.body.error, null);
|
||||||
|
result.body.result.csrfToken.should.be.a('string');
|
||||||
|
const {csrfToken, ...u} = result.body.result;
|
||||||
|
u.should.deep.equal(user);
|
||||||
|
};
|
||||||
|
|
||||||
|
const shareLogin = async (srv: Server, sharingKey: string, password?: string): Promise<any> => {
|
||||||
|
return (chai.request(srv.App) as SuperAgentStatic)
|
||||||
|
.post('/api/share/login?' + QueryParams.gallery.sharingKey_query + '=' + sharingKey)
|
||||||
|
.send({password});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const login = async (srv: Server): Promise<any> => {
|
||||||
|
const result = await (chai.request(srv.App) as SuperAgentStatic)
|
||||||
|
.post('/api/user/login')
|
||||||
|
.send({
|
||||||
|
loginCredential: <LoginCredential>{
|
||||||
|
password: testUser.password,
|
||||||
|
username: testUser.name,
|
||||||
|
rememberMe: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
shouldBeValidUser(result, expectedUser);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
describe('/POST share/login', () => {
|
||||||
|
|
||||||
|
beforeEach(setUp);
|
||||||
|
afterEach(tearDown);
|
||||||
|
|
||||||
|
it('should login with passworded share', async () => {
|
||||||
|
const sharing = await RouteTestingHelper.createSharing(testUser, 'secret_pass');
|
||||||
|
const res = await shareLogin(server, sharing.sharingKey, sharing.password);
|
||||||
|
shouldBeValidUser(res, RouteTestingHelper.getExpectedSharingUser(sharing));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not login with passworded share without password', async () => {
|
||||||
|
const sharing = await RouteTestingHelper.createSharing(testUser, 'secret_pass');
|
||||||
|
const result = await shareLogin(server, sharing.sharingKey);
|
||||||
|
|
||||||
|
result.should.have.status(401);
|
||||||
|
result.body.should.be.a('object');
|
||||||
|
result.body.error.should.be.a('object');
|
||||||
|
should.equal(result.body.error.code, ErrorCodes.CREDENTIAL_NOT_FOUND);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should login with no-password share', async () => {
|
||||||
|
const sharing = await RouteTestingHelper.createSharing(testUser,);
|
||||||
|
const res = await shareLogin(server, sharing.sharingKey, sharing.password);
|
||||||
|
shouldBeValidUser(res, RouteTestingHelper.getExpectedSharingUser(sharing));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
@ -1,12 +1,19 @@
|
|||||||
import {Server} from '../../../../src/backend/server';
|
|
||||||
import {Config} from '../../../../src/common/config/private/Config';
|
import {Config} from '../../../../src/common/config/private/Config';
|
||||||
|
import {ServerConfig} from '../../../../src/common/config/private/IPrivateConfig';
|
||||||
|
import {Server} from '../../../../src/backend/server';
|
||||||
import {LoginCredential} from '../../../../src/common/entities/LoginCredential';
|
import {LoginCredential} from '../../../../src/common/entities/LoginCredential';
|
||||||
import {UserDTO, UserRoles} from '../../../../src/common/entities/UserDTO';
|
import {UserDTO, UserRoles} from '../../../../src/common/entities/UserDTO';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
import * as rimraf from 'rimraf';
|
import * as rimraf from 'rimraf';
|
||||||
import {ServerConfig} from '../../../../src/common/config/private/IPrivateConfig';
|
|
||||||
import {SQLConnection} from '../../../../src/backend/model/database/sql/SQLConnection';
|
import {SQLConnection} from '../../../../src/backend/model/database/sql/SQLConnection';
|
||||||
|
import {ObjectManagers} from '../../../../src/backend/model/ObjectManagers';
|
||||||
|
import {QueryParams} from '../../../../src/common/QueryParams';
|
||||||
|
import {Utils} from '../../../../src/common/Utils';
|
||||||
|
import {SuperAgentStatic} from 'superagent';
|
||||||
|
import {RouteTestingHelper} from './RouteTestingHelper';
|
||||||
|
import {ErrorCodes} from '../../../../src/common/entities/Error';
|
||||||
|
|
||||||
|
|
||||||
process.env.NODE_ENV = 'test';
|
process.env.NODE_ENV = 'test';
|
||||||
const chai: any = require('chai');
|
const chai: any = require('chai');
|
||||||
@ -17,33 +24,170 @@ chai.use(chaiHttp);
|
|||||||
const rimrafPR = util.promisify(rimraf);
|
const rimrafPR = util.promisify(rimraf);
|
||||||
describe('UserRouter', () => {
|
describe('UserRouter', () => {
|
||||||
|
|
||||||
|
const testUser: UserDTO = {
|
||||||
|
id: 1,
|
||||||
|
name: 'test',
|
||||||
|
password: 'test',
|
||||||
|
role: UserRoles.User,
|
||||||
|
permissions: null
|
||||||
|
};
|
||||||
|
const {password, ...expectedUser} = testUser;
|
||||||
const tempDir = path.join(__dirname, '../../tmp');
|
const tempDir = path.join(__dirname, '../../tmp');
|
||||||
beforeEach(async () => {
|
let server: Server;
|
||||||
|
const setUp = async () => {
|
||||||
await rimrafPR(tempDir);
|
await rimrafPR(tempDir);
|
||||||
Config.Server.Threading.enabled = false;
|
Config.Server.Threading.enabled = false;
|
||||||
Config.Server.Database.type = ServerConfig.DatabaseType.sqlite;
|
Config.Server.Database.type = ServerConfig.DatabaseType.sqlite;
|
||||||
Config.Server.Database.dbFolder = tempDir;
|
Config.Server.Database.dbFolder = tempDir;
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
afterEach(async () => {
|
server = new Server();
|
||||||
|
await server.onStarted.wait();
|
||||||
|
await ObjectManagers.InitSQLManagers();
|
||||||
|
await ObjectManagers.getInstance().UserManager.createUser(Utils.clone(testUser));
|
||||||
|
await SQLConnection.close();
|
||||||
|
};
|
||||||
|
const tearDown = async () => {
|
||||||
await SQLConnection.close();
|
await SQLConnection.close();
|
||||||
await rimrafPR(tempDir);
|
await rimrafPR(tempDir);
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkUserResult = (result: any, user: any) => {
|
||||||
|
|
||||||
|
result.should.have.status(200);
|
||||||
|
result.body.should.be.a('object');
|
||||||
|
should.equal(result.body.error, null);
|
||||||
|
result.body.result.csrfToken.should.be.a('string');
|
||||||
|
const {csrfToken, ...u} = result.body.result;
|
||||||
|
u.should.deep.equal(user);
|
||||||
|
};
|
||||||
|
|
||||||
|
const login = async (srv: Server): Promise<any> => {
|
||||||
|
const result = await (chai.request(srv.App) as SuperAgentStatic)
|
||||||
|
.post('/api/user/login')
|
||||||
|
.send({
|
||||||
|
loginCredential: <LoginCredential>{
|
||||||
|
password: testUser.password,
|
||||||
|
username: testUser.name,
|
||||||
|
rememberMe: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
checkUserResult(result, expectedUser);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
describe('/POST user/login', () => {
|
||||||
|
beforeEach(setUp);
|
||||||
|
afterEach(tearDown);
|
||||||
|
it('it should login', async () => {
|
||||||
|
Config.Client.authenticationRequired = true;
|
||||||
|
await login(server);
|
||||||
|
|
||||||
|
});
|
||||||
|
it('it skip login', async () => {
|
||||||
|
Config.Client.authenticationRequired = false;
|
||||||
|
const result = await chai.request(server.App)
|
||||||
|
.post('/api/user/login');
|
||||||
|
|
||||||
|
result.res.should.have.status(404);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('/POST login', () => {
|
|
||||||
it('it should GET all the books', async () => {
|
|
||||||
const srv = new Server();
|
|
||||||
await srv.onStarted.wait();
|
|
||||||
const result = await chai.request(srv.App)
|
|
||||||
.post('/api/user/login')
|
|
||||||
.send({loginCredential: <LoginCredential>{password: 'admin', username: 'admin', rememberMe: false}});
|
|
||||||
|
|
||||||
result.res.should.have.status(200);
|
describe('/GET user/me', () => {
|
||||||
|
beforeEach(setUp);
|
||||||
|
afterEach(tearDown);
|
||||||
|
it('it should GET the authenticated user', async () => {
|
||||||
|
Config.Client.authenticationRequired = true;
|
||||||
|
|
||||||
|
const loginRes = await login(server);
|
||||||
|
|
||||||
|
const result = await chai.request(server.App)
|
||||||
|
.get('/api/user/me')
|
||||||
|
.set('Cookie', loginRes.res.headers['set-cookie'])
|
||||||
|
.set('CSRF-Token', loginRes.body.result.csrfToken);
|
||||||
|
|
||||||
|
checkUserResult(result, expectedUser);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should not authenticate', async () => {
|
||||||
|
Config.Client.authenticationRequired = true;
|
||||||
|
|
||||||
|
const result = await chai.request(server.App)
|
||||||
|
.get('/api/user/me');
|
||||||
|
|
||||||
|
result.res.should.have.status(401);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should authenticate as user with sharing key', async () => {
|
||||||
|
Config.Client.authenticationRequired = true;
|
||||||
|
Config.Client.Sharing.enabled = true;
|
||||||
|
|
||||||
|
const sharingKey = (await RouteTestingHelper.createSharing(testUser)).sharingKey;
|
||||||
|
|
||||||
|
|
||||||
|
const loginRes = await login(server);
|
||||||
|
const q: any = {};
|
||||||
|
q[QueryParams.gallery.sharingKey_query] = sharingKey;
|
||||||
|
const result = await chai.request(server.App)
|
||||||
|
.get('/api/user/me?' + QueryParams.gallery.sharingKey_query + '=' + sharingKey)
|
||||||
|
.set('Cookie', loginRes.res.headers['set-cookie'])
|
||||||
|
.set('CSRF-Token', loginRes.body.result.csrfToken);
|
||||||
|
|
||||||
|
// should return with logged in user, not limited sharing one
|
||||||
|
checkUserResult(result, expectedUser);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('it should authenticate with sharing key', async () => {
|
||||||
|
Config.Client.authenticationRequired = true;
|
||||||
|
Config.Client.Sharing.enabled = true;
|
||||||
|
const sharing = (await RouteTestingHelper.createSharing(testUser));
|
||||||
|
|
||||||
|
|
||||||
|
const q: any = {};
|
||||||
|
q[QueryParams.gallery.sharingKey_query] = sharing.sharingKey;
|
||||||
|
const result = await chai.request(server.App)
|
||||||
|
.get('/api/user/me?' + QueryParams.gallery.sharingKey_query + '=' + sharing.sharingKey);
|
||||||
|
|
||||||
|
|
||||||
|
checkUserResult(result, RouteTestingHelper.getExpectedSharingUser(sharing));
|
||||||
|
});
|
||||||
|
it('it should not authenticate with sharing key without password', async () => {
|
||||||
|
Config.Client.authenticationRequired = true;
|
||||||
|
Config.Client.Sharing.enabled = true;
|
||||||
|
const sharing = (await RouteTestingHelper.createSharing(testUser, 'pass_secret'));
|
||||||
|
|
||||||
|
|
||||||
|
const q: any = {};
|
||||||
|
q[QueryParams.gallery.sharingKey_query] = sharing.sharingKey;
|
||||||
|
const result = await chai.request(server.App)
|
||||||
|
.get('/api/user/me?' + QueryParams.gallery.sharingKey_query + '=' + sharing.sharingKey);
|
||||||
|
|
||||||
|
|
||||||
|
result.should.have.status(401);
|
||||||
result.body.should.be.a('object');
|
result.body.should.be.a('object');
|
||||||
should.equal(result.body.error, null);
|
result.body.error.should.be.a('object');
|
||||||
result.body.result.should.deep.equal(<UserDTO>{id: 1, name: 'admin', role: UserRoles.Admin, permissions: null});
|
should.equal(result.body.error.code, ErrorCodes.NOT_AUTHENTICATED);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should authenticate as guest', async () => {
|
||||||
|
Config.Client.authenticationRequired = false;
|
||||||
|
|
||||||
|
const result = await chai.request(server.App)
|
||||||
|
.get('/api/user/me');
|
||||||
|
|
||||||
|
const expectedGuestUser = <UserDTO>{
|
||||||
|
name: UserRoles[Config.Client.unAuthenticatedUserRole],
|
||||||
|
role: Config.Client.unAuthenticatedUserRole
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
checkUserResult(result, expectedGuestUser);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -59,9 +59,7 @@ describe('Authentication middleware', () => {
|
|||||||
describe('authorisePath', () => {
|
describe('authorisePath', () => {
|
||||||
|
|
||||||
const req = {
|
const req = {
|
||||||
session: {
|
user: {permissions: <string[]>null},
|
||||||
user: {permissions: <string[]>null}
|
|
||||||
},
|
|
||||||
sessionOptions: {},
|
sessionOptions: {},
|
||||||
query: {},
|
query: {},
|
||||||
params: {
|
params: {
|
||||||
@ -82,14 +80,14 @@ describe('Authentication middleware', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
it('should catch unauthorized path usage', async () => {
|
it('should catch unauthorized path usage', async () => {
|
||||||
req.session.user.permissions = [path.normalize('/sub/subsub')];
|
req.user.permissions = [path.normalize('/sub/subsub')];
|
||||||
expect(await test('/sub/subsub')).to.be.eql('ok');
|
expect(await test('/sub/subsub')).to.be.eql('ok');
|
||||||
expect(await test('/test')).to.be.eql(403);
|
expect(await test('/test')).to.be.eql(403);
|
||||||
expect(await test('/')).to.be.eql(403);
|
expect(await test('/')).to.be.eql(403);
|
||||||
expect(await test('/sub/test')).to.be.eql(403);
|
expect(await test('/sub/test')).to.be.eql(403);
|
||||||
expect(await test('/sub/subsub/test')).to.be.eql(403);
|
expect(await test('/sub/subsub/test')).to.be.eql(403);
|
||||||
expect(await test('/sub/subsub/test/test2')).to.be.eql(403);
|
expect(await test('/sub/subsub/test/test2')).to.be.eql(403);
|
||||||
req.session.user.permissions = [path.normalize('/sub/subsub'), path.normalize('/sub/subsub2')];
|
req.user.permissions = [path.normalize('/sub/subsub'), path.normalize('/sub/subsub2')];
|
||||||
expect(await test('/sub/subsub2')).to.be.eql('ok');
|
expect(await test('/sub/subsub2')).to.be.eql('ok');
|
||||||
expect(await test('/sub/subsub')).to.be.eql('ok');
|
expect(await test('/sub/subsub')).to.be.eql('ok');
|
||||||
expect(await test('/test')).to.be.eql(403);
|
expect(await test('/test')).to.be.eql(403);
|
||||||
@ -97,7 +95,7 @@ describe('Authentication middleware', () => {
|
|||||||
expect(await test('/sub/test')).to.be.eql(403);
|
expect(await test('/sub/test')).to.be.eql(403);
|
||||||
expect(await test('/sub/subsub/test')).to.be.eql(403);
|
expect(await test('/sub/subsub/test')).to.be.eql(403);
|
||||||
expect(await test('/sub/subsub2/test')).to.be.eql(403);
|
expect(await test('/sub/subsub2/test')).to.be.eql(403);
|
||||||
req.session.user.permissions = [path.normalize('/sub/subsub*')];
|
req.user.permissions = [path.normalize('/sub/subsub*')];
|
||||||
expect(await test('/b')).to.be.eql(403);
|
expect(await test('/b')).to.be.eql(403);
|
||||||
expect(await test('/sub')).to.be.eql(403);
|
expect(await test('/sub')).to.be.eql(403);
|
||||||
expect(await test('/sub/subsub2')).to.be.eql(403);
|
expect(await test('/sub/subsub2')).to.be.eql(403);
|
||||||
@ -274,7 +272,7 @@ describe('Authentication middleware', () => {
|
|||||||
};
|
};
|
||||||
const next = (err: ErrorDTO) => {
|
const next = (err: ErrorDTO) => {
|
||||||
expect(err).to.be.undefined;
|
expect(err).to.be.undefined;
|
||||||
expect(req.session.user).to.be.eql('test user');
|
expect(req.user).to.be.eql('test user');
|
||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
ObjectManagers.getInstance().UserManager = <IUserManager>{
|
ObjectManagers.getInstance().UserManager = <IUserManager>{
|
||||||
@ -300,7 +298,7 @@ describe('Authentication middleware', () => {
|
|||||||
};
|
};
|
||||||
const next: any = (err: ErrorDTO) => {
|
const next: any = (err: ErrorDTO) => {
|
||||||
expect(err).to.be.undefined;
|
expect(err).to.be.undefined;
|
||||||
expect(req.session.user).to.be.undefined;
|
expect(req.user).to.be.undefined;
|
||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
AuthenticationMWs.logout(req, null, next);
|
AuthenticationMWs.logout(req, null, next);
|
||||||
|
@ -3,6 +3,8 @@ import {Utils} from '../../../src/common/Utils';
|
|||||||
|
|
||||||
describe('Utils', () => {
|
describe('Utils', () => {
|
||||||
it('should concat urls', () => {
|
it('should concat urls', () => {
|
||||||
|
expect(Utils.concatUrls('\\')).to.be.equal('.');
|
||||||
|
expect(Utils.concatUrls('\\*')).to.be.equal('/*');
|
||||||
expect(Utils.concatUrls('abc', 'cde')).to.be.equal('abc/cde');
|
expect(Utils.concatUrls('abc', 'cde')).to.be.equal('abc/cde');
|
||||||
expect(Utils.concatUrls('abc/', 'cde')).to.be.equal('abc/cde');
|
expect(Utils.concatUrls('abc/', 'cde')).to.be.equal('abc/cde');
|
||||||
expect(Utils.concatUrls('abc\\', 'cde')).to.be.equal('abc/cde');
|
expect(Utils.concatUrls('abc\\', 'cde')).to.be.equal('abc/cde');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user