diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/.npmignore b/.npmignore old mode 100644 new mode 100755 diff --git a/.travis.yml b/.travis.yml old mode 100644 new mode 100755 diff --git a/CHANGELOG.md b/CHANGELOG.md old mode 100644 new mode 100755 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md old mode 100644 new mode 100755 diff --git a/Dockerfile b/Dockerfile old mode 100644 new mode 100755 diff --git a/Dockerfile.dev b/Dockerfile.dev old mode 100644 new mode 100755 diff --git a/Gruntfile.js b/Gruntfile.js old mode 100644 new mode 100755 index fcf4a5731..2bcb4f8f7 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,7 +1,8 @@ module.exports = function (grunt) { const buildDir = "dist"; const schemaDir = "server/src/lib/configuration/Configuration.schema.json" - + var theme = grunt.option('theme') || 'default'; + grunt.initConfig({ env: { "env-test-server-unit": { @@ -14,7 +15,10 @@ module.exports = function (grunt) { TS_NODE_PROJECT: "server/tsconfig.json" } }, - clean: ['dist'], + clean: { + dist: ['dist'], + backup: ['backup'], + }, run: { "compile-server": { cmd: "./node_modules/.bin/tsc", @@ -83,123 +87,34 @@ module.exports = function (grunt) { } }, copy: { - main_resources: { + backup: { + files: [{ expand: true, - cwd: 'themes/main/server/src/resources', + src: ['dist/**'], + dest: 'backup' + }] + }, + resources: { + expand: true, + cwd: 'themes/' + theme + '/server/src/resources', src: '**', dest: `${buildDir}/server/src/resources/` }, - main_views: { + views: { expand: true, - cwd: 'themes/main/server/src/views', + cwd: 'themes/' + theme + '/server/src/views', src: '**', dest: `${buildDir}/server/src/views/` }, - main_images: { + images: { expand: true, - cwd: 'themes/main/client/src/img', + cwd: 'themes/' + theme + '/client/src/img', src: '**', dest: `${buildDir}/server/src/public_html/img/` }, - main_thirdparties: { + thirdparties: { expand: true, - cwd: 'themes/main/client/src/thirdparties', - src: '**', - dest: `${buildDir}/server/src/public_html/js/` - }, - matrix_resources: { - expand: true, - cwd: 'themes/matrix/server/src/resources', - src: '**', - dest: `${buildDir}/server/src/resources/` - }, - matrix_views: { - expand: true, - cwd: 'themes/matrix/server/src/views', - src: '**', - dest: `${buildDir}/server/src/views/` - }, - matrix_images: { - expand: true, - cwd: 'themes/matrix/client/src/img', - src: '**', - dest: `${buildDir}/server/src/public_html/img/` - }, - matrix_thirdparties: { - expand: true, - cwd: 'themes/matrix/client/src/thirdparties', - src: '**', - dest: `${buildDir}/server/src/public_html/js/` - }, - black_resources: { - expand: true, - cwd: 'themes/black/server/src/resources', - src: '**', - dest: `${buildDir}/server/src/resources/` - }, - black_views: { - expand: true, - cwd: 'themes/black/server/src/views', - src: '**', - dest: `${buildDir}/server/src/views/` - }, - black_images: { - expand: true, - cwd: 'themes/black/client/src/img', - src: '**', - dest: `${buildDir}/server/src/public_html/img/` - }, - black_thirdparties: { - expand: true, - cwd: 'themes/black/client/src/thirdparties', - src: '**', - dest: `${buildDir}/server/src/public_html/js/` - }, - squares_resources: { - expand: true, - cwd: 'themes/squares/server/src/resources', - src: '**', - dest: `${buildDir}/server/src/resources/` - }, - squares_views: { - expand: true, - cwd: 'themes/squares/server/src/views', - src: '**', - dest: `${buildDir}/server/src/views/` - }, - squares_images: { - expand: true, - cwd: 'themes/squares/client/src/img', - src: '**', - dest: `${buildDir}/server/src/public_html/img/` - }, - squares_thirdparties: { - expand: true, - cwd: 'themes/squares/client/src/thirdparties', - src: '**', - dest: `${buildDir}/server/src/public_html/js/` - }, - triangles_resources: { - expand: true, - cwd: 'themes/triangles/server/src/resources', - src: '**', - dest: `${buildDir}/server/src/resources/` - }, - triangles_views: { - expand: true, - cwd: 'themes/triangles/server/src/views', - src: '**', - dest: `${buildDir}/server/src/views/` - }, - triangles_images: { - expand: true, - cwd: 'themes/triangles/client/src/img', - src: '**', - dest: `${buildDir}/server/src/public_html/img/` - }, - triangles_thirdparties: { - expand: true, - cwd: 'themes/triangles/client/src/thirdparties', + cwd: 'themes/' + theme + '/client/src/thirdparties', src: '**', dest: `${buildDir}/server/src/public_html/js/` }, @@ -270,24 +185,8 @@ module.exports = function (grunt) { } }, concat: { - main_css: { - src: ['themes/main/client/src/css/*.css'], - dest: `${buildDir}/server/src/public_html/css/authelia.css` - }, - matrix_css: { - src: ['themes/matrix/client/src/css/*.css'], - dest: `${buildDir}/server/src/public_html/css/authelia.css` - }, - black_css: { - src: ['themes/black/client/src/css/*.css'], - dest: `${buildDir}/server/src/public_html/css/authelia.css` - }, - squares_css: { - src: ['themes/squares/client/src/css/*.css'], - dest: `${buildDir}/server/src/public_html/css/authelia.css` - }, - triangles_css: { - src: ['themes/triangles/client/src/css/*.css'], + css: { + src: ['themes/' + theme + '/client/src/css/*.css'], dest: `${buildDir}/server/src/public_html/css/authelia.css` }, }, @@ -299,8 +198,6 @@ module.exports = function (grunt) { } } }); - - var target = grunt.option('target') || 'main'; grunt.loadNpmTasks('grunt-browserify'); grunt.loadNpmTasks('grunt-contrib-concat'); @@ -311,7 +208,6 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-run'); grunt.loadNpmTasks('grunt-env'); - grunt.registerTask('compile-server', ['run:lint-server', 'run:compile-server']) grunt.registerTask('compile-client', ['run:lint-client', 'run:compile-client']) @@ -321,31 +217,33 @@ module.exports = function (grunt) { grunt.registerTask('test-unit', ['test-server', 'test-client', 'test-shared']); grunt.registerTask('test-int', ['run:test-cucumber', 'run:test-minimal-config', 'run:test-complete-config', 'run:test-inactivity']); - grunt.registerTask('copy-resources-main', ['copy:main_resources', 'copy:main_views', 'copy:main_images', 'copy:main_thirdparties', 'concat:main_css']); - - grunt.registerTask('copy-resources-matrix', ['copy:matrix_resources', 'copy:matrix_views', 'copy:matrix_images', 'copy:matrix_thirdparties', 'concat:matrix_css']); - - grunt.registerTask('copy-resources-black', ['copy:black_resources', 'copy:black_views', 'copy:black_images', 'copy:black_thirdparties', 'concat:black_css']); - - grunt.registerTask('copy-resources-squares', ['copy:squares_resources', 'copy:squares_views', 'copy:squares_images', 'copy:squares_thirdparties', 'concat:squares_css']); - - grunt.registerTask('copy-resources-triangles', ['copy:triangles_resources', 'copy:triangles_views', 'copy:triangles_images', 'copy:triangles_thirdparties', 'concat:triangles_css']); + grunt.registerTask('copy-resources', ['copy:resources', 'copy:views', 'copy:images', 'copy:thirdparties', 'concat:css']); grunt.registerTask('generate-config-schema', ['run:generate-config-schema', 'copy:schema']); grunt.registerTask('build-client', ['compile-client', 'browserify']); - grunt.registerTask('build-server-main', ['compile-server', 'copy-resources-main', 'generate-config-schema']); - grunt.registerTask('build-server-matrix', ['compile-server', 'copy-resources-matrix', 'generate-config-schema']); - grunt.registerTask('build-server-black', ['compile-server', 'copy-resources-black', 'generate-config-schema']); - grunt.registerTask('build-server-squares', ['compile-server', 'copy-resources-squares', 'generate-config-schema']); - grunt.registerTask('build-server-triangles', ['compile-server', 'copy-resources-triangles', 'generate-config-schema']); + + grunt.registerTask('build-server', ['compile-server', 'copy-resources', 'generate-config-schema']); - grunt.registerTask('build', ['build-client', 'build-server-'+target]); - grunt.registerTask('build-dist', ['clean', 'build', 'run:minify', 'cssmin', 'run:include-minified-script']); + grunt.registerTask('build', ['build-client', 'build-server']); + grunt.registerTask('build-dist', ['clean:backup', 'copy:backup', 'clean:dist', 'build', 'run:minify', 'cssmin', 'run:include-minified-script']); grunt.registerTask('schema', ['run:generate-config-schema']) grunt.registerTask('docker-build', ['run:docker-build']); + + grunt.registerTask('check', function() { + if (grunt.option('theme') == 'undefined') { + grunt.log.writeln('1- Valid argmuents are just "grunt" (will use default) or "grunt --theme=|default|black|matrix|squares|triangles"'); + } + if ((theme != 'default') && (theme != 'black') && (theme != 'matrix') && (theme != 'squares') && (theme != 'triangles')) { + grunt.warn('2- Valid argmuents are just "grunt" (will use default) or "grunt --theme=|default|black|matrix|squares|triangles"'); + } + if (grunt.option('theme') == 'default' || 'black' || 'matrix' || 'squares' || 'triangles') { + grunt.log.ok(); + grunt.log.writeln('Building "'+ theme +'" theme'); + } + }); - grunt.registerTask('default', ['build-dist']); + grunt.registerTask('default', ['check', 'build-dist']); }; diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/client/src/css/00-bootstrap.min.css b/client/src/css/00-bootstrap.min.css old mode 100644 new mode 100755 diff --git a/client/src/css/01-main.css b/client/src/css/01-main.css old mode 100644 new mode 100755 diff --git a/client/src/css/02-login.css b/client/src/css/02-login.css old mode 100644 new mode 100755 diff --git a/client/src/css/03-errors.css b/client/src/css/03-errors.css old mode 100644 new mode 100755 diff --git a/client/src/css/03-password-reset-form.css b/client/src/css/03-password-reset-form.css old mode 100644 new mode 100755 diff --git a/client/src/css/03-password-reset-request.css b/client/src/css/03-password-reset-request.css old mode 100644 new mode 100755 diff --git a/client/src/css/03-totp-register.css b/client/src/css/03-totp-register.css old mode 100644 new mode 100755 diff --git a/client/src/css/03-u2f-register.css b/client/src/css/03-u2f-register.css old mode 100644 new mode 100755 diff --git a/client/src/img/background.svg b/client/src/img/background.svg old mode 100644 new mode 100755 diff --git a/client/src/img/icon.png b/client/src/img/icon.png old mode 100644 new mode 100755 diff --git a/client/src/img/mail.png b/client/src/img/mail.png old mode 100644 new mode 100755 diff --git a/client/src/img/notifications/error.png b/client/src/img/notifications/error.png old mode 100644 new mode 100755 diff --git a/client/src/img/notifications/info.png b/client/src/img/notifications/info.png old mode 100644 new mode 100755 diff --git a/client/src/img/notifications/success.png b/client/src/img/notifications/success.png old mode 100644 new mode 100755 diff --git a/client/src/img/notifications/warning.png b/client/src/img/notifications/warning.png old mode 100644 new mode 100755 diff --git a/client/src/img/padlock.png b/client/src/img/padlock.png old mode 100644 new mode 100755 diff --git a/client/src/img/password.png b/client/src/img/password.png old mode 100644 new mode 100755 diff --git a/client/src/img/pendrive.png b/client/src/img/pendrive.png old mode 100644 new mode 100755 diff --git a/client/src/img/stores/applestore-badge.svg b/client/src/img/stores/applestore-badge.svg old mode 100644 new mode 100755 diff --git a/client/src/img/stores/googleplay-badge.svg b/client/src/img/stores/googleplay-badge.svg old mode 100644 new mode 100755 diff --git a/client/src/img/success.png b/client/src/img/success.png old mode 100644 new mode 100755 diff --git a/client/src/img/user.png b/client/src/img/user.png old mode 100644 new mode 100755 diff --git a/client/src/img/warning.png b/client/src/img/warning.png old mode 100644 new mode 100755 diff --git a/client/src/index.ts b/client/src/index.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/GetPromised.ts b/client/src/lib/GetPromised.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/INotifier.ts b/client/src/lib/INotifier.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/Notifier.ts b/client/src/lib/Notifier.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/QueryParametersRetriever.ts b/client/src/lib/QueryParametersRetriever.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/SafeRedirect.ts b/client/src/lib/SafeRedirect.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/firstfactor/FirstFactorValidator.ts b/client/src/lib/firstfactor/FirstFactorValidator.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/firstfactor/UISelectors.ts b/client/src/lib/firstfactor/UISelectors.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/firstfactor/index.ts b/client/src/lib/firstfactor/index.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/reset-password/constants.ts b/client/src/lib/reset-password/constants.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/reset-password/reset-password-form.ts b/client/src/lib/reset-password/reset-password-form.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/reset-password/reset-password-request.ts b/client/src/lib/reset-password/reset-password-request.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/secondfactor/TOTPValidator.ts b/client/src/lib/secondfactor/TOTPValidator.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/secondfactor/U2FValidator.ts b/client/src/lib/secondfactor/U2FValidator.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/secondfactor/constants.ts b/client/src/lib/secondfactor/constants.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/secondfactor/index.ts b/client/src/lib/secondfactor/index.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/totp-register/totp-register.ts b/client/src/lib/totp-register/totp-register.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/totp-register/ui-selector.ts b/client/src/lib/totp-register/ui-selector.ts old mode 100644 new mode 100755 diff --git a/client/src/lib/u2f-register/u2f-register.ts b/client/src/lib/u2f-register/u2f-register.ts old mode 100644 new mode 100755 diff --git a/client/src/thirdparties/qrcode.min.js b/client/src/thirdparties/qrcode.min.js old mode 100644 new mode 100755 diff --git a/client/test/Notifier.test.ts b/client/test/Notifier.test.ts old mode 100644 new mode 100755 diff --git a/client/test/firstfactor/FirstFactorValidator.test.ts b/client/test/firstfactor/FirstFactorValidator.test.ts old mode 100644 new mode 100755 diff --git a/client/test/mocks/NotifierStub.ts b/client/test/mocks/NotifierStub.ts old mode 100644 new mode 100755 diff --git a/client/test/mocks/jquery.ts b/client/test/mocks/jquery.ts old mode 100644 new mode 100755 diff --git a/client/test/mocks/u2f-api.ts b/client/test/mocks/u2f-api.ts old mode 100644 new mode 100755 diff --git a/client/test/secondfactor/TOTPValidator.test.ts b/client/test/secondfactor/TOTPValidator.test.ts old mode 100644 new mode 100755 diff --git a/client/test/totp-register/totp-register.test.ts b/client/test/totp-register/totp-register.test.ts old mode 100644 new mode 100755 diff --git a/client/tsconfig.json b/client/tsconfig.json old mode 100644 new mode 100755 diff --git a/client/tslint.json b/client/tslint.json old mode 100644 new mode 100755 diff --git a/config.minimal.yml b/config.minimal.yml old mode 100644 new mode 100755 index 8da7bc5e1..c43828f2e --- a/config.minimal.yml +++ b/config.minimal.yml @@ -4,7 +4,7 @@ authentication_backend: file: - path: /etc/authelia/users_database.yml + path: users_database.yml session: secret: unsecure_session_secret diff --git a/config.template.yml b/config.template.yml old mode 100644 new mode 100755 diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml old mode 100644 new mode 100755 diff --git a/docker-compose.dockerhub.yml b/docker-compose.dockerhub.yml old mode 100644 new mode 100755 diff --git a/docker-compose.minimal.dev.yml b/docker-compose.minimal.dev.yml old mode 100644 new mode 100755 diff --git a/docker-compose.minimal.yml b/docker-compose.minimal.yml old mode 100644 new mode 100755 diff --git a/docker-compose.swarm.minimal.yml b/docker-compose.swarm.minimal.yml old mode 100644 new mode 100755 diff --git a/docker-compose.test.yml b/docker-compose.test.yml old mode 100644 new mode 100755 diff --git a/docker-compose.yml b/docker-compose.yml old mode 100644 new mode 100755 diff --git a/docs/build.md b/docs/build.md old mode 100644 new mode 100755 diff --git a/docs/configuration.md b/docs/configuration.md old mode 100644 new mode 100755 diff --git a/docs/deployment-dev.md b/docs/deployment-dev.md old mode 100644 new mode 100755 diff --git a/docs/deployment-production.md b/docs/deployment-production.md old mode 100644 new mode 100755 diff --git a/docs/features.md b/docs/features.md old mode 100644 new mode 100755 diff --git a/docs/getting-started.md b/docs/getting-started.md old mode 100644 new mode 100755 diff --git a/docs/security.md b/docs/security.md old mode 100644 new mode 100755 diff --git a/example/compose/authelia/docker-compose.test.yml b/example/compose/authelia/docker-compose.test.yml old mode 100644 new mode 100755 diff --git a/example/compose/docker-compose.base.yml b/example/compose/docker-compose.base.yml old mode 100644 new mode 100755 diff --git a/example/compose/httpbin/docker-compose.yml b/example/compose/httpbin/docker-compose.yml old mode 100644 new mode 100755 diff --git a/example/compose/ldap/access.rules b/example/compose/ldap/access.rules old mode 100644 new mode 100755 diff --git a/example/compose/ldap/base.ldif b/example/compose/ldap/base.ldif old mode 100644 new mode 100755 diff --git a/example/compose/ldap/docker-compose.admin.yml b/example/compose/ldap/docker-compose.admin.yml old mode 100644 new mode 100755 diff --git a/example/compose/ldap/docker-compose.yml b/example/compose/ldap/docker-compose.yml old mode 100644 new mode 100755 diff --git a/example/compose/mongo/docker-compose.yml b/example/compose/mongo/docker-compose.yml old mode 100644 new mode 100755 diff --git a/example/compose/nginx/backend/docker-compose.yml b/example/compose/nginx/backend/docker-compose.yml old mode 100644 new mode 100755 diff --git a/example/compose/nginx/backend/html/admin/secret.html b/example/compose/nginx/backend/html/admin/secret.html old mode 100644 new mode 100755 diff --git a/example/compose/nginx/backend/html/dev/groups/admin/secret.html b/example/compose/nginx/backend/html/dev/groups/admin/secret.html old mode 100644 new mode 100755 diff --git a/example/compose/nginx/backend/html/dev/groups/dev/secret.html b/example/compose/nginx/backend/html/dev/groups/dev/secret.html old mode 100644 new mode 100755 diff --git a/example/compose/nginx/backend/html/dev/users/bob/secret.html b/example/compose/nginx/backend/html/dev/users/bob/secret.html old mode 100644 new mode 100755 diff --git a/example/compose/nginx/backend/html/dev/users/harry/secret.html b/example/compose/nginx/backend/html/dev/users/harry/secret.html old mode 100644 new mode 100755 diff --git a/example/compose/nginx/backend/html/dev/users/john/secret.html b/example/compose/nginx/backend/html/dev/users/john/secret.html old mode 100644 new mode 100755 diff --git a/example/compose/nginx/backend/html/home/index.html b/example/compose/nginx/backend/html/home/index.html old mode 100644 new mode 100755 diff --git a/example/compose/nginx/backend/html/icon.png b/example/compose/nginx/backend/html/icon.png old mode 100644 new mode 100755 diff --git a/example/compose/nginx/backend/html/mail/secret.html b/example/compose/nginx/backend/html/mail/secret.html old mode 100644 new mode 100755 diff --git a/example/compose/nginx/backend/html/public/index.html b/example/compose/nginx/backend/html/public/index.html old mode 100644 new mode 100755 diff --git a/example/compose/nginx/backend/html/public/secret.html b/example/compose/nginx/backend/html/public/secret.html old mode 100644 new mode 100755 diff --git a/example/compose/nginx/backend/html/single_factor/secret.html b/example/compose/nginx/backend/html/single_factor/secret.html old mode 100644 new mode 100755 diff --git a/example/compose/nginx/backend/nginx.conf b/example/compose/nginx/backend/nginx.conf old mode 100644 new mode 100755 diff --git a/example/compose/nginx/minimal/docker-compose.yml b/example/compose/nginx/minimal/docker-compose.yml old mode 100644 new mode 100755 diff --git a/example/compose/nginx/minimal/html/admin/secret.html b/example/compose/nginx/minimal/html/admin/secret.html old mode 100644 new mode 100755 diff --git a/example/compose/nginx/minimal/html/home/index.html b/example/compose/nginx/minimal/html/home/index.html old mode 100644 new mode 100755 diff --git a/example/compose/nginx/minimal/nginx.conf b/example/compose/nginx/minimal/nginx.conf old mode 100644 new mode 100755 diff --git a/example/compose/nginx/minimal/ssl/server.crt b/example/compose/nginx/minimal/ssl/server.crt old mode 100644 new mode 100755 diff --git a/example/compose/nginx/minimal/ssl/server.csr b/example/compose/nginx/minimal/ssl/server.csr old mode 100644 new mode 100755 diff --git a/example/compose/nginx/minimal/ssl/server.key b/example/compose/nginx/minimal/ssl/server.key old mode 100644 new mode 100755 diff --git a/example/compose/nginx/portal/docker-compose.yml b/example/compose/nginx/portal/docker-compose.yml old mode 100644 new mode 100755 diff --git a/example/compose/nginx/portal/nginx.conf b/example/compose/nginx/portal/nginx.conf old mode 100644 new mode 100755 diff --git a/example/compose/nginx/portal/ssl/server.crt b/example/compose/nginx/portal/ssl/server.crt old mode 100644 new mode 100755 diff --git a/example/compose/nginx/portal/ssl/server.csr b/example/compose/nginx/portal/ssl/server.csr old mode 100644 new mode 100755 diff --git a/example/compose/nginx/portal/ssl/server.key b/example/compose/nginx/portal/ssl/server.key old mode 100644 new mode 100755 diff --git a/example/compose/redis/docker-compose.yml b/example/compose/redis/docker-compose.yml old mode 100644 new mode 100755 diff --git a/example/compose/smtp/docker-compose.yml b/example/compose/smtp/docker-compose.yml old mode 100644 new mode 100755 diff --git a/example/kube/README.md b/example/kube/README.md old mode 100644 new mode 100755 diff --git a/example/kube/apps/app-home/deployment.yml b/example/kube/apps/app-home/deployment.yml old mode 100644 new mode 100755 diff --git a/example/kube/apps/app-home/index.html b/example/kube/apps/app-home/index.html old mode 100644 new mode 100755 diff --git a/example/kube/apps/app-home/service.yml b/example/kube/apps/app-home/service.yml old mode 100644 new mode 100755 diff --git a/example/kube/apps/app1/deployment.yml b/example/kube/apps/app1/deployment.yml old mode 100644 new mode 100755 diff --git a/example/kube/apps/app1/index.html b/example/kube/apps/app1/index.html old mode 100644 new mode 100755 diff --git a/example/kube/apps/app1/service.yml b/example/kube/apps/app1/service.yml old mode 100644 new mode 100755 diff --git a/example/kube/apps/app1/ssl/tls.crt b/example/kube/apps/app1/ssl/tls.crt old mode 100644 new mode 100755 diff --git a/example/kube/apps/app1/ssl/tls.csr b/example/kube/apps/app1/ssl/tls.csr old mode 100644 new mode 100755 diff --git a/example/kube/apps/app1/ssl/tls.key b/example/kube/apps/app1/ssl/tls.key old mode 100644 new mode 100755 diff --git a/example/kube/apps/app2/deployment.yml b/example/kube/apps/app2/deployment.yml old mode 100644 new mode 100755 diff --git a/example/kube/apps/app2/index.html b/example/kube/apps/app2/index.html old mode 100644 new mode 100755 diff --git a/example/kube/apps/app2/service.yml b/example/kube/apps/app2/service.yml old mode 100644 new mode 100755 diff --git a/example/kube/apps/app2/ssl/tls.crt b/example/kube/apps/app2/ssl/tls.crt old mode 100644 new mode 100755 diff --git a/example/kube/apps/app2/ssl/tls.csr b/example/kube/apps/app2/ssl/tls.csr old mode 100644 new mode 100755 diff --git a/example/kube/apps/app2/ssl/tls.key b/example/kube/apps/app2/ssl/tls.key old mode 100644 new mode 100755 diff --git a/example/kube/apps/insecure-ingress.yml b/example/kube/apps/insecure-ingress.yml old mode 100644 new mode 100755 diff --git a/example/kube/apps/secure-ingress.yml b/example/kube/apps/secure-ingress.yml old mode 100644 new mode 100755 diff --git a/example/kube/authelia/configs/config.yml b/example/kube/authelia/configs/config.yml old mode 100644 new mode 100755 diff --git a/example/kube/authelia/deployment.yml b/example/kube/authelia/deployment.yml old mode 100644 new mode 100755 diff --git a/example/kube/authelia/ingress.yml b/example/kube/authelia/ingress.yml old mode 100644 new mode 100755 diff --git a/example/kube/authelia/service.yml b/example/kube/authelia/service.yml old mode 100644 new mode 100755 diff --git a/example/kube/authelia/ssl/tls.crt b/example/kube/authelia/ssl/tls.crt old mode 100644 new mode 100755 diff --git a/example/kube/authelia/ssl/tls.csr b/example/kube/authelia/ssl/tls.csr old mode 100644 new mode 100755 diff --git a/example/kube/authelia/ssl/tls.key b/example/kube/authelia/ssl/tls.key old mode 100644 new mode 100755 diff --git a/example/kube/docker-registry/daemonset.yml b/example/kube/docker-registry/daemonset.yml old mode 100644 new mode 100755 diff --git a/example/kube/docker-registry/ingress.yml b/example/kube/docker-registry/ingress.yml old mode 100644 new mode 100755 diff --git a/example/kube/docker-registry/replicationcontroller.yml b/example/kube/docker-registry/replicationcontroller.yml old mode 100644 new mode 100755 diff --git a/example/kube/docker-registry/service.yml b/example/kube/docker-registry/service.yml old mode 100644 new mode 100755 diff --git a/example/kube/ingress-controller/default-backend.yml b/example/kube/ingress-controller/default-backend.yml old mode 100644 new mode 100755 diff --git a/example/kube/ingress-controller/deployment.yml b/example/kube/ingress-controller/deployment.yml old mode 100644 new mode 100755 diff --git a/example/kube/ingress-controller/service.yml b/example/kube/ingress-controller/service.yml old mode 100644 new mode 100755 diff --git a/example/kube/ldap/Dockerfile b/example/kube/ldap/Dockerfile old mode 100644 new mode 100755 diff --git a/example/kube/ldap/deployment.yml b/example/kube/ldap/deployment.yml old mode 100644 new mode 100755 diff --git a/example/kube/ldap/service.yml b/example/kube/ldap/service.yml old mode 100644 new mode 100755 diff --git a/example/kube/mailcatcher/deployment.yml b/example/kube/mailcatcher/deployment.yml old mode 100644 new mode 100755 diff --git a/example/kube/mailcatcher/ingress.yml b/example/kube/mailcatcher/ingress.yml old mode 100644 new mode 100755 diff --git a/example/kube/mailcatcher/service.yml b/example/kube/mailcatcher/service.yml old mode 100644 new mode 100755 diff --git a/example/kube/namespace.yml b/example/kube/namespace.yml old mode 100644 new mode 100755 diff --git a/example/kube/storage/mongo.yml b/example/kube/storage/mongo.yml old mode 100644 new mode 100755 diff --git a/example/kube/storage/redis.yml b/example/kube/storage/redis.yml old mode 100644 new mode 100755 diff --git a/images/authelia-title-white.png b/images/authelia-title-white.png old mode 100644 new mode 100755 diff --git a/images/authelia-title.png b/images/authelia-title.png old mode 100644 new mode 100755 diff --git a/images/email_confirmation.png b/images/email_confirmation.png old mode 100644 new mode 100755 diff --git a/images/first_factor.png b/images/first_factor.png old mode 100644 new mode 100755 diff --git a/images/icon.png b/images/icon.png old mode 100644 new mode 100755 diff --git a/images/kube-logo.png b/images/kube-logo.png old mode 100644 new mode 100755 diff --git a/images/reset_password.png b/images/reset_password.png old mode 100644 new mode 100755 diff --git a/images/second_factor.png b/images/second_factor.png old mode 100644 new mode 100755 diff --git a/images/totp.png b/images/totp.png old mode 100644 new mode 100755 diff --git a/images/u2f.png b/images/u2f.png old mode 100644 new mode 100755 diff --git a/package-lock.json b/package-lock.json old mode 100644 new mode 100755 index 6e9837892..890819932 --- a/package-lock.json +++ b/package-lock.json @@ -2971,12 +2971,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2991,17 +2993,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3118,7 +3123,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3130,6 +3136,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3144,6 +3151,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3151,12 +3159,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -3175,6 +3185,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3255,7 +3266,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3267,6 +3279,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3388,6 +3401,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", diff --git a/package.json b/package.json old mode 100644 new mode 100755 diff --git a/server/src/lib/AuthenticationSessionHandler.ts b/server/src/lib/AuthenticationSessionHandler.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/ErrorReplies.ts b/server/src/lib/ErrorReplies.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/Exceptions.ts b/server/src/lib/Exceptions.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/FirstFactorValidator.ts b/server/src/lib/FirstFactorValidator.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/IdentityCheckMiddleware.spec.ts b/server/src/lib/IdentityCheckMiddleware.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/IdentityCheckMiddleware.ts b/server/src/lib/IdentityCheckMiddleware.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/IdentityCheckPreValidationTemplate.ts b/server/src/lib/IdentityCheckPreValidationTemplate.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/IdentityValidable.ts b/server/src/lib/IdentityValidable.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/IdentityValidableStub.spec.ts b/server/src/lib/IdentityValidableStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/Server.spec.ts b/server/src/lib/Server.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/Server.ts b/server/src/lib/Server.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/ServerVariables.ts b/server/src/lib/ServerVariables.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/ServerVariablesInitializer.ts b/server/src/lib/ServerVariablesInitializer.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/ServerVariablesMockBuilder.spec.ts b/server/src/lib/ServerVariablesMockBuilder.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/Level.ts b/server/src/lib/authentication/Level.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/GroupsAndEmails.ts b/server/src/lib/authentication/backends/GroupsAndEmails.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/IUsersDatabase.ts b/server/src/lib/authentication/backends/IUsersDatabase.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts b/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts b/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/file/FileUsersDatabase.ts b/server/src/lib/authentication/backends/file/FileUsersDatabase.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/file/ReadWriteQueue.ts b/server/src/lib/authentication/backends/file/ReadWriteQueue.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/ISession.ts b/server/src/lib/authentication/backends/ldap/ISession.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/ISessionFactory.ts b/server/src/lib/authentication/backends/ldap/ISessionFactory.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts b/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts b/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts b/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/SafeSession.ts b/server/src/lib/authentication/backends/ldap/SafeSession.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts b/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/Sanitizer.ts b/server/src/lib/authentication/backends/ldap/Sanitizer.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/Session.spec.ts b/server/src/lib/authentication/backends/ldap/Session.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/Session.ts b/server/src/lib/authentication/backends/ldap/Session.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/SessionFactory.ts b/server/src/lib/authentication/backends/ldap/SessionFactory.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts b/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts b/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/connector/Connector.ts b/server/src/lib/authentication/backends/ldap/connector/Connector.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts b/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts b/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts b/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/connector/IConnector.ts b/server/src/lib/authentication/backends/ldap/connector/IConnector.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts b/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/totp/ITotpHandler.ts b/server/src/lib/authentication/totp/ITotpHandler.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/totp/TotpHandler.spec.ts b/server/src/lib/authentication/totp/TotpHandler.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/totp/TotpHandler.ts b/server/src/lib/authentication/totp/TotpHandler.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts b/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/u2f/IU2fHandler.ts b/server/src/lib/authentication/u2f/IU2fHandler.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/u2f/U2fHandler.ts b/server/src/lib/authentication/u2f/U2fHandler.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts b/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authorization/Authorizer.spec.ts b/server/src/lib/authorization/Authorizer.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authorization/Authorizer.ts b/server/src/lib/authorization/Authorizer.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authorization/AuthorizerStub.spec.ts b/server/src/lib/authorization/AuthorizerStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authorization/IAuthorizer.ts b/server/src/lib/authorization/IAuthorizer.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authorization/Level.ts b/server/src/lib/authorization/Level.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authorization/MultipleDomainMatcher.ts b/server/src/lib/authorization/MultipleDomainMatcher.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authorization/Object.ts b/server/src/lib/authorization/Object.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/authorization/Subject.ts b/server/src/lib/authorization/Subject.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/ConfigurationParser.spec.ts b/server/src/lib/configuration/ConfigurationParser.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/ConfigurationParser.ts b/server/src/lib/configuration/ConfigurationParser.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts b/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/SessionConfigurationBuilder.ts b/server/src/lib/configuration/SessionConfigurationBuilder.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/AclConfiguration.spec.ts b/server/src/lib/configuration/schema/AclConfiguration.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/AclConfiguration.ts b/server/src/lib/configuration/schema/AclConfiguration.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts b/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts b/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/Configuration.ts b/server/src/lib/configuration/schema/Configuration.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts b/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/LdapConfiguration.spec.ts b/server/src/lib/configuration/schema/LdapConfiguration.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/LdapConfiguration.ts b/server/src/lib/configuration/schema/LdapConfiguration.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts b/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/NotifierConfiguration.ts b/server/src/lib/configuration/schema/NotifierConfiguration.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts b/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/RegulationConfiguration.ts b/server/src/lib/configuration/schema/RegulationConfiguration.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/SessionConfiguration.spec.ts b/server/src/lib/configuration/schema/SessionConfiguration.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/SessionConfiguration.ts b/server/src/lib/configuration/schema/SessionConfiguration.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/StorageConfiguration.spec.ts b/server/src/lib/configuration/schema/StorageConfiguration.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/StorageConfiguration.ts b/server/src/lib/configuration/schema/StorageConfiguration.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/TotpConfiguration.ts b/server/src/lib/configuration/schema/TotpConfiguration.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts b/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/connectors/mongo/IMongoClient.d.ts b/server/src/lib/connectors/mongo/IMongoClient.d.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/connectors/mongo/MongoClient.spec.ts b/server/src/lib/connectors/mongo/MongoClient.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/connectors/mongo/MongoClient.ts b/server/src/lib/connectors/mongo/MongoClient.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/connectors/mongo/MongoClientStub.spec.ts b/server/src/lib/connectors/mongo/MongoClientStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/logging/GlobalLogger.ts b/server/src/lib/logging/GlobalLogger.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/logging/GlobalLoggerStub.spec.ts b/server/src/lib/logging/GlobalLoggerStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/logging/IGlobalLogger.ts b/server/src/lib/logging/IGlobalLogger.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/logging/IRequestLogger.ts b/server/src/lib/logging/IRequestLogger.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/logging/RequestLogger.ts b/server/src/lib/logging/RequestLogger.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/logging/RequestLoggerStub.spec.ts b/server/src/lib/logging/RequestLoggerStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/AbstractEmailNotifier.ts b/server/src/lib/notifiers/AbstractEmailNotifier.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/EmailNotifier.spec.ts b/server/src/lib/notifiers/EmailNotifier.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/EmailNotifier.ts b/server/src/lib/notifiers/EmailNotifier.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/FileSystemNotifier.ts b/server/src/lib/notifiers/FileSystemNotifier.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/IMailSender.ts b/server/src/lib/notifiers/IMailSender.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/IMailSenderBuilder.ts b/server/src/lib/notifiers/IMailSenderBuilder.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/INotifier.ts b/server/src/lib/notifiers/INotifier.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/MailSender.ts b/server/src/lib/notifiers/MailSender.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/MailSenderBuilder.spec.ts b/server/src/lib/notifiers/MailSenderBuilder.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/MailSenderBuilder.ts b/server/src/lib/notifiers/MailSenderBuilder.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts b/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/MailSenderStub.spec.ts b/server/src/lib/notifiers/MailSenderStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/NotifierFactory.spec.ts b/server/src/lib/notifiers/NotifierFactory.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/NotifierFactory.ts b/server/src/lib/notifiers/NotifierFactory.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/NotifierStub.spec.ts b/server/src/lib/notifiers/NotifierStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/notifiers/SmtpNotifier.ts b/server/src/lib/notifiers/SmtpNotifier.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/regulation/IRegulator.ts b/server/src/lib/regulation/IRegulator.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/regulation/Regulator.spec.ts b/server/src/lib/regulation/Regulator.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/regulation/Regulator.ts b/server/src/lib/regulation/Regulator.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/regulation/RegulatorStub.spec.ts b/server/src/lib/regulation/RegulatorStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/error/401/get.spec.ts b/server/src/lib/routes/error/401/get.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/error/401/get.ts b/server/src/lib/routes/error/401/get.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/error/403/get.spec.ts b/server/src/lib/routes/error/403/get.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/error/403/get.ts b/server/src/lib/routes/error/403/get.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/error/404/get.spec.ts b/server/src/lib/routes/error/404/get.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/error/404/get.ts b/server/src/lib/routes/error/404/get.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/error/redirector.ts b/server/src/lib/routes/error/redirector.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/firstfactor/get.ts b/server/src/lib/routes/firstfactor/get.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/firstfactor/post.spec.ts b/server/src/lib/routes/firstfactor/post.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/firstfactor/post.ts b/server/src/lib/routes/firstfactor/post.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/loggedin/get.ts b/server/src/lib/routes/loggedin/get.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/logout/get.ts b/server/src/lib/routes/logout/get.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/password-reset/constants.ts b/server/src/lib/routes/password-reset/constants.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/password-reset/form/post.spec.ts b/server/src/lib/routes/password-reset/form/post.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/password-reset/form/post.ts b/server/src/lib/routes/password-reset/form/post.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts b/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts b/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/password-reset/request/get.ts b/server/src/lib/routes/password-reset/request/get.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/get.spec.ts b/server/src/lib/routes/secondfactor/get.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/get.ts b/server/src/lib/routes/secondfactor/get.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/redirect.spec.ts b/server/src/lib/routes/secondfactor/redirect.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/redirect.ts b/server/src/lib/routes/secondfactor/redirect.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/totp/constants.ts b/server/src/lib/routes/secondfactor/totp/constants.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts b/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts b/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts b/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/totp/sign/post.ts b/server/src/lib/routes/secondfactor/totp/sign/post.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts b/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts b/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts b/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts b/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/u2f/register/post.ts b/server/src/lib/routes/secondfactor/u2f/register/post.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts b/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/u2f/register_request/get.ts b/server/src/lib/routes/secondfactor/u2f/register_request/get.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts b/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/u2f/sign/post.ts b/server/src/lib/routes/secondfactor/u2f/sign/post.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts b/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts b/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/verify/access_control.ts b/server/src/lib/routes/verify/access_control.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/verify/get.spec.ts b/server/src/lib/routes/verify/get.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/verify/get.ts b/server/src/lib/routes/verify/get.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/verify/get_basic_auth.ts b/server/src/lib/routes/verify/get_basic_auth.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/routes/verify/get_session_cookie.ts b/server/src/lib/routes/verify/get_session_cookie.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/AuthenticationTraceDocument.d.ts b/server/src/lib/storage/AuthenticationTraceDocument.d.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/CollectionFactoryFactory.ts b/server/src/lib/storage/CollectionFactoryFactory.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/CollectionFactoryStub.spec.ts b/server/src/lib/storage/CollectionFactoryStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/CollectionStub.spec.ts b/server/src/lib/storage/CollectionStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/ICollection.d.ts b/server/src/lib/storage/ICollection.d.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/ICollectionFactory.d.ts b/server/src/lib/storage/ICollectionFactory.d.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/IUserDataStore.d.ts b/server/src/lib/storage/IUserDataStore.d.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/IdentityValidationDocument.d.ts b/server/src/lib/storage/IdentityValidationDocument.d.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/TOTPSecretDocument.d.ts b/server/src/lib/storage/TOTPSecretDocument.d.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/U2FRegistrationDocument.d.ts b/server/src/lib/storage/U2FRegistrationDocument.d.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/UserDataStore.spec.ts b/server/src/lib/storage/UserDataStore.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/UserDataStore.ts b/server/src/lib/storage/UserDataStore.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/UserDataStoreStub.spec.ts b/server/src/lib/storage/UserDataStoreStub.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/mongo/MongoCollection.spec.ts b/server/src/lib/storage/mongo/MongoCollection.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/mongo/MongoCollection.ts b/server/src/lib/storage/mongo/MongoCollection.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts b/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/mongo/MongoCollectionFactory.ts b/server/src/lib/storage/mongo/MongoCollectionFactory.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/nedb/NedbCollection.spec.ts b/server/src/lib/storage/nedb/NedbCollection.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/nedb/NedbCollection.ts b/server/src/lib/storage/nedb/NedbCollection.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts b/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/storage/nedb/NedbCollectionFactory.ts b/server/src/lib/storage/nedb/NedbCollectionFactory.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/stubs/express.spec.ts b/server/src/lib/stubs/express.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/stubs/ldapjs.spec.ts b/server/src/lib/stubs/ldapjs.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/stubs/speakeasy.spec.ts b/server/src/lib/stubs/speakeasy.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/stubs/u2f.spec.ts b/server/src/lib/stubs/u2f.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/utils/HashGenerator.spec.ts b/server/src/lib/utils/HashGenerator.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/utils/HashGenerator.ts b/server/src/lib/utils/HashGenerator.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/utils/ObjectCloner.ts b/server/src/lib/utils/ObjectCloner.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/utils/SafeRedirection.spec.ts b/server/src/lib/utils/SafeRedirection.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/utils/SafeRedirection.ts b/server/src/lib/utils/SafeRedirection.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/utils/URLDecomposer.spec.ts b/server/src/lib/utils/URLDecomposer.spec.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/utils/URLDecomposer.ts b/server/src/lib/utils/URLDecomposer.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/web_server/Configurator.ts b/server/src/lib/web_server/Configurator.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/web_server/RestApi.ts b/server/src/lib/web_server/RestApi.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts b/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts old mode 100644 new mode 100755 diff --git a/server/src/lib/web_server/middlewares/WithHeadersLogged.ts b/server/src/lib/web_server/middlewares/WithHeadersLogged.ts old mode 100644 new mode 100755 diff --git a/server/src/resources/email-template.ejs b/server/src/resources/email-template.ejs old mode 100644 new mode 100755 diff --git a/server/src/views/already-logged-in.pug b/server/src/views/already-logged-in.pug old mode 100644 new mode 100755 diff --git a/server/src/views/errors/401.pug b/server/src/views/errors/401.pug old mode 100644 new mode 100755 diff --git a/server/src/views/errors/403.pug b/server/src/views/errors/403.pug old mode 100644 new mode 100755 diff --git a/server/src/views/errors/404.pug b/server/src/views/errors/404.pug old mode 100644 new mode 100755 diff --git a/server/src/views/firstfactor.pug b/server/src/views/firstfactor.pug old mode 100644 new mode 100755 diff --git a/server/src/views/layout/layout.pug b/server/src/views/layout/layout.pug old mode 100644 new mode 100755 diff --git a/server/src/views/need-identity-validation.pug b/server/src/views/need-identity-validation.pug old mode 100644 new mode 100755 diff --git a/server/src/views/password-reset-form.pug b/server/src/views/password-reset-form.pug old mode 100644 new mode 100755 diff --git a/server/src/views/password-reset-request.pug b/server/src/views/password-reset-request.pug old mode 100644 new mode 100755 diff --git a/server/src/views/secondfactor.pug b/server/src/views/secondfactor.pug old mode 100644 new mode 100755 diff --git a/server/src/views/totp-register.pug b/server/src/views/totp-register.pug old mode 100644 new mode 100755 diff --git a/server/src/views/u2f-register.pug b/server/src/views/u2f-register.pug old mode 100644 new mode 100755 diff --git a/server/test/requests.ts b/server/test/requests.ts old mode 100644 new mode 100755 diff --git a/server/tsconfig.json b/server/tsconfig.json old mode 100644 new mode 100755 diff --git a/server/tslint.json b/server/tslint.json old mode 100644 new mode 100755 diff --git a/server/types/AuthenticationSession.ts b/server/types/AuthenticationSession.ts old mode 100644 new mode 100755 diff --git a/server/types/Dependencies.ts b/server/types/Dependencies.ts old mode 100644 new mode 100755 diff --git a/server/types/Identity.ts b/server/types/Identity.ts old mode 100644 new mode 100755 diff --git a/server/types/TOTPSecret.ts b/server/types/TOTPSecret.ts old mode 100644 new mode 100755 diff --git a/server/types/U2FRegistration.ts b/server/types/U2FRegistration.ts old mode 100644 new mode 100755 diff --git a/server/types/dovehash.d.ts b/server/types/dovehash.d.ts old mode 100644 new mode 100755 diff --git a/server/types/speakeasy.d.ts b/server/types/speakeasy.d.ts old mode 100644 new mode 100755 diff --git a/shared/BelongToDomain.ts b/shared/BelongToDomain.ts old mode 100644 new mode 100755 diff --git a/shared/DomainExtractor.spec.ts b/shared/DomainExtractor.spec.ts old mode 100644 new mode 100755 diff --git a/shared/DomainExtractor.ts b/shared/DomainExtractor.ts old mode 100644 new mode 100755 diff --git a/shared/ErrorMessage.ts b/shared/ErrorMessage.ts old mode 100644 new mode 100755 diff --git a/shared/RedirectionMessage.ts b/shared/RedirectionMessage.ts old mode 100644 new mode 100755 diff --git a/shared/SignMessage.ts b/shared/SignMessage.ts old mode 100644 new mode 100755 diff --git a/shared/UserMessages.ts b/shared/UserMessages.ts old mode 100644 new mode 100755 diff --git a/shared/api.ts b/shared/api.ts old mode 100644 new mode 100755 diff --git a/shared/constants.ts b/shared/constants.ts old mode 100644 new mode 100755 diff --git a/shared/types/u2f.d.ts b/shared/types/u2f.d.ts old mode 100644 new mode 100755 diff --git a/test/complete-config/00-suite.ts b/test/complete-config/00-suite.ts old mode 100644 new mode 100755 diff --git a/test/complete-config/closed-redirection.ts b/test/complete-config/closed-redirection.ts old mode 100644 new mode 100755 diff --git a/test/complete-config/mongo-broken-connection.ts b/test/complete-config/mongo-broken-connection.ts old mode 100644 new mode 100755 diff --git a/test/configuration.ts b/test/configuration.ts old mode 100644 new mode 100755 diff --git a/test/environment.ts b/test/environment.ts old mode 100644 new mode 100755 diff --git a/test/features/access-control.feature b/test/features/access-control.feature old mode 100644 new mode 100755 diff --git a/test/features/auth-portal-redirection.feature b/test/features/auth-portal-redirection.feature old mode 100644 new mode 100755 diff --git a/test/features/authelia.feature b/test/features/authelia.feature old mode 100644 new mode 100755 diff --git a/test/features/authentication.feature b/test/features/authentication.feature old mode 100644 new mode 100755 diff --git a/test/features/forward-headers.feature b/test/features/forward-headers.feature old mode 100644 new mode 100755 diff --git a/test/features/redirection.feature b/test/features/redirection.feature old mode 100644 new mode 100755 diff --git a/test/features/registration.feature b/test/features/registration.feature old mode 100644 new mode 100755 diff --git a/test/features/regulation.feature b/test/features/regulation.feature old mode 100644 new mode 100755 diff --git a/test/features/reset-password.feature b/test/features/reset-password.feature old mode 100644 new mode 100755 diff --git a/test/features/resilience.feature b/test/features/resilience.feature old mode 100644 new mode 100755 diff --git a/test/features/restrictions.feature b/test/features/restrictions.feature old mode 100644 new mode 100755 diff --git a/test/features/session-timeout.feature b/test/features/session-timeout.feature old mode 100644 new mode 100755 diff --git a/test/features/single-factor-domain.feature b/test/features/single-factor-domain.feature old mode 100644 new mode 100755 diff --git a/test/features/step_definitions/access-control.ts b/test/features/step_definitions/access-control.ts old mode 100644 new mode 100755 diff --git a/test/features/step_definitions/authelia.ts b/test/features/step_definitions/authelia.ts old mode 100644 new mode 100755 diff --git a/test/features/step_definitions/authentication.ts b/test/features/step_definitions/authentication.ts old mode 100644 new mode 100755 diff --git a/test/features/step_definitions/forward-headers.ts b/test/features/step_definitions/forward-headers.ts old mode 100644 new mode 100755 diff --git a/test/features/step_definitions/hooks.ts b/test/features/step_definitions/hooks.ts old mode 100644 new mode 100755 diff --git a/test/features/step_definitions/notifications.ts b/test/features/step_definitions/notifications.ts old mode 100644 new mode 100755 diff --git a/test/features/step_definitions/redirection.ts b/test/features/step_definitions/redirection.ts old mode 100644 new mode 100755 diff --git a/test/features/step_definitions/registration.ts b/test/features/step_definitions/registration.ts old mode 100644 new mode 100755 diff --git a/test/features/step_definitions/regulation.ts b/test/features/step_definitions/regulation.ts old mode 100644 new mode 100755 diff --git a/test/features/step_definitions/reset-password.ts b/test/features/step_definitions/reset-password.ts old mode 100644 new mode 100755 diff --git a/test/features/step_definitions/resilience.ts b/test/features/step_definitions/resilience.ts old mode 100644 new mode 100755 diff --git a/test/features/step_definitions/restrictions.ts b/test/features/step_definitions/restrictions.ts old mode 100644 new mode 100755 diff --git a/test/features/step_definitions/session-timeout.ts b/test/features/step_definitions/session-timeout.ts old mode 100644 new mode 100755 diff --git a/test/features/step_definitions/single-factor.ts b/test/features/step_definitions/single-factor.ts old mode 100644 new mode 100755 diff --git a/test/features/support/world.ts b/test/features/support/world.ts old mode 100644 new mode 100755 diff --git a/test/helpers/access-secret.ts b/test/helpers/access-secret.ts old mode 100644 new mode 100755 diff --git a/test/helpers/click-on-button.ts b/test/helpers/click-on-button.ts old mode 100644 new mode 100755 diff --git a/test/helpers/click-on-link.ts b/test/helpers/click-on-link.ts old mode 100644 new mode 100755 diff --git a/test/helpers/fill-field.ts b/test/helpers/fill-field.ts old mode 100644 new mode 100755 diff --git a/test/helpers/fill-login-page-and-click.ts b/test/helpers/fill-login-page-and-click.ts old mode 100644 new mode 100755 diff --git a/test/helpers/full-login.ts b/test/helpers/full-login.ts old mode 100644 new mode 100755 diff --git a/test/helpers/get-identity-link.ts b/test/helpers/get-identity-link.ts old mode 100644 new mode 100755 diff --git a/test/helpers/login-and-register-totp.ts b/test/helpers/login-and-register-totp.ts old mode 100644 new mode 100755 diff --git a/test/helpers/login-as.ts b/test/helpers/login-as.ts old mode 100644 new mode 100755 diff --git a/test/helpers/register-totp.ts b/test/helpers/register-totp.ts old mode 100644 new mode 100755 diff --git a/test/helpers/see-notification.ts b/test/helpers/see-notification.ts old mode 100644 new mode 100755 diff --git a/test/helpers/validate-totp.ts b/test/helpers/validate-totp.ts old mode 100644 new mode 100755 diff --git a/test/helpers/visit-page.ts b/test/helpers/visit-page.ts old mode 100644 new mode 100755 diff --git a/test/helpers/wait-redirected.ts b/test/helpers/wait-redirected.ts old mode 100644 new mode 100755 diff --git a/test/helpers/with-driver.ts b/test/helpers/with-driver.ts old mode 100644 new mode 100755 diff --git a/test/inactivity/00-suite.ts b/test/inactivity/00-suite.ts old mode 100644 new mode 100755 diff --git a/test/inactivity/keep_me_logged_in.ts b/test/inactivity/keep_me_logged_in.ts old mode 100644 new mode 100755 diff --git a/test/minimal-config/00-suite.ts b/test/minimal-config/00-suite.ts old mode 100644 new mode 100755 diff --git a/test/minimal-config/bad_password.ts b/test/minimal-config/bad_password.ts old mode 100644 new mode 100755 diff --git a/test/minimal-config/fail_totp.ts b/test/minimal-config/fail_totp.ts old mode 100644 new mode 100755 diff --git a/test/minimal-config/register_totp.ts b/test/minimal-config/register_totp.ts old mode 100644 new mode 100755 diff --git a/test/minimal-config/reset_password.ts b/test/minimal-config/reset_password.ts old mode 100644 new mode 100755 diff --git a/test/minimal-config/validate_totp.ts b/test/minimal-config/validate_totp.ts old mode 100644 new mode 100755 diff --git a/themes/black/client/src/css/.directory b/themes/black/client/src/css/.directory old mode 100644 new mode 100755 diff --git a/themes/black/client/src/css/00-bootstrap.min.css b/themes/black/client/src/css/00-bootstrap.min.css old mode 100644 new mode 100755 diff --git a/themes/black/client/src/css/01-main.css b/themes/black/client/src/css/01-main.css old mode 100644 new mode 100755 index 318b90e25..e62ff8dd0 --- a/themes/black/client/src/css/01-main.css +++ b/themes/black/client/src/css/01-main.css @@ -2,7 +2,7 @@ body { /*background-image: url("//*img//*LargeTriangles.svg");*/ /*background-image: url("//*img//*RandomizedPattern.svg");*/ /*background-image: url("//*img//*background.svg");*/ - background-color:#000000;*/ + background-color:#000000; } canvas{ position:absolute; diff --git a/themes/black/client/src/css/02-login.css b/themes/black/client/src/css/02-login.css old mode 100644 new mode 100755 diff --git a/themes/black/client/src/css/03-errors.css b/themes/black/client/src/css/03-errors.css old mode 100644 new mode 100755 diff --git a/themes/black/client/src/css/03-password-reset-form.css b/themes/black/client/src/css/03-password-reset-form.css old mode 100644 new mode 100755 diff --git a/themes/black/client/src/css/03-password-reset-request.css b/themes/black/client/src/css/03-password-reset-request.css old mode 100644 new mode 100755 diff --git a/themes/black/client/src/css/03-totp-register.css b/themes/black/client/src/css/03-totp-register.css old mode 100644 new mode 100755 diff --git a/themes/black/client/src/css/03-u2f-register.css b/themes/black/client/src/css/03-u2f-register.css old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/RandomizedPattern.svg b/themes/black/client/src/img/RandomizedPattern.svg old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/background.jpg b/themes/black/client/src/img/background.jpg old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/icon.png b/themes/black/client/src/img/icon.png old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/mail.png b/themes/black/client/src/img/mail.png old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/notifications/.directory b/themes/black/client/src/img/notifications/.directory old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/notifications/error.png b/themes/black/client/src/img/notifications/error.png old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/notifications/info.png b/themes/black/client/src/img/notifications/info.png old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/notifications/success.png b/themes/black/client/src/img/notifications/success.png old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/notifications/warning.png b/themes/black/client/src/img/notifications/warning.png old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/padlock.png b/themes/black/client/src/img/padlock.png old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/password_white.png b/themes/black/client/src/img/password_white.png old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/pendrive.png b/themes/black/client/src/img/pendrive.png old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/sharingan.png b/themes/black/client/src/img/sharingan.png old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/stores/.directory b/themes/black/client/src/img/stores/.directory old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/stores/applestore-badge.svg b/themes/black/client/src/img/stores/applestore-badge.svg old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/stores/googleplay-badge.svg b/themes/black/client/src/img/stores/googleplay-badge.svg old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/success.png b/themes/black/client/src/img/success.png old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/user.png b/themes/black/client/src/img/user.png old mode 100644 new mode 100755 diff --git a/themes/black/client/src/img/warning.png b/themes/black/client/src/img/warning.png old mode 100644 new mode 100755 diff --git a/themes/black/client/src/index.ts b/themes/black/client/src/index.ts deleted file mode 100644 index 802004a8f..000000000 --- a/themes/black/client/src/index.ts +++ /dev/null @@ -1,34 +0,0 @@ - -import FirstFactorValidator = require("./lib/firstfactor/FirstFactorValidator"); - -import FirstFactor from "./lib/firstfactor/index"; -import SecondFactor from "./lib/secondfactor/index"; -import TOTPRegister from "./lib/totp-register/totp-register"; -import U2fRegister from "./lib/u2f-register/u2f-register"; -import ResetPasswordRequest from "./lib/reset-password/reset-password-request"; -import ResetPasswordForm from "./lib/reset-password/reset-password-form"; -import jslogger = require("js-logger"); -import jQuery = require("jquery"); -import Endpoints = require("../../shared/api"); - -jslogger.useDefaults(); -jslogger.setLevel(jslogger.INFO); - -(function () { - (window).jQuery = jQuery; - require("bootstrap"); - - jQuery('[data-toggle="tooltip"]').tooltip(); - if (window.location.pathname == Endpoints.FIRST_FACTOR_GET) - FirstFactor(window, jQuery, FirstFactorValidator, jslogger); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_GET) - SecondFactor(window, jQuery, (global as any).u2f); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET) - TOTPRegister(window, jQuery); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET) - U2fRegister(window, jQuery, (global as any).u2f); - else if (window.location.pathname == Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET) - ResetPasswordForm(window, jQuery); - else if (window.location.pathname == Endpoints.RESET_PASSWORD_REQUEST_GET) - ResetPasswordRequest(window, jQuery); -})(); diff --git a/themes/black/client/src/lib/GetPromised.ts b/themes/black/client/src/lib/GetPromised.ts deleted file mode 100644 index 779139654..000000000 --- a/themes/black/client/src/lib/GetPromised.ts +++ /dev/null @@ -1,14 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -export default function ($: JQueryStatic, url: string, data: Object, fn: any, - dataType: string): BluebirdPromise { - return new BluebirdPromise((resolve, reject) => { - $.get(url, {}, undefined, dataType) - .done((data: any) => { - resolve(data); - }) - .fail((xhr: JQueryXHR, textStatus: string) => { - reject(textStatus); - }); - }); -} \ No newline at end of file diff --git a/themes/black/client/src/lib/INotifier.ts b/themes/black/client/src/lib/INotifier.ts deleted file mode 100644 index df947538f..000000000 --- a/themes/black/client/src/lib/INotifier.ts +++ /dev/null @@ -1,14 +0,0 @@ - -declare type Handler = () => void; - -export interface Handlers { - onFadedIn: Handler; - onFadedOut: Handler; -} - -export interface INotifier { - success(msg: string, handlers?: Handlers): void; - error(msg: string, handlers?: Handlers): void; - warning(msg: string, handlers?: Handlers): void; - info(msg: string, handlers?: Handlers): void; -} \ No newline at end of file diff --git a/themes/black/client/src/lib/Notifier.ts b/themes/black/client/src/lib/Notifier.ts deleted file mode 100644 index c0252b9b9..000000000 --- a/themes/black/client/src/lib/Notifier.ts +++ /dev/null @@ -1,83 +0,0 @@ - - -import util = require("util"); -import { INotifier, Handlers } from "./INotifier"; - -class NotificationEvent { - private element: JQuery; - private message: string; - private statusType: string; - private timeoutId: any; - - constructor(element: JQuery, msg: string, statusType: string) { - this.message = msg; - this.statusType = statusType; - this.element = element; - } - - private clearNotification() { - this.element.removeClass(this.statusType); - this.element.html(""); - } - - start(handlers?: Handlers) { - const that = this; - const FADE_TIME = 500; - const html = util.format('status %s\ - %s', this.statusType, this.statusType, this.message); - this.element.html(html); - this.element.addClass(this.statusType); - this.element.fadeIn(FADE_TIME, function () { - if (handlers) - handlers.onFadedIn(); - }); - - this.timeoutId = setTimeout(function () { - that.element.fadeOut(FADE_TIME, function () { - that.clearNotification(); - if (handlers) - handlers.onFadedOut(); - }); - }, 4000); - } - - interrupt() { - this.clearNotification(); - this.element.hide(); - clearTimeout(this.timeoutId); - } -} - -export class Notifier implements INotifier { - private element: JQuery; - private onGoingEvent: NotificationEvent; - - constructor(selector: string, $: JQueryStatic) { - this.element = $(selector); - this.onGoingEvent = undefined; - } - - private displayAndFadeout(msg: string, statusType: string, handlers?: Handlers): void { - if (this.onGoingEvent) - this.onGoingEvent.interrupt(); - - this.onGoingEvent = new NotificationEvent(this.element, msg, statusType); - this.onGoingEvent.start(handlers); - } - - success(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "success", handlers); - } - - error(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "error", handlers); - } - - warning(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "warning", handlers); - } - - info(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "info", handlers); - } -} \ No newline at end of file diff --git a/themes/black/client/src/lib/QueryParametersRetriever.ts b/themes/black/client/src/lib/QueryParametersRetriever.ts deleted file mode 100644 index a529adb6e..000000000 --- a/themes/black/client/src/lib/QueryParametersRetriever.ts +++ /dev/null @@ -1,12 +0,0 @@ - -export class QueryParametersRetriever { - static get(name: string, url?: string): string { - if (!url) url = window.location.href; - name = name.replace(/[\[\]]/g, "\\$&"); - const regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), - results = regex.exec(url); - if (!results) return undefined; - if (!results[2]) return ""; - return decodeURIComponent(results[2].replace(/\+/g, " ")); - } -} \ No newline at end of file diff --git a/themes/black/client/src/lib/SafeRedirect.ts b/themes/black/client/src/lib/SafeRedirect.ts deleted file mode 100644 index 7e7684b8a..000000000 --- a/themes/black/client/src/lib/SafeRedirect.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BelongToDomain } from "../../../shared/BelongToDomain"; - -export function SafeRedirect(url: string, cb: () => void): void { - const domain = window.location.hostname.split(".").slice(-2).join("."); - if (url.startsWith("/") || BelongToDomain(url, domain)) { - window.location.href = url; - return; - } - cb(); -} \ No newline at end of file diff --git a/themes/black/client/src/lib/firstfactor/FirstFactorValidator.ts b/themes/black/client/src/lib/firstfactor/FirstFactorValidator.ts deleted file mode 100644 index eaa496fdd..000000000 --- a/themes/black/client/src/lib/firstfactor/FirstFactorValidator.ts +++ /dev/null @@ -1,46 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import Constants = require("../../../../shared/constants"); -import Util = require("util"); -import UserMessages = require("../../../../shared/UserMessages"); - -export function validate(username: string, password: string, - keepMeLoggedIn: boolean, redirectUrl: string, $: JQueryStatic) - : BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - let url: string; - if (redirectUrl != undefined) { - const redirectParam = Util.format("%s=%s", Constants.REDIRECT_QUERY_PARAM, redirectUrl); - url = Util.format("%s?%s", Endpoints.FIRST_FACTOR_POST, redirectParam); - } - else { - url = Util.format("%s", Endpoints.FIRST_FACTOR_POST); - } - - const data: any = { - username: username, - password: password, - }; - - if (keepMeLoggedIn) { - data.keepMeLoggedIn = "true"; - } - - $.ajax({ - method: "POST", - url: url, - data: data - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(body.redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(UserMessages.AUTHENTICATION_FAILED)); - }); - }); -} diff --git a/themes/black/client/src/lib/firstfactor/UISelectors.ts b/themes/black/client/src/lib/firstfactor/UISelectors.ts deleted file mode 100644 index 0e971b3c3..000000000 --- a/themes/black/client/src/lib/firstfactor/UISelectors.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export const USERNAME_FIELD_ID = "#username"; -export const PASSWORD_FIELD_ID = "#password"; -export const SIGN_IN_BUTTON_ID = "#signin"; -export const KEEP_ME_LOGGED_IN_ID = "#keep_me_logged_in"; diff --git a/themes/black/client/src/lib/firstfactor/index.ts b/themes/black/client/src/lib/firstfactor/index.ts deleted file mode 100644 index 24affee2d..000000000 --- a/themes/black/client/src/lib/firstfactor/index.ts +++ /dev/null @@ -1,49 +0,0 @@ -import FirstFactorValidator = require("./FirstFactorValidator"); -import JSLogger = require("js-logger"); -import UISelectors = require("./UISelectors"); -import { Notifier } from "../Notifier"; -import { QueryParametersRetriever } from "../QueryParametersRetriever"; -import Constants = require("../../../../shared/constants"); -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic, - firstFactorValidator: typeof FirstFactorValidator, jslogger: typeof JSLogger) { - - const notifier = new Notifier(".notification", $); - - function onFormSubmitted() { - const username: string = $(UISelectors.USERNAME_FIELD_ID).val() as string; - const password: string = $(UISelectors.PASSWORD_FIELD_ID).val() as string; - const keepMeLoggedIn: boolean = $(UISelectors.KEEP_ME_LOGGED_IN_ID).is(":checked"); - - $("form").css("opacity", 0.5); - $("input,button").attr("disabled", "true"); - $(UISelectors.SIGN_IN_BUTTON_ID).text("Please wait..."); - - const redirectUrl = QueryParametersRetriever.get(Constants.REDIRECT_QUERY_PARAM); - firstFactorValidator.validate(username, password, keepMeLoggedIn, redirectUrl, $) - .then(onFirstFactorSuccess, onFirstFactorFailure); - return false; - } - - function onFirstFactorSuccess(redirectUrl: string) { - SafeRedirect(redirectUrl, () => { - notifier.error("Cannot redirect to an external domain."); - }); - } - - function onFirstFactorFailure(err: Error) { - $("input,button").removeAttr("disabled"); - $("form").css("opacity", 1); - notifier.error(UserMessages.AUTHENTICATION_FAILED); - $(UISelectors.PASSWORD_FIELD_ID).select(); - $(UISelectors.SIGN_IN_BUTTON_ID).text("Sign in"); - } - - $(window.document).ready(function () { - $("form").on("submit", onFormSubmitted); - }); -} - diff --git a/themes/black/client/src/lib/reset-password/constants.ts b/themes/black/client/src/lib/reset-password/constants.ts deleted file mode 100644 index d48d4e67d..000000000 --- a/themes/black/client/src/lib/reset-password/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const FORM_SELECTOR = ".form-signin"; \ No newline at end of file diff --git a/themes/black/client/src/lib/reset-password/reset-password-form.ts b/themes/black/client/src/lib/reset-password/reset-password-form.ts deleted file mode 100644 index b94279cde..000000000 --- a/themes/black/client/src/lib/reset-password/reset-password-form.ts +++ /dev/null @@ -1,57 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); - -import Constants = require("./constants"); -import { Notifier } from "../Notifier"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function modifyPassword(newPassword: string) { - return new BluebirdPromise(function (resolve, reject) { - $.post(Endpoints.RESET_PASSWORD_FORM_POST, { - password: newPassword, - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(body); - }) - .fail(function (xhr, status) { - reject(status); - }); - }); - } - - function onFormSubmitted() { - const password1 = $("#password1").val() as string; - const password2 = $("#password2").val() as string; - - if (!password1 || !password2) { - notifier.warning(UserMessages.MISSING_PASSWORD); - return false; - } - - if (password1 != password2) { - notifier.warning(UserMessages.DIFFERENT_PASSWORDS); - return false; - } - - modifyPassword(password1) - .then(function () { - window.location.href = Endpoints.FIRST_FACTOR_GET; - }) - .error(function () { - notifier.error(UserMessages.RESET_PASSWORD_FAILED); - }); - return false; - } - - $(document).ready(function () { - $(Constants.FORM_SELECTOR).on("submit", onFormSubmitted); - }); -} diff --git a/themes/black/client/src/lib/reset-password/reset-password-request.ts b/themes/black/client/src/lib/reset-password/reset-password-request.ts deleted file mode 100644 index 846226d75..000000000 --- a/themes/black/client/src/lib/reset-password/reset-password-request.ts +++ /dev/null @@ -1,56 +0,0 @@ - -import BluebirdPromise = require("bluebird"); - -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import Constants = require("./constants"); -import jslogger = require("js-logger"); -import { Notifier } from "../Notifier"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function requestPasswordReset(username: string) { - return new BluebirdPromise(function (resolve, reject) { - $.get(Endpoints.RESET_PASSWORD_IDENTITY_START_GET, { - userid: username, - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); - } - - function onFormSubmitted() { - const username = $("#username").val() as string; - - if (!username) { - notifier.warning(UserMessages.MISSING_USERNAME); - return; - } - - requestPasswordReset(username) - .then(function () { - notifier.success(UserMessages.MAIL_SENT); - setTimeout(function () { - window.location.replace(Endpoints.FIRST_FACTOR_GET); - }, 1000); - }) - .error(function () { - notifier.error(UserMessages.MAIL_NOT_SENT); - }); - return false; - } - - $(document).ready(function () { - $(Constants.FORM_SELECTOR).on("submit", onFormSubmitted); - }); -} - diff --git a/themes/black/client/src/lib/secondfactor/TOTPValidator.ts b/themes/black/client/src/lib/secondfactor/TOTPValidator.ts deleted file mode 100644 index 5394139a6..000000000 --- a/themes/black/client/src/lib/secondfactor/TOTPValidator.ts +++ /dev/null @@ -1,28 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; - -export function validate(token: string, $: JQueryStatic): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.ajax({ - url: Endpoints.SECOND_FACTOR_TOTP_POST, - data: { - token: token, - }, - method: "POST", - dataType: "json" - } as JQueryAjaxSettings) - .done(function (body: RedirectionMessage | ErrorMessage) { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); -} \ No newline at end of file diff --git a/themes/black/client/src/lib/secondfactor/U2FValidator.ts b/themes/black/client/src/lib/secondfactor/U2FValidator.ts deleted file mode 100644 index 5812922f7..000000000 --- a/themes/black/client/src/lib/secondfactor/U2FValidator.ts +++ /dev/null @@ -1,42 +0,0 @@ -import U2f = require("u2f"); -import U2fApi from "u2f-api"; -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { INotifier } from "../INotifier"; -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; -import GetPromised from "../GetPromised"; - -function finishU2fAuthentication(responseData: U2fApi.SignResponse, - $: JQueryStatic): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.ajax({ - url: Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - data: responseData, - method: "POST", - dataType: "json" - } as JQueryAjaxSettings) - .done(function (body: RedirectionMessage | ErrorMessage) { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); -} - -export function validate($: JQueryStatic): BluebirdPromise { - return GetPromised($, Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, {}, - undefined, "json") - .then(function (signRequest: U2f.Request) { - return U2fApi.sign(signRequest, 60); - }) - .then(function (signResponse: U2fApi.SignResponse) { - return finishU2fAuthentication(signResponse, $); - }); -} diff --git a/themes/black/client/src/lib/secondfactor/constants.ts b/themes/black/client/src/lib/secondfactor/constants.ts deleted file mode 100644 index 50bba7571..000000000 --- a/themes/black/client/src/lib/secondfactor/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ - -export const TOTP_FORM_SELECTOR = ".form-signin.totp"; -export const TOTP_TOKEN_SELECTOR = ".form-signin #token"; diff --git a/themes/black/client/src/lib/secondfactor/index.ts b/themes/black/client/src/lib/secondfactor/index.ts deleted file mode 100644 index 279723dce..000000000 --- a/themes/black/client/src/lib/secondfactor/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import TOTPValidator = require("./TOTPValidator"); -import U2FValidator = require("./U2FValidator"); -import ClientConstants = require("./constants"); -import { Notifier } from "../Notifier"; -import { QueryParametersRetriever } from "../QueryParametersRetriever"; -import UserMessages = require("../../../../shared/UserMessages"); -import SharedConstants = require("../../../../shared/constants"); -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function onAuthenticationSuccess(serverRedirectUrl: string) { - const queryRedirectUrl = QueryParametersRetriever.get(SharedConstants.REDIRECT_QUERY_PARAM); - if (queryRedirectUrl) { - SafeRedirect(queryRedirectUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - } else if (serverRedirectUrl) { - SafeRedirect(serverRedirectUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - } else { - notifier.success(UserMessages.AUTHENTICATION_SUCCEEDED); - } - } - - function onSecondFactorTotpSuccess(redirectUrl: string) { - onAuthenticationSuccess(redirectUrl); - } - - function onSecondFactorTotpFailure(err: Error) { - notifier.error(UserMessages.AUTHENTICATION_TOTP_FAILED); - } - - function onU2fAuthenticationSuccess(redirectUrl: string) { - onAuthenticationSuccess(redirectUrl); - } - - function onU2fAuthenticationFailure() { - // TODO(clems4ever): we should not display this error message until a device - // is registered. - // notifier.error(UserMessages.AUTHENTICATION_U2F_FAILED); - } - - function onTOTPFormSubmitted(): boolean { - const token = $(ClientConstants.TOTP_TOKEN_SELECTOR).val() as string; - TOTPValidator.validate(token, $) - .then(onSecondFactorTotpSuccess) - .catch(onSecondFactorTotpFailure); - return false; - } - - $(window.document).ready(function () { - $(ClientConstants.TOTP_FORM_SELECTOR).on("submit", onTOTPFormSubmitted); - U2FValidator.validate($) - .then(onU2fAuthenticationSuccess, onU2fAuthenticationFailure); - }); -} \ No newline at end of file diff --git a/themes/black/client/src/lib/totp-register/totp-register.ts b/themes/black/client/src/lib/totp-register/totp-register.ts deleted file mode 100644 index 6a9aa7ee0..000000000 --- a/themes/black/client/src/lib/totp-register/totp-register.ts +++ /dev/null @@ -1,11 +0,0 @@ - -import jslogger = require("js-logger"); -import UISelector = require("./ui-selector"); - -export default function(window: Window, $: JQueryStatic) { - jslogger.debug("Creating QRCode from OTPAuth url"); - const qrcode = $(UISelector.QRCODE_ID_SELECTOR); - const val = qrcode.text(); - qrcode.empty(); - new (window as any).QRCode(qrcode.get(0), val); -} diff --git a/themes/black/client/src/lib/totp-register/ui-selector.ts b/themes/black/client/src/lib/totp-register/ui-selector.ts deleted file mode 100644 index 9d43fabea..000000000 --- a/themes/black/client/src/lib/totp-register/ui-selector.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const QRCODE_ID_SELECTOR = "#qrcode"; \ No newline at end of file diff --git a/themes/black/client/src/lib/u2f-register/u2f-register.ts b/themes/black/client/src/lib/u2f-register/u2f-register.ts deleted file mode 100644 index abf40ee05..000000000 --- a/themes/black/client/src/lib/u2f-register/u2f-register.ts +++ /dev/null @@ -1,56 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import U2f = require("u2f"); -import * as U2fApi from "u2f-api"; -import { Notifier } from "../Notifier"; -import GetPromised from "../GetPromised"; -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function checkRegistration(regResponse: U2fApi.RegisterResponse): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, regResponse, undefined, "json") - .done((body: RedirectionMessage | ErrorMessage) => { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail((xhr, status) => { - reject(new Error("Failed to register device.")); - }); - }); - } - - function requestRegistration(): BluebirdPromise { - return GetPromised($, Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, {}, - undefined, "json") - .then((registrationRequest: U2f.Request) => { - return U2fApi.register(registrationRequest, [], 60); - }) - .then((res) => checkRegistration(res)); - } - - function onRegisterFailure(err: Error) { - notifier.error(UserMessages.REGISTRATION_U2F_FAILED); - } - - $(document).ready(function () { - requestRegistration() - .then((redirectionUrl: string) => { - SafeRedirect(redirectionUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - }) - .catch((err) => { - onRegisterFailure(err); - }); - }); -} diff --git a/themes/black/client/src/thirdparties/qrcode.min.js b/themes/black/client/src/thirdparties/qrcode.min.js old mode 100644 new mode 100755 diff --git a/themes/black/client/src/thirdparties/u2f-api.js b/themes/black/client/src/thirdparties/u2f-api.js old mode 100644 new mode 100755 diff --git a/themes/black/client/test/Notifier.test.ts b/themes/black/client/test/Notifier.test.ts deleted file mode 100644 index 70bfea146..000000000 --- a/themes/black/client/test/Notifier.test.ts +++ /dev/null @@ -1,71 +0,0 @@ - -import Assert = require("assert"); -import Sinon = require("sinon"); -import JQueryMock = require("./mocks/jquery"); - -import { Notifier } from "../src/lib/Notifier"; - -describe("test notifier", function() { - const SELECTOR = "dummy-selector"; - const MESSAGE = "This is a message"; - let jqueryMock: { jquery: JQueryMock.JQueryMock, element: JQueryMock.JQueryElementsMock }; - let clock: any; - - beforeEach(function() { - jqueryMock = JQueryMock.JQueryMock(); - clock = Sinon.useFakeTimers(); - }); - - afterEach(function() { - clock.restore(); - }); - - function should_fade_in_and_out_on_notification(notificationType: string): void { - const delayReturn = { - fadeOut: Sinon.stub() - }; - - jqueryMock.element.fadeIn.yields(); - - function onFadedInCallback() { - Assert(jqueryMock.element.fadeIn.calledOnce); - Assert(jqueryMock.element.addClass.calledWith(notificationType)); - Assert(!jqueryMock.element.removeClass.calledWith(notificationType)); - clock.tick(10 * 1000); - } - - function onFadedOutCallback() { - Assert(jqueryMock.element.removeClass.calledWith(notificationType)); - Assert(jqueryMock.element.fadeOut.calledOnce); - } - - const notifier = new Notifier(SELECTOR, jqueryMock.jquery as any); - - // Call the method by its name... Bad but allows code reuse. - (notifier as any)[notificationType](MESSAGE, { - onFadedIn: onFadedInCallback, - onFadedOut: onFadedOutCallback - }); - - clock.tick(510); - - Assert(jqueryMock.element.fadeIn.calledOnce); - } - - - it("should fade in and fade out an error message", function() { - should_fade_in_and_out_on_notification("error"); - }); - - it("should fade in and fade out an info message", function() { - should_fade_in_and_out_on_notification("info"); - }); - - it("should fade in and fade out an warning message", function() { - should_fade_in_and_out_on_notification("warning"); - }); - - it("should fade in and fade out an success message", function() { - should_fade_in_and_out_on_notification("success"); - }); -}); \ No newline at end of file diff --git a/themes/black/client/test/firstfactor/FirstFactorValidator.test.ts b/themes/black/client/test/firstfactor/FirstFactorValidator.test.ts deleted file mode 100644 index ac8353278..000000000 --- a/themes/black/client/test/firstfactor/FirstFactorValidator.test.ts +++ /dev/null @@ -1,44 +0,0 @@ - -import FirstFactorValidator = require("../../src/lib/firstfactor/FirstFactorValidator"); -import JQueryMock = require("../mocks/jquery"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -describe("test FirstFactorValidator", function () { - it("should validate first factor successfully", () => { - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.done.yields({ redirect: "http://redirect" }); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return FirstFactorValidator.validate("username", "password", "http://redirect", jqueryMock.jquery as any); - }); - - function should_fail_first_factor_validation(errorMessage: string) { - const xhr = { - status: 401 - }; - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.fail.yields(xhr, errorMessage); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return FirstFactorValidator.validate("username", "password", "http://redirect", jqueryMock.jquery as any) - .then(function () { - return BluebirdPromise.reject(new Error("First factor validation successfully finished while it should have not.")); - }, function (err: Error) { - Assert.equal(errorMessage, err.message); - return BluebirdPromise.resolve(); - }); - } - - describe("should fail first factor validation", () => { - it("should fail with error", () => { - return should_fail_first_factor_validation("Authentication failed. Please check your credentials."); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/client/test/mocks/NotifierStub.ts b/themes/black/client/test/mocks/NotifierStub.ts deleted file mode 100644 index 9c268d66d..000000000 --- a/themes/black/client/test/mocks/NotifierStub.ts +++ /dev/null @@ -1,33 +0,0 @@ - -import Sinon = require("sinon"); -import { INotifier } from "../../src/lib/INotifier"; - -export class NotifierStub implements INotifier { - successStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - warnStub: Sinon.SinonStub; - infoStub: Sinon.SinonStub; - - constructor() { - this.successStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - this.warnStub = Sinon.stub(); - this.infoStub = Sinon.stub(); - } - - success(msg: string) { - this.successStub(); - } - - error(msg: string) { - this.errorStub(); - } - - warning(msg: string) { - this.warnStub(); - } - - info(msg: string) { - this.infoStub(); - } -} \ No newline at end of file diff --git a/themes/black/client/test/mocks/jquery.ts b/themes/black/client/test/mocks/jquery.ts deleted file mode 100644 index 273f90861..000000000 --- a/themes/black/client/test/mocks/jquery.ts +++ /dev/null @@ -1,59 +0,0 @@ - -import sinon = require("sinon"); -import jquery = require("jquery"); - - -export interface JQueryMock extends sinon.SinonStub { - get: sinon.SinonStub; - post: sinon.SinonStub; - ajax: sinon.SinonStub; - notify: sinon.SinonStub; -} - -export interface JQueryElementsMock { - ready: sinon.SinonStub; - show: sinon.SinonStub; - hide: sinon.SinonStub; - html: sinon.SinonStub; - addClass: sinon.SinonStub; - removeClass: sinon.SinonStub; - fadeIn: sinon.SinonStub; - fadeOut: sinon.SinonStub; - on: sinon.SinonStub; -} - -export interface JQueryDeferredMock { - done: sinon.SinonStub; - fail: sinon.SinonStub; -} - -export function JQueryMock(): { jquery: JQueryMock, element: JQueryElementsMock } { - const jquery = sinon.stub() as any; - const jqueryInstance: JQueryElementsMock = { - ready: sinon.stub(), - show: sinon.stub(), - hide: sinon.stub(), - html: sinon.stub(), - addClass: sinon.stub(), - removeClass: sinon.stub(), - fadeIn: sinon.stub(), - fadeOut: sinon.stub(), - on: sinon.stub() - }; - jquery.ajax = sinon.stub(); - jquery.get = sinon.stub(); - jquery.post = sinon.stub(); - jquery.notify = sinon.stub(); - jquery.returns(jqueryInstance); - return { - jquery: jquery, - element: jqueryInstance - }; -} - -export function JQueryDeferredMock(): JQueryDeferredMock { - return { - done: sinon.stub(), - fail: sinon.stub() - }; -} diff --git a/themes/black/client/test/mocks/u2f-api.ts b/themes/black/client/test/mocks/u2f-api.ts deleted file mode 100644 index d123f6a95..000000000 --- a/themes/black/client/test/mocks/u2f-api.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import sinon = require("sinon"); - -export interface U2FApiMock { - sign: sinon.SinonStub; - register: sinon.SinonStub; -} - -export function U2FApiMock(): U2FApiMock { - return { - sign: sinon.stub(), - register: sinon.stub() - }; -} \ No newline at end of file diff --git a/themes/black/client/test/secondfactor/TOTPValidator.test.ts b/themes/black/client/test/secondfactor/TOTPValidator.test.ts deleted file mode 100644 index 5dd6f15c3..000000000 --- a/themes/black/client/test/secondfactor/TOTPValidator.test.ts +++ /dev/null @@ -1,37 +0,0 @@ - -import TOTPValidator = require("../../src/lib/secondfactor/TOTPValidator"); -import JQueryMock = require("../mocks/jquery"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -describe("test TOTPValidator", function () { - it("should initiate an identity check successfully", () => { - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.done.yields({ redirect: "https://home.test.url" }); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return TOTPValidator.validate("totp_token", jqueryMock.jquery as any); - }); - - it("should fail validating TOTP token", () => { - const errorMessage = "Error while validating TOTP token"; - - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.fail.yields(undefined, errorMessage); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return TOTPValidator.validate("totp_token", jqueryMock.jquery as any) - .then(function () { - return BluebirdPromise.reject(new Error("Registration successfully finished while it should have not.")); - }, function (err: Error) { - Assert.equal(errorMessage, err.message); - return BluebirdPromise.resolve(); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/client/test/totp-register/totp-register.test.ts b/themes/black/client/test/totp-register/totp-register.test.ts deleted file mode 100644 index 86fc455a1..000000000 --- a/themes/black/client/test/totp-register/totp-register.test.ts +++ /dev/null @@ -1,31 +0,0 @@ - -import sinon = require("sinon"); -import assert = require("assert"); - -import UISelector = require("../../src/lib/totp-register/ui-selector"); -import TOTPRegister = require("../../src/lib/totp-register/totp-register"); - -describe("test totp-register", function() { - let jqueryMock: any; - let windowMock: any; - before(function() { - jqueryMock = sinon.stub(); - windowMock = { - QRCode: sinon.spy() - }; - }); - - it("should create qrcode in page", function() { - const mock = { - text: sinon.stub(), - empty: sinon.stub(), - get: sinon.stub() - }; - jqueryMock.withArgs(UISelector.QRCODE_ID_SELECTOR).returns(mock); - - TOTPRegister.default(windowMock, jqueryMock); - - assert(mock.text.calledOnce); - assert(mock.empty.calledOnce); - }); -}); \ No newline at end of file diff --git a/themes/black/client/tsconfig.json b/themes/black/client/tsconfig.json deleted file mode 100644 index 0bb4d62ff..000000000 --- a/themes/black/client/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "moduleResolution": "node", - "noImplicitAny": true, - "sourceMap": true, - "removeComments": true, - "outDir": "../dist", - "baseUrl": ".", - "paths": { - "*": [ - "./types/*", - "../shared/types/*" - ] - } - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "test/**/*" - ] -} diff --git a/themes/black/client/tslint.json b/themes/black/client/tslint.json deleted file mode 100644 index c2c1b7501..000000000 --- a/themes/black/client/tslint.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "no-var-keyword": true, - "quotemark": [ - true, - "double", - "avoid-escape" - ], - "semicolon": [ - true, - "always", - "ignore-bound-class-methods" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-module", - "check-separator", - "check-type" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "onespace", - "index-signature": "onespace", - "parameter": "onespace", - "property-declaration": "onespace", - "variable-declaration": "onespace" - } - ], - "no-internal-module": true, - "no-trailing-whitespace": true, - "no-null-keyword": true, - "prefer-const": true, - "jsdoc-format": true - } -} diff --git a/themes/black/server/.directory b/themes/black/server/.directory old mode 100644 new mode 100755 diff --git a/themes/black/server/src/index.ts b/themes/black/server/src/index.ts deleted file mode 100755 index fcbf4d022..000000000 --- a/themes/black/server/src/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -#! /usr/bin/env node - -import Server from "./lib/Server"; -import { GlobalDependencies } from "../types/Dependencies"; -import YAML = require("yamljs"); - -const configurationFilepath = process.argv[2]; -if (!configurationFilepath) { - console.log("No config file has been provided."); - console.log("Usage: authelia "); - process.exit(0); -} - -const yamlContent = YAML.load(configurationFilepath); - -const deps: GlobalDependencies = { - u2f: require("u2f"), - ldapjs: require("ldapjs"), - session: require("express-session"), - winston: require("winston"), - speakeasy: require("speakeasy"), - nedb: require("nedb"), - ConnectRedis: require("connect-redis"), - Redis: require("redis") -}; - -const server = new Server(deps); -server.start(yamlContent, deps); diff --git a/themes/black/server/src/lib/.directory b/themes/black/server/src/lib/.directory deleted file mode 100644 index 006b379ad..000000000 --- a/themes/black/server/src/lib/.directory +++ /dev/null @@ -1,4 +0,0 @@ -[Dolphin] -Timestamp=2018,12,17,20,59,13 -Version=3 -ViewMode=1 diff --git a/themes/black/server/src/lib/AuthenticationSessionHandler.ts b/themes/black/server/src/lib/AuthenticationSessionHandler.ts deleted file mode 100644 index 57361bf8a..000000000 --- a/themes/black/server/src/lib/AuthenticationSessionHandler.ts +++ /dev/null @@ -1,45 +0,0 @@ - - -import express = require("express"); -import U2f = require("u2f"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { IRequestLogger } from "./logging/IRequestLogger"; -import { Level } from "./authentication/Level"; - -const INITIAL_AUTHENTICATION_SESSION: AuthenticationSession = { - keep_me_logged_in: false, - authentication_level: Level.NOT_AUTHENTICATED, - last_activity_datetime: undefined, - userid: undefined, - email: undefined, - groups: [], - register_request: undefined, - sign_request: undefined, - identity_check: undefined, - redirect: undefined -}; - -export class AuthenticationSessionHandler { - static reset(req: express.Request): void { - req.session.auth = Object.assign({}, INITIAL_AUTHENTICATION_SESSION, {}); - - // Initialize last activity with current time - req.session.auth.last_activity_datetime = new Date().getTime(); - } - - static get(req: express.Request, logger: IRequestLogger): AuthenticationSession { - if (!req.session) { - const errorMsg = "Something is wrong with session cookies. Please check Redis is running and Authelia can connect to it."; - logger.error(req, errorMsg); - throw new Error(errorMsg); - } - - if (!req.session.auth) { - logger.debug(req, "Authentication session %s was undefined. Resetting.", req.sessionID); - AuthenticationSessionHandler.reset(req); - } - - return req.session.auth; - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/ErrorReplies.ts b/themes/black/server/src/lib/ErrorReplies.ts deleted file mode 100644 index f1c5f4fd1..000000000 --- a/themes/black/server/src/lib/ErrorReplies.ts +++ /dev/null @@ -1,49 +0,0 @@ -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import { IRequestLogger } from "./logging/IRequestLogger"; - -function replyWithError(req: express.Request, res: express.Response, - code: number, logger: IRequestLogger, body?: Object): (err: Error) => void { - return function (err: Error): void { - if (req.originalUrl.startsWith("/api/") || code == 200) { - logger.error(req, "Reply with error %d: %s", code, err.message); - logger.debug(req, "%s", err.stack); - res.status(code); - res.send(body); - } - else { - logger.error(req, "Redirect to error %d: %s", code, err.message); - logger.debug(req, "%s", err.stack); - res.redirect("/error/" + code); - } - }; -} - -export function redirectTo(redirectUrl: string, req: express.Request, - res: express.Response, logger: IRequestLogger) { - return function(err: Error) { - logger.error(req, "Error: %s", err.message); - logger.debug(req, "Redirecting to %s", redirectUrl); - res.redirect(redirectUrl); - }; -} - -export function replyWithError400(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 400, logger); -} - -export function replyWithError401(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 401, logger); -} - -export function replyWithError403(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 403, logger); -} - -export function replyWithError200(req: express.Request, - res: express.Response, logger: IRequestLogger, message: string) { - return replyWithError(req, res, 200, logger, { error: message }); -} \ No newline at end of file diff --git a/themes/black/server/src/lib/Exceptions.ts b/themes/black/server/src/lib/Exceptions.ts deleted file mode 100644 index 83fa4eb6b..000000000 --- a/themes/black/server/src/lib/Exceptions.ts +++ /dev/null @@ -1,88 +0,0 @@ - -export class LdapSearchError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapSearchError"; - (Object).setPrototypeOf(this, LdapSearchError.prototype); - } -} - -export class LdapBindError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapBindError"; - (Object).setPrototypeOf(this, LdapBindError.prototype); - } -} - -export class LdapError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapError"; - (Object).setPrototypeOf(this, LdapError.prototype); - } -} - -export class IdentityError extends Error { - constructor(message?: string) { - super(message); - this.name = "IdentityError"; - (Object).setPrototypeOf(this, IdentityError.prototype); - } -} - -export class AccessDeniedError extends Error { - constructor(message?: string) { - super(message); - this.name = "AccessDeniedError"; - (Object).setPrototypeOf(this, AccessDeniedError.prototype); - } -} - -export class AuthenticationRegulationError extends Error { - constructor(message?: string) { - super(message); - this.name = "AuthenticationRegulationError"; - (Object).setPrototypeOf(this, AuthenticationRegulationError.prototype); - } -} - -export class InvalidTOTPError extends Error { - constructor(message?: string) { - super(message); - this.name = "InvalidTOTPError"; - (Object).setPrototypeOf(this, InvalidTOTPError.prototype); - } -} - -export class NotAuthenticatedError extends Error { - constructor(message?: string) { - super(message); - this.name = "NotAuthenticatedError"; - (Object).setPrototypeOf(this, NotAuthenticatedError.prototype); - } -} - -export class NotAuthorizedError extends Error { - constructor(message?: string) { - super(message); - this.name = "NotAuthanticatedError"; - (Object).setPrototypeOf(this, NotAuthorizedError.prototype); - } -} - -export class FirstFactorValidationError extends Error { - constructor(message?: string) { - super(message); - this.name = "FirstFactorValidationError"; - (Object).setPrototypeOf(this, FirstFactorValidationError.prototype); - } -} - -export class SecondFactorValidationError extends Error { - constructor(message?: string) { - super(message); - this.name = "SecondFactorValidationError"; - (Object).setPrototypeOf(this, FirstFactorValidationError.prototype); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/FirstFactorValidator.ts b/themes/black/server/src/lib/FirstFactorValidator.ts deleted file mode 100644 index 231060002..000000000 --- a/themes/black/server/src/lib/FirstFactorValidator.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import objectPath = require("object-path"); -import Exceptions = require("./Exceptions"); -import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler"; -import { IRequestLogger } from "./logging/IRequestLogger"; -import { Level } from "./authentication/Level"; - -export function validate(req: express.Request, logger: IRequestLogger): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, logger); - - if (!authSession.userid || authSession.authentication_level < Level.ONE_FACTOR) - return reject(new Exceptions.FirstFactorValidationError( - "First factor has not been validated yet.")); - - resolve(); - }); -} \ No newline at end of file diff --git a/themes/black/server/src/lib/IdentityCheckMiddleware.spec.ts b/themes/black/server/src/lib/IdentityCheckMiddleware.spec.ts deleted file mode 100644 index 842ed6bcb..000000000 --- a/themes/black/server/src/lib/IdentityCheckMiddleware.spec.ts +++ /dev/null @@ -1,176 +0,0 @@ - -import sinon = require("sinon"); -import IdentityValidator = require("./IdentityCheckMiddleware"); -import { AuthenticationSessionHandler } - from "./AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { UserDataStore } from "./storage/UserDataStore"; -import exceptions = require("./Exceptions"); -import { ServerVariables } from "./ServerVariables"; -import Assert = require("assert"); -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("./stubs/express.spec"); -import NotifierMock = require("./notifiers/NotifierStub.spec"); -import { IdentityValidableStub } from "./IdentityValidableStub.spec"; -import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "./ServerVariablesMockBuilder.spec"; -import { PRE_VALIDATION_TEMPLATE } - from "./IdentityCheckPreValidationTemplate"; - - -describe("IdentityCheckMiddleware", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let app: express.Application; - let app_get: sinon.SinonStub; - let app_post: sinon.SinonStub; - let identityValidable: IdentityValidableStub; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - - req.headers = {}; - req.originalUrl = "/non-api/xxx"; - req.session = {}; - - req.query = {}; - req.app = {}; - - identityValidable = new IdentityValidableStub(); - - mocks.notifier.notifyStub.returns(BluebirdPromise.resolve()); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve()); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({ userId: "user" })); - - app = express(); - app_get = sinon.stub(app, "get"); - app_post = sinon.stub(app, "post"); - }); - - afterEach(function () { - app_get.restore(); - app_post.restore(); - }); - - describe("test start GET", function () { - it("should redirect to error 401 if pre validation initialization \ -throws a first factor error", function () { - identityValidable.preValidationInitStub.returns(BluebirdPromise.reject( - new exceptions.FirstFactorValidationError( - "Error during prevalidation"))); - const callback = IdentityValidator.get_start_validation( - identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(() => { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - - // In that case we answer with 200 to avoid user enumeration. - it("should send 200 if email is missing in provided identity", function () { - const identity = { userid: "abc" }; - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(identityValidable.preValidationResponseStub.called); - }); - }); - - // In that case we answer with 200 to avoid user enumeration. - it("should send 200 if userid is missing in provided identity", - function () { - const endpoint = "/protected"; - const identity = { email: "abc@example.com" }; - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(identityValidable.preValidationResponseStub.called); - }); - }); - - it("should issue a token, send an email and return 204", function () { - const endpoint = "/protected"; - const identity = { userid: "user", email: "abc@example.com" }; - req.get = sinon.stub().withArgs("Host").returns("localhost"); - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/finish_endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(mocks.notifier.notifyStub.calledOnce); - Assert(mocks.userDataStore.produceIdentityValidationTokenStub - .calledOnce); - Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub - .getCall(0).args[0], "user"); - Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub - .getCall(0).args[3], 240000); - }); - }); - }); - - - - describe("test finish GET", function () { - it("should send 401 if no identity_token is provided", () => { - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - - it("should call postValidation if identity_token is provided and still \ -valid", function () { - req.query.identity_token = "token"; - - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - return callback(req as any, res as any, undefined); - }); - - it("should return 401 if identity_token is provided but invalid", - function () { - req.query.identity_token = "token"; - - identityValidable.postValidationInitStub - .returns(BluebirdPromise.resolve()); - mocks.userDataStore.consumeIdentityValidationTokenStub.reset(); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.reject(new Error("Invalid token"))); - - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - return callback(req as any, res as any, undefined) - .then(() => { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - }); -}); diff --git a/themes/black/server/src/lib/IdentityCheckMiddleware.ts b/themes/black/server/src/lib/IdentityCheckMiddleware.ts deleted file mode 100644 index e72ea4db1..000000000 --- a/themes/black/server/src/lib/IdentityCheckMiddleware.ts +++ /dev/null @@ -1,138 +0,0 @@ -import objectPath = require("object-path"); -import randomstring = require("randomstring"); -import BluebirdPromise = require("bluebird"); -import util = require("util"); -import Exceptions = require("./Exceptions"); -import fs = require("fs"); -import ejs = require("ejs"); -import { IUserDataStore } from "./storage/IUserDataStore"; -import Express = require("express"); -import ErrorReplies = require("./ErrorReplies"); -import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { ServerVariables } from "./ServerVariables"; -import { IdentityValidable } from "./IdentityValidable"; - -import Identity = require("../../types/Identity"); -import { IdentityValidationDocument } - from "./storage/IdentityValidationDocument"; - -const filePath = __dirname + "/../resources/email-template.ejs"; -const email_template = fs.readFileSync(filePath, "utf8"); - -function createAndSaveToken(userid: string, challenge: string, - userDataStore: IUserDataStore): BluebirdPromise { - - const five_minutes = 4 * 60 * 1000; - const token = randomstring.generate({ length: 64 }); - const that = this; - - return userDataStore.produceIdentityValidationToken(userid, token, challenge, - five_minutes) - .then(function () { - return BluebirdPromise.resolve(token); - }); -} - -function consumeToken(token: string, challenge: string, - userDataStore: IUserDataStore) - : BluebirdPromise { - return userDataStore.consumeIdentityValidationToken(token, challenge); -} - -export function register(app: Express.Application, - pre_validation_endpoint: string, - post_validation_endpoint: string, - handler: IdentityValidable, - vars: ServerVariables) { - - app.get(pre_validation_endpoint, - get_start_validation(handler, post_validation_endpoint, vars)); - app.get(post_validation_endpoint, - get_finish_validation(handler, vars)); -} - -function checkIdentityToken(req: Express.Request, identityToken: string) - : BluebirdPromise { - if (!identityToken) - return BluebirdPromise.reject( - new Exceptions.AccessDeniedError("No identity token provided")); - return BluebirdPromise.resolve(); -} - -export function get_finish_validation(handler: IdentityValidable, - vars: ServerVariables) - : Express.RequestHandler { - - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - - let authSession: AuthenticationSession; - const identityToken = objectPath.get( - req, "query.identity_token"); - vars.logger.debug(req, "Identity token provided is %s", identityToken); - - return checkIdentityToken(req, identityToken) - .then(() => { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - return handler.postValidationInit(req); - }) - .then(() => { - return consumeToken(identityToken, handler.challenge(), - vars.userDataStore); - }) - .then((doc: IdentityValidationDocument) => { - authSession.identity_check = { - challenge: handler.challenge(), - userid: doc.userId - }; - handler.postValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - }; -} - -export function get_start_validation(handler: IdentityValidable, - postValidationEndpoint: string, - vars: ServerVariables) - : Express.RequestHandler { - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - let identity: Identity.Identity; - - return handler.preValidationInit(req) - .then((id: Identity.Identity) => { - identity = id; - const email = identity.email; - const userid = identity.userid; - vars.logger.info(req, "Start identity validation of user \"%s\"", - userid); - - if (!(email && userid)) - return BluebirdPromise.reject(new Exceptions.IdentityError( - "Missing user id or email address")); - - return createAndSaveToken(userid, handler.challenge(), - vars.userDataStore); - }) - .then((token) => { - const host = req.get("Host"); - const link_url = util.format("https://%s%s?identity_token=%s", host, - postValidationEndpoint, token); - vars.logger.info(req, "Notification sent to user \"%s\"", - identity.userid); - return vars.notifier.notify(identity.email, handler.mailSubject(), - link_url); - }) - .then(() => { - handler.preValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(Exceptions.IdentityError, (err: Error) => { - handler.preValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - }; -} diff --git a/themes/black/server/src/lib/IdentityCheckPreValidationTemplate.ts b/themes/black/server/src/lib/IdentityCheckPreValidationTemplate.ts deleted file mode 100644 index 0161ce406..000000000 --- a/themes/black/server/src/lib/IdentityCheckPreValidationTemplate.ts +++ /dev/null @@ -1,3 +0,0 @@ - - -export const PRE_VALIDATION_TEMPLATE = "need-identity-validation"; \ No newline at end of file diff --git a/themes/black/server/src/lib/IdentityValidable.ts b/themes/black/server/src/lib/IdentityValidable.ts deleted file mode 100644 index 075580c9e..000000000 --- a/themes/black/server/src/lib/IdentityValidable.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Bluebird = require("bluebird"); -import Identity = require("../../types/Identity"); - -// IdentityValidator allows user to go through a identity validation process -// in two steps: -// - Request an operation to be performed (password reset, registration). -// - Confirm operation with email. - -export interface IdentityValidable { - challenge(): string; - preValidationInit(req: Express.Request): Bluebird; - postValidationInit(req: Express.Request): Bluebird; - - // Serves a page after identity check request - preValidationResponse(req: Express.Request, res: Express.Response): void; - // Serves the page if identity validated - postValidationResponse(req: Express.Request, res: Express.Response): void; - mailSubject(): string; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/IdentityValidableStub.spec.ts b/themes/black/server/src/lib/IdentityValidableStub.spec.ts deleted file mode 100644 index 20a977140..000000000 --- a/themes/black/server/src/lib/IdentityValidableStub.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ - -import Sinon = require("sinon"); -import { IdentityValidable } from "./IdentityValidable"; -import express = require("express"); -import Bluebird = require("bluebird"); -import { Identity } from "../../types/Identity"; - - -export class IdentityValidableStub implements IdentityValidable { - challengeStub: Sinon.SinonStub; - preValidationInitStub: Sinon.SinonStub; - postValidationInitStub: Sinon.SinonStub; - preValidationResponseStub: Sinon.SinonStub; - postValidationResponseStub: Sinon.SinonStub; - mailSubjectStub: Sinon.SinonStub; - - constructor() { - this.challengeStub = Sinon.stub(); - - this.preValidationInitStub = Sinon.stub(); - this.postValidationInitStub = Sinon.stub(); - - this.preValidationResponseStub = Sinon.stub(); - this.postValidationResponseStub = Sinon.stub(); - - this.mailSubjectStub = Sinon.stub(); - } - - challenge(): string { - return this.challengeStub(); - } - - preValidationInit(req: Express.Request): Bluebird { - return this.preValidationInitStub(req); - } - - postValidationInit(req: Express.Request): Bluebird { - return this.postValidationInitStub(req); - } - - preValidationResponse(req: Express.Request, res: Express.Response): void { - return this.preValidationResponseStub(req, res); - } - - postValidationResponse(req: Express.Request, res: Express.Response): void { - return this.postValidationResponseStub(req, res); - } - - mailSubject(): string { - return this.mailSubjectStub(); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/Server.spec.ts b/themes/black/server/src/lib/Server.spec.ts deleted file mode 100644 index 365163254..000000000 --- a/themes/black/server/src/lib/Server.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ - -import Assert = require("assert"); -import Sinon = require("sinon"); -import nedb = require("nedb"); -import express = require("express"); -import winston = require("winston"); -import speakeasy = require("speakeasy"); -import u2f = require("u2f"); -import session = require("express-session"); -import { Configuration } from "./configuration/schema/Configuration"; -import { GlobalDependencies } from "../../types/Dependencies"; -import Server from "./Server"; -import { LdapjsMock, LdapjsClientMock } from "./stubs/ldapjs.spec"; - - -describe("Server", function () { - let deps: GlobalDependencies; - let sessionMock: Sinon.SinonSpy; - let ldapjsMock: LdapjsMock; - - before(function () { - sessionMock = Sinon.spy(session); - ldapjsMock = new LdapjsMock(); - - deps = { - speakeasy: speakeasy, - u2f: u2f, - nedb: nedb, - winston: winston, - ldapjs: ldapjsMock as any, - session: sessionMock as any, - ConnectRedis: Sinon.spy(), - Redis: Sinon.spy() as any - }; - }); - - - it("should set cookie scope to domain set in the config", function () { - const config: Configuration = { - port: 8081, - session: { - domain: "example.com", - secret: "secret" - }, - authentication_backend: { - ldap: { - url: "http://ldap", - user: "user", - password: "password", - base_dn: "dc=example,dc=com" - }, - }, - notifier: { - email: { - username: "user@example.com", - password: "password", - sender: "test@authelia.com", - service: "gmail" - } - }, - regulation: { - max_retries: 3, - ban_time: 5 * 60, - find_time: 5 * 60 - }, - storage: { - local: { - in_memory: true - } - } - }; - - const server = new Server(deps); - server.start(config, deps) - .then(function () { - Assert(sessionMock.calledOnce); - Assert.equal(sessionMock.getCall(0).args[0].cookie.domain, "example.com"); - server.stop(); - }); - }); -}); diff --git a/themes/black/server/src/lib/Server.ts b/themes/black/server/src/lib/Server.ts deleted file mode 100644 index 4090f6294..000000000 --- a/themes/black/server/src/lib/Server.ts +++ /dev/null @@ -1,93 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import ObjectPath = require("object-path"); - -import { Configuration } from "./configuration/schema/Configuration"; -import { GlobalDependencies } from "../../types/Dependencies"; -import { UserDataStore } from "./storage/UserDataStore"; -import { ConfigurationParser } from "./configuration/ConfigurationParser"; -import { SessionConfigurationBuilder } from "./configuration/SessionConfigurationBuilder"; -import { GlobalLogger } from "./logging/GlobalLogger"; -import { RequestLogger } from "./logging/RequestLogger"; -import { ServerVariables } from "./ServerVariables"; -import { ServerVariablesInitializer } from "./ServerVariablesInitializer"; -import { Configurator } from "./web_server/Configurator"; - -import * as Express from "express"; -import * as Path from "path"; -import * as http from "http"; - -function clone(obj: any) { - return JSON.parse(JSON.stringify(obj)); -} - -export default class Server { - private httpServer: http.Server; - private globalLogger: GlobalLogger; - private requestLogger: RequestLogger; - - constructor(deps: GlobalDependencies) { - this.globalLogger = new GlobalLogger(deps.winston); - this.requestLogger = new RequestLogger(deps.winston); - } - - private displayConfigurations(configuration: Configuration) { - const displayableConfiguration: Configuration = clone(configuration); - const STARS = "*****"; - - if (displayableConfiguration.authentication_backend.ldap) { - displayableConfiguration.authentication_backend.ldap.password = STARS; - } - - displayableConfiguration.session.secret = STARS; - if (displayableConfiguration.notifier && displayableConfiguration.notifier.email) - displayableConfiguration.notifier.email.password = STARS; - if (displayableConfiguration.notifier && displayableConfiguration.notifier.smtp) - displayableConfiguration.notifier.smtp.password = STARS; - - this.globalLogger.debug("User configuration is %s", - JSON.stringify(displayableConfiguration, undefined, 2)); - } - - private setup(config: Configuration, app: Express.Application, deps: GlobalDependencies): BluebirdPromise { - const that = this; - return ServerVariablesInitializer.initialize( - config, this.globalLogger, this.requestLogger, deps) - .then(function (vars: ServerVariables) { - Configurator.configure(config, app, vars, deps); - return BluebirdPromise.resolve(); - }); - } - - private startServer(app: Express.Application, port: number) { - const that = this; - that.globalLogger.info("Starting Authelia..."); - return new BluebirdPromise((resolve, reject) => { - this.httpServer = app.listen(port, function (err: string) { - that.globalLogger.info("Listening on port %d...", port); - resolve(); - }); - }); - } - - start(configuration: Configuration, deps: GlobalDependencies) - : BluebirdPromise { - const that = this; - const app = Express(); - - const appConfiguration = ConfigurationParser.parse(configuration); - - // by default the level of logs is info - deps.winston.level = appConfiguration.logs_level; - this.displayConfigurations(appConfiguration); - - return this.setup(appConfiguration, app, deps) - .then(function () { - return that.startServer(app, appConfiguration.port); - }); - } - - stop() { - this.httpServer.close(); - } -} - diff --git a/themes/black/server/src/lib/ServerVariables.ts b/themes/black/server/src/lib/ServerVariables.ts deleted file mode 100644 index cd3dd6dc8..000000000 --- a/themes/black/server/src/lib/ServerVariables.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { IRequestLogger } from "./logging/IRequestLogger"; -import { ITotpHandler } from "./authentication/totp/ITotpHandler"; -import { IU2fHandler } from "./authentication/u2f/IU2fHandler"; -import { IUserDataStore } from "./storage/IUserDataStore"; -import { INotifier } from "./notifiers/INotifier"; -import { IRegulator } from "./regulation/IRegulator"; -import { Configuration } from "./configuration/schema/Configuration"; -import { IAuthorizer } from "./authorization/IAuthorizer"; -import { IUsersDatabase } from "./authentication/backends/IUsersDatabase"; - -export interface ServerVariables { - logger: IRequestLogger; - usersDatabase: IUsersDatabase; - totpHandler: ITotpHandler; - u2f: IU2fHandler; - userDataStore: IUserDataStore; - notifier: INotifier; - regulator: IRegulator; - config: Configuration; - authorizer: IAuthorizer; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/ServerVariablesInitializer.ts b/themes/black/server/src/lib/ServerVariablesInitializer.ts deleted file mode 100644 index df79238cc..000000000 --- a/themes/black/server/src/lib/ServerVariablesInitializer.ts +++ /dev/null @@ -1,116 +0,0 @@ - -import winston = require("winston"); -import BluebirdPromise = require("bluebird"); -import U2F = require("u2f"); -import Nodemailer = require("nodemailer"); - -import { IRequestLogger } from "./logging/IRequestLogger"; -import { RequestLogger } from "./logging/RequestLogger"; - -import { TotpHandler } from "./authentication/totp/TotpHandler"; -import { ITotpHandler } from "./authentication/totp/ITotpHandler"; -import { NotifierFactory } from "./notifiers/NotifierFactory"; -import { MailSenderBuilder } from "./notifiers/MailSenderBuilder"; -import { LdapUsersDatabase } from "./authentication/backends/ldap/LdapUsersDatabase"; -import { ConnectorFactory } from "./authentication/backends/ldap/connector/ConnectorFactory"; - -import { IUserDataStore } from "./storage/IUserDataStore"; -import { UserDataStore } from "./storage/UserDataStore"; -import { INotifier } from "./notifiers/INotifier"; -import { Regulator } from "./regulation/Regulator"; -import { IRegulator } from "./regulation/IRegulator"; -import Configuration = require("./configuration/schema/Configuration"); -import { CollectionFactoryFactory } from "./storage/CollectionFactoryFactory"; -import { ICollectionFactory } from "./storage/ICollectionFactory"; -import { MongoCollectionFactory } from "./storage/mongo/MongoCollectionFactory"; -import { IMongoClient } from "./connectors/mongo/IMongoClient"; - -import { GlobalDependencies } from "../../types/Dependencies"; -import { ServerVariables } from "./ServerVariables"; -import { MongoClient } from "./connectors/mongo/MongoClient"; -import { IGlobalLogger } from "./logging/IGlobalLogger"; -import { SessionFactory } from "./authentication/backends/ldap/SessionFactory"; -import { IUsersDatabase } from "./authentication/backends/IUsersDatabase"; -import { FileUsersDatabase } from "./authentication/backends/file/FileUsersDatabase"; -import { Authorizer } from "./authorization/Authorizer"; - -class UserDataStoreFactory { - static create(config: Configuration.Configuration, globalLogger: IGlobalLogger): BluebirdPromise { - if (config.storage.local) { - const nedbOptions: Nedb.DataStoreOptions = { - filename: config.storage.local.path, - inMemoryOnly: config.storage.local.in_memory - }; - const collectionFactory = CollectionFactoryFactory.createNedb(nedbOptions); - return BluebirdPromise.resolve(new UserDataStore(collectionFactory)); - } - else if (config.storage.mongo) { - const mongoClient = new MongoClient( - config.storage.mongo, - globalLogger); - const collectionFactory = CollectionFactoryFactory.createMongo(mongoClient); - return BluebirdPromise.resolve(new UserDataStore(collectionFactory)); - } - - return BluebirdPromise.reject(new Error("Storage backend incorrectly configured.")); - } -} - -export class ServerVariablesInitializer { - static createUsersDatabase( - config: Configuration.Configuration, - deps: GlobalDependencies) - : IUsersDatabase { - - if (config.authentication_backend.ldap) { - const ldapConfig = config.authentication_backend.ldap; - return new LdapUsersDatabase( - new SessionFactory( - ldapConfig, - new ConnectorFactory(ldapConfig, deps.ldapjs), - deps.winston - ), - ldapConfig - ); - } - else if (config.authentication_backend.file) { - return new FileUsersDatabase(config.authentication_backend.file); - } - } - - static initialize( - config: Configuration.Configuration, - globalLogger: IGlobalLogger, - requestLogger: IRequestLogger, - deps: GlobalDependencies) - : BluebirdPromise { - - const mailSenderBuilder = - new MailSenderBuilder(Nodemailer); - const notifier = NotifierFactory.build( - config.notifier, mailSenderBuilder); - const authorizer = new Authorizer(config.access_control, deps.winston); - const totpHandler = new TotpHandler(deps.speakeasy); - const usersDatabase = this.createUsersDatabase( - config, deps); - - return UserDataStoreFactory.create(config, globalLogger) - .then(function (userDataStore: UserDataStore) { - const regulator = new Regulator(userDataStore, config.regulation.max_retries, - config.regulation.find_time, config.regulation.ban_time); - - const variables: ServerVariables = { - authorizer: authorizer, - config: config, - usersDatabase: usersDatabase, - logger: requestLogger, - notifier: notifier, - regulator: regulator, - totpHandler: totpHandler, - u2f: deps.u2f, - userDataStore: userDataStore - }; - return BluebirdPromise.resolve(variables); - }); - } -} diff --git a/themes/black/server/src/lib/ServerVariablesMockBuilder.spec.ts b/themes/black/server/src/lib/ServerVariablesMockBuilder.spec.ts deleted file mode 100644 index 7874702a0..000000000 --- a/themes/black/server/src/lib/ServerVariablesMockBuilder.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { ServerVariables } from "./ServerVariables"; - -import { Configuration } from "./configuration/schema/Configuration"; -import { IUsersDatabaseStub } from "./authentication/backends/IUsersDatabaseStub.spec"; -import { AuthorizerStub } from "./authorization/AuthorizerStub.spec"; -import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec"; -import { NotifierStub } from "./notifiers/NotifierStub.spec"; -import { RegulatorStub } from "./regulation/RegulatorStub.spec"; -import { TotpHandlerStub } from "./authentication/totp/TotpHandlerStub.spec"; -import { UserDataStoreStub } from "./storage/UserDataStoreStub.spec"; -import { U2fHandlerStub } from "./authentication/u2f/U2fHandlerStub.spec"; - -export interface ServerVariablesMock { - authorizer: AuthorizerStub; - config: Configuration; - usersDatabase: IUsersDatabaseStub; - logger: RequestLoggerStub; - notifier: NotifierStub; - regulator: RegulatorStub; - totpHandler: TotpHandlerStub; - userDataStore: UserDataStoreStub; - u2f: U2fHandlerStub; -} - -export class ServerVariablesMockBuilder { - static build(enableLogging?: boolean): { variables: ServerVariables, mocks: ServerVariablesMock} { - const mocks: ServerVariablesMock = { - authorizer: new AuthorizerStub(), - config: { - access_control: {}, - totp: { - issuer: "authelia.com" - }, - authentication_backend: { - ldap: { - url: "ldap://ldap", - base_dn: "dc=example,dc=com", - user: "user", - password: "password", - mail_attribute: "mail", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn" - }, - }, - logs_level: "debug", - notifier: {}, - port: 8080, - regulation: { - ban_time: 50, - find_time: 50, - max_retries: 3 - }, - session: { - secret: "my_secret", - domain: "mydomain" - }, - storage: {} - }, - usersDatabase: new IUsersDatabaseStub(), - logger: new RequestLoggerStub(enableLogging), - notifier: new NotifierStub(), - regulator: new RegulatorStub(), - totpHandler: new TotpHandlerStub(), - userDataStore: new UserDataStoreStub(), - u2f: new U2fHandlerStub() - }; - const vars: ServerVariables = { - authorizer: mocks.authorizer, - config: mocks.config, - usersDatabase: mocks.usersDatabase, - logger: mocks.logger, - notifier: mocks.notifier, - regulator: mocks.regulator, - totpHandler: mocks.totpHandler, - userDataStore: mocks.userDataStore, - u2f: mocks.u2f - }; - - return { - variables: vars, - mocks: mocks - }; - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/Level.ts b/themes/black/server/src/lib/authentication/Level.ts deleted file mode 100644 index 57b6a2346..000000000 --- a/themes/black/server/src/lib/authentication/Level.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum Level { - NOT_AUTHENTICATED = 0, - ONE_FACTOR = 1, - TWO_FACTOR = 2 -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/GroupsAndEmails.ts b/themes/black/server/src/lib/authentication/backends/GroupsAndEmails.ts deleted file mode 100644 index 3434ba66f..000000000 --- a/themes/black/server/src/lib/authentication/backends/GroupsAndEmails.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface GroupsAndEmails { - groups: string[]; - emails: string[]; -} diff --git a/themes/black/server/src/lib/authentication/backends/IUsersDatabase.ts b/themes/black/server/src/lib/authentication/backends/IUsersDatabase.ts deleted file mode 100644 index d7fa13b7a..000000000 --- a/themes/black/server/src/lib/authentication/backends/IUsersDatabase.ts +++ /dev/null @@ -1,10 +0,0 @@ -import Bluebird = require("bluebird"); - -import { GroupsAndEmails } from "./GroupsAndEmails"; - -export interface IUsersDatabase { - checkUserPassword(username: string, password: string): Bluebird; - getEmails(username: string): Bluebird; - getGroups(username: string): Bluebird; - updatePassword(username: string, newPassword: string): Bluebird; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts b/themes/black/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts deleted file mode 100644 index 19341a5dd..000000000 --- a/themes/black/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); - -import { IUsersDatabase } from "./IUsersDatabase"; -import { GroupsAndEmails } from "./GroupsAndEmails"; - -export class IUsersDatabaseStub implements IUsersDatabase { - checkUserPasswordStub: Sinon.SinonStub; - getEmailsStub: Sinon.SinonStub; - getGroupsStub: Sinon.SinonStub; - updatePasswordStub: Sinon.SinonStub; - - constructor() { - this.checkUserPasswordStub = Sinon.stub(); - this.getEmailsStub = Sinon.stub(); - this.getGroupsStub = Sinon.stub(); - this.updatePasswordStub = Sinon.stub(); - } - - checkUserPassword(username: string, password: string): Bluebird { - return this.checkUserPasswordStub(username, password); - } - - getEmails(username: string): Bluebird { - return this.getEmailsStub(username); - } - - getGroups(username: string): Bluebird { - return this.getGroupsStub(username); - } - - updatePassword(username: string, newPassword: string): Bluebird { - return this.updatePasswordStub(username, newPassword); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts b/themes/black/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts deleted file mode 100644 index a258a78f7..000000000 --- a/themes/black/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts +++ /dev/null @@ -1,224 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); -import Fs = require("fs"); -import Sinon = require("sinon"); -import Tmp = require("tmp"); - -import { FileUsersDatabase } from "./FileUsersDatabase"; -import { FileUsersDatabaseConfiguration } from "../../../configuration/schema/FileUsersDatabaseConfiguration"; -import { HashGenerator } from "../../../utils/HashGenerator"; - -const GOOD_DATABASE = ` -users: - john: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev - - harry: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - emails: harry.potter@authelia.com - groups: [] -`; - -const BAD_HASH = ` -users: - john: - password: "{CRYPT}$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev -`; - -const NO_PASSWORD_DATABASE = ` -users: - john: - email: john.doe@authelia.com - groups: - - admins - - dev -`; - -const NO_EMAIL_DATABASE = ` -users: - john: - password: "{CRYPT}$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - groups: - - admins - - dev -`; - -const SINGLE_USER_DATABASE = ` -users: - john: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev -` - -function createTmpFileFrom(yaml: string) { - const tmpFileAsync = Bluebird.promisify(Tmp.file); - return tmpFileAsync() - .then((path: string) => { - Fs.writeFileSync(path, yaml, "utf-8"); - return Bluebird.resolve(path); - }); -} - -describe("authentication/backends/file/FileUsersDatabase", function() { - let configuration: FileUsersDatabaseConfiguration; - - describe("checkUserPassword", () => { - describe("good config", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then((groupsAndEmails) => { - Assert.deepEqual(groupsAndEmails.groups, ["admins", "dev"]); - Assert.deepEqual(groupsAndEmails.emails, ["john.doe@authelia.com"]); - }); - }); - - it("should fail when password is wrong", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "bad_password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("no_user", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("bad hash", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail when hash is wrong", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("no password", () => { - beforeEach(() => { - return createTmpFileFrom(NO_PASSWORD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - }); - - describe("getEmails", () => { - describe("good config", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("john") - .then((emails) => { - Assert.deepEqual(emails, ["john.doe@authelia.com"]); - }); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("no_user") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("no email provided", () => { - beforeEach(() => { - return createTmpFileFrom(NO_EMAIL_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("john") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - }); - - describe("updatePassword", () => { - beforeEach(() => { - return createTmpFileFrom(SINGLE_USER_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - const NEW_HASH = "{CRYPT}$6$rounds=500000$Qw6MhgADvLyYMEq9$ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const stub = Sinon.stub(HashGenerator, "ssha512").returns(Bluebird.resolve(NEW_HASH)); - return usersDatabase.updatePassword("john", "mypassword") - .then(() => { - const content = Fs.readFileSync(configuration.path, "utf-8"); - const matches = content.match(/password: '(.+)'/); - Assert.equal(matches[1], NEW_HASH); - }) - .finally(() => stub.restore()); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.updatePassword("bad_user", "mypassword") - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => Bluebird.resolve()); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/file/FileUsersDatabase.ts b/themes/black/server/src/lib/authentication/backends/file/FileUsersDatabase.ts deleted file mode 100644 index d34dde21e..000000000 --- a/themes/black/server/src/lib/authentication/backends/file/FileUsersDatabase.ts +++ /dev/null @@ -1,182 +0,0 @@ -import Bluebird = require("bluebird"); -import Fs = require("fs"); -import Yaml = require("yamljs"); - -import { FileUsersDatabaseConfiguration } - from "../../../configuration/schema/FileUsersDatabaseConfiguration"; -import { GroupsAndEmails } from "../GroupsAndEmails"; -import { IUsersDatabase } from "../IUsersDatabase"; -import { HashGenerator } from "../../../utils/HashGenerator"; -import { ReadWriteQueue } from "./ReadWriteQueue"; - -const loadAsync = Bluebird.promisify(Yaml.load); - -export class FileUsersDatabase implements IUsersDatabase { - private configuration: FileUsersDatabaseConfiguration; - private queue: ReadWriteQueue; - - constructor(configuration: FileUsersDatabaseConfiguration) { - this.configuration = configuration; - this.queue = new ReadWriteQueue(this.configuration.path); - } - - /** - * Read database from file. - * It enqueues the read task so that it is scheduled - * between other reads and writes. - */ - private readDatabase(): Bluebird { - return new Bluebird((resolve, reject) => { - this.queue.read((err: Error, data: string) => { - if (err) { - reject(err); - return; - } - resolve(data); - this.queue.next(); - }); - }) - .then((content) => { - const database = Yaml.parse(content); - if (!database) { - return Bluebird.reject(new Error("Unable to parse YAML file.")); - } - return Bluebird.resolve(database); - }); - } - - /** - * Checks the user exists in the database. - */ - private checkUserExists( - database: any, - username: string) - : Bluebird { - if (!(username in database.users)) { - return Bluebird.reject( - new Error(`User ${username} does not exist in database.`)); - } - return Bluebird.resolve(); - } - - /** - * Check the password of a given user. - */ - private checkPassword( - database: any, - username: string, - password: string) - : Bluebird { - const storedHash: string = database.users[username].password; - const matches = storedHash.match(/rounds=([0-9]+)\$([a-zA-z0-9]+)\$/); - if (!(matches && matches.length == 3)) { - return Bluebird.reject(new Error("Unable to detect the hash salt and rounds. " + - "Make sure the password is hashed with SSHA512.")); - } - - const rounds: number = parseInt(matches[1]); - const salt = matches[2]; - - return HashGenerator.ssha512(password, rounds, salt) - .then((hash: string) => { - if (hash !== storedHash) { - return Bluebird.reject(new Error("Wrong username/password.")); - } - return Bluebird.resolve(); - }); - } - - /** - * Retrieve email addresses of a given user. - */ - private retrieveEmails( - database: any, - username: string) - : Bluebird { - if (!("email" in database.users[username])) { - return Bluebird.reject( - new Error(`User ${username} has no email address.`)); - } - return Bluebird.resolve( - [database.users[username].email]); - } - - private retrieveGroups( - database: any, - username: string) - : Bluebird { - if (!("groups" in database.users[username])) { - return Bluebird.resolve([]); - } - return Bluebird.resolve( - database.users[username].groups); - } - - private replacePassword( - database: any, - username: string, - newPassword: string) - : Bluebird { - const that = this; - return HashGenerator.ssha512(newPassword) - .then((hash) => { - database.users[username].password = hash; - const str = Yaml.stringify(database, 4, 2); - return Bluebird.resolve(str); - }) - .then((content: string) => { - return new Bluebird((resolve, reject) => { - that.queue.write(content, (err) => { - if (err) { - return reject(err); - } - resolve(); - that.queue.next(); - }); - }); - }); - } - - checkUserPassword( - username: string, - password: string) - : Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.checkPassword(database, username, password)) - .then(() => { - return Bluebird.join( - this.retrieveEmails(database, username), - this.retrieveGroups(database, username) - ).spread((emails: string[], groups: string[]) => { - return { emails: emails, groups: groups }; - }); - }); - }); - } - - getEmails(username: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.retrieveEmails(database, username)); - }); - } - - getGroups(username: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.retrieveGroups(database, username)); - }); - } - - updatePassword(username: string, newPassword: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.replacePassword(database, username, newPassword)); - }); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/file/ReadWriteQueue.ts b/themes/black/server/src/lib/authentication/backends/file/ReadWriteQueue.ts deleted file mode 100644 index 957ddaec5..000000000 --- a/themes/black/server/src/lib/authentication/backends/file/ReadWriteQueue.ts +++ /dev/null @@ -1,60 +0,0 @@ -import Fs = require("fs"); - -type Callback = (err: Error, data?: string) => void; -type ContentAndCallback = [string, Callback] | [string, string, Callback]; - -/** - * WriteQueue is a queue synchronizing writes to a file. - * - * Example of use: - * - * queue.add(mycontent, (err) => { - * // do whatever you want here. - * queue.next(); - * }) - */ -export class ReadWriteQueue { - private filePath: string; - private queue: ContentAndCallback[]; - - constructor (filePath: string) { - this.queue = []; - this.filePath = filePath; - } - - next () { - if (this.queue.length === 0) - return; - - const task = this.queue[0]; - - if (task[0] == "write") { - Fs.writeFile(this.filePath, task[1], "utf-8", (err) => { - this.queue.shift(); - const cb = task[2] as Callback; - cb(err); - }); - } - else if (task[0] == "read") { - Fs.readFile(this.filePath, { encoding: "utf-8"} , (err, data) => { - this.queue.shift(); - const cb = task[1] as Callback; - cb(err, data); - }); - } - } - - write (content: string, cb: Callback) { - this.queue.push(["write", content, cb]); - if (this.queue.length === 1) { - this.next(); - } - } - - read (cb: Callback) { - this.queue.push(["read", cb]); - if (this.queue.length === 1) { - this.next(); - } - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/ISession.ts b/themes/black/server/src/lib/authentication/backends/ldap/ISession.ts deleted file mode 100644 index da2c74433..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/ISession.ts +++ /dev/null @@ -1,12 +0,0 @@ - -import BluebirdPromise = require("bluebird"); - -export interface ISession { - open(): BluebirdPromise; - close(): BluebirdPromise; - - searchUserDn(username: string): BluebirdPromise; - searchEmails(username: string): BluebirdPromise; - searchGroups(username: string): BluebirdPromise; - modifyPassword(username: string, newPassword: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/ISessionFactory.ts b/themes/black/server/src/lib/authentication/backends/ldap/ISessionFactory.ts deleted file mode 100644 index 014d1eea5..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/ISessionFactory.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import { ISession } from "./ISession"; - -export interface ISessionFactory { - create(userDN: string, password: string): ISession; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts b/themes/black/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts deleted file mode 100644 index f4a6e6306..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts +++ /dev/null @@ -1,386 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); - -import { LdapUsersDatabase } from "./LdapUsersDatabase"; - -import { SessionFactoryStub } from "./SessionFactoryStub.spec"; -import { SessionStub } from "./SessionStub.spec"; - -const ADMIN_USER_DN = "cn=admin,dc=example,dc=com"; -const ADMIN_PASSWORD = "password"; - -describe("ldap/connector/LdapUsersDatabase", function() { - let sessionFactory: SessionFactoryStub; - let usersDatabase: LdapUsersDatabase; - - const USERNAME = "user"; - const PASSWORD = "pass"; - const NEW_PASSWORD = "pass2"; - - const LDAP_CONFIG = { - url: "http://localhost:324", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={0}", - mail_attribute: "mail", - group_name_attribute: "cn", - user: ADMIN_USER_DN, - password: ADMIN_PASSWORD - }; - - beforeEach(function() { - sessionFactory = new SessionFactoryStub(); - usersDatabase = new LdapUsersDatabase(sessionFactory, LDAP_CONFIG); - }) - - describe("checkUserPassword", function() { - it("should return groups and emails when user/password matches", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME).returns(Bluebird.resolve(emails)); - adminSession.searchGroupsStub.withArgs(USERNAME).returns(Bluebird.resolve(groups)); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => { - Assert.deepEqual(groupsAndEmails.groups, groups); - Assert.deepEqual(groupsAndEmails.emails, emails); - }) - }); - - it("should fail when username/password is wrong", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - - userSession.openStub.returns(Bluebird.reject(new Error("Failed binding"))); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when admin binding fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.reject(new Error("Failed binding"))); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.notCalled); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when search for user dn fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.reject(new Error("Failed searching user dn"))); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.notCalled); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when groups retrieval fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME) - .returns(Bluebird.resolve(emails)); - adminSession.searchGroupsStub.withArgs(USERNAME) - .returns(Bluebird.reject(new Error("Failed retrieving groups"))); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - }) - }); - - it("should fail when emails retrieval fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME) - .returns(Bluebird.reject(new Error("Emails retrieval failed"))); - adminSession.searchGroupsStub.withArgs(USERNAME) - .returns(Bluebird.resolve(groups)); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - }) - }); - }); - - describe("getEmails", function() { - it("should succefully retrieves email", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.resolve(emails)); - - return usersDatabase.getEmails(USERNAME) - .then((foundEmails) => { - Assert(session.closeStub.called); - Assert.deepEqual(foundEmails, emails); - }) - }); - - it("should fail when binding fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbinding fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.resolve(emails)); - session.closeStub.returns(Bluebird.reject(new Error("Unbinding failed"))); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when search fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.reject(new Error("Search failed"))); - session.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - }); - - - describe("getGroups", function() { - it("should succefully retrieves groups", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.resolve(groups)); - - return usersDatabase.getGroups(USERNAME) - .then((foundGroups) => { - Assert(session.closeStub.called); - Assert.deepEqual(foundGroups, groups); - }) - }); - - it("should fail when binding fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbinding fails", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.resolve(groups)); - session.closeStub.returns(Bluebird.reject(new Error("Unbinding failed"))); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when search fails", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.reject(new Error("Search failed"))); - session.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - }); - - - describe("updatePassword", function() { - it("should successfully update password", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => { - Assert(session.modifyPasswordStub.calledWith(USERNAME, NEW_PASSWORD)); - Assert(session.closeStub.called); - }) - }); - - it("should fail when binding fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when update fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.reject(new Error("Update failed"))); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbind fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.reject(new Error("Unbind failed"))); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts b/themes/black/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts deleted file mode 100644 index edda62ec6..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts +++ /dev/null @@ -1,107 +0,0 @@ -import Bluebird = require("bluebird"); -import { IUsersDatabase } from "../IUsersDatabase"; -import { ISessionFactory } from "./ISessionFactory"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { ISession } from "./ISession"; -import { GroupsAndEmails } from "../GroupsAndEmails"; -import Exceptions = require("../../../Exceptions"); - -type SessionCallback = (session: ISession) => Bluebird; - -export class LdapUsersDatabase implements IUsersDatabase { - private sessionFactory: ISessionFactory; - private configuration: LdapConfiguration; - - constructor( - sessionFactory: ISessionFactory, - configuration: LdapConfiguration) { - this.sessionFactory = sessionFactory; - this.configuration = configuration; - } - - private withSession( - username: string, - password: string, - cb: SessionCallback): Bluebird { - const session = this.sessionFactory.create(username, password); - return session.open() - .then(() => cb(session)) - .finally(() => session.close()); - } - - checkUserPassword(username: string, password: string): Bluebird { - const that = this; - function verifyUserPassword(userDN: string) { - return that.withSession( - userDN, - password, - (session) => Bluebird.resolve() - ); - } - - function getInfo(session: ISession) { - return Bluebird.join( - session.searchGroups(username), - session.searchEmails(username) - ) - .spread((groups: string[], emails: string[]) => { - return { groups: groups, emails: emails }; - }); - } - - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchUserDn(username) - .then(verifyUserPassword) - .then(() => getInfo(session)); - }) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError(err.message))); - } - - getEmails(username: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchEmails(username); - } - ) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError("Failed during email retrieval: " + err.message)) - ); - } - - getGroups(username: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchGroups(username); - } - ) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError("Failed during email retrieval: " + err.message)) - ); - } - - updatePassword(username: string, newPassword: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.modifyPassword(username, newPassword); - } - ) - .catch(function (err: Error) { - return Bluebird.reject( - new Exceptions.LdapError( - "Error while updating password: " + err.message)); - }); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts b/themes/black/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts deleted file mode 100644 index 9dedfcb7c..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { SessionStub } from "./SessionStub.spec"; -import { SafeSession } from "./SafeSession"; - -describe("ldap/SanitizedClient", function () { - let client: SafeSession; - - beforeEach(function () { - const clientStub = new SessionStub(); - clientStub.searchUserDnStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.searchGroupsStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.searchEmailsStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.modifyPasswordStub.onCall(0).returns(BluebirdPromise.resolve()); - client = new SafeSession(clientStub); - }); - - describe("special chars are used", function () { - it("should fail when special chars are used in searchUserDn", function () { - // potential ldap injection"; - return client.searchUserDn("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in searchGroups", function () { - // potential ldap injection"; - return client.searchGroups("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in searchEmails", function () { - // potential ldap injection"; - return client.searchEmails("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in modifyPassword", function () { - // potential ldap injection"; - return client.modifyPassword("cn=dummy_user,ou=groupgs", "abc") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - }); - - describe("no special chars are used", function() { - it("should succeed when no special chars are used in searchUserDn", function () { - return client.searchUserDn("dummy_user"); - }); - - it("should succeed when no special chars are used in searchGroups", function () { - return client.searchGroups("dummy_user"); - }); - - it("should succeed when no special chars are used in searchEmails", function () { - return client.searchEmails("dummy_user"); - }); - - it("should succeed when no special chars are used in modifyPassword", function () { - return client.modifyPassword("dummy_user", "abc"); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/SafeSession.ts b/themes/black/server/src/lib/authentication/backends/ldap/SafeSession.ts deleted file mode 100644 index 572209066..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/SafeSession.ts +++ /dev/null @@ -1,62 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ISession } from "./ISession"; -import { Sanitizer } from "./Sanitizer"; - -const SPECIAL_CHAR_USED_MESSAGE = "Special character used in LDAP query."; - - -export class SafeSession implements ISession { - private sesion: ISession; - - constructor(sesion: ISession) { - this.sesion = sesion; - } - - open(): BluebirdPromise { - return this.sesion.open(); - } - - close(): BluebirdPromise { - return this.sesion.close(); - } - - searchGroups(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchGroups(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - searchUserDn(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchUserDn(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - searchEmails(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchEmails(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - modifyPassword(username: string, newPassword: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.modifyPassword(sanitizedUsername, newPassword); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } -} diff --git a/themes/black/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts b/themes/black/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts deleted file mode 100644 index 9dd33fed4..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import Assert = require("assert"); -import { Sanitizer } from "./Sanitizer"; - -describe("ldap/InputsSanitizer", function () { - it("should fail when special characters are used", function () { - Assert.throws(() => { Sanitizer.sanitize("ab,c"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a\\bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a'bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a#bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a+bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a { Sanitizer.sanitize("a>bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a;bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a\"bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a=bc"); }, Error); - }); - - it("should return original string", function () { - Assert.equal(Sanitizer.sanitize("abcdef"), "abcdef"); - }); - - it("should trim", function () { - Assert.throws(() => { Sanitizer.sanitize(" abc "); }, Error); - }); -}); diff --git a/themes/black/server/src/lib/authentication/backends/ldap/Sanitizer.ts b/themes/black/server/src/lib/authentication/backends/ldap/Sanitizer.ts deleted file mode 100644 index be74132ad..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/Sanitizer.ts +++ /dev/null @@ -1,25 +0,0 @@ - -// returns true for 1 or more matches, where 'a' is an array and 'b' is a search string or an array of multiple search strings -function contains(a: string, character: string) { - // string match - return a.indexOf(character) > -1; -} - -function containsOneOf(s: string, characters: string[]) { - return characters - .map((character: string) => { return contains(s, character); }) - .reduce((acc: boolean, current: boolean) => { return acc || current; }, false); -} - -export class Sanitizer { - static sanitize(input: string): string { - const forbiddenChars = [",", "\\", "'", "#", "+", "<", ">", ";", "\"", "="]; - if (containsOneOf(input, forbiddenChars)) - throw new Error("Input containing unsafe characters."); - - if (input != input.trim()) - throw new Error("Input has unexpected spaces."); - - return input; - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/Session.spec.ts b/themes/black/server/src/lib/authentication/backends/ldap/Session.spec.ts deleted file mode 100644 index d55f6a805..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/Session.spec.ts +++ /dev/null @@ -1,127 +0,0 @@ - -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Session } from "./Session"; -import { ConnectorFactoryStub } from "./connector/ConnectorFactoryStub.spec"; -import { ConnectorStub } from "./connector/ConnectorStub.spec"; - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import Winston = require("winston"); - -describe("ldap/Session", function () { - const USERNAME = "username"; - const ADMIN_USER_DN = "cn=admin,dc=example,dc=com"; - const ADMIN_PASSWORD = "password"; - - it("should replace {0} by username when searching for groups in LDAP", function () { - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member=cn={0},ou=users,dc=example,dc=com", - group_name_attribute: "cn", - mail_attribute: "mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const connectorStub = new ConnectorStub(); - connectorStub.searchAsyncStub.returns(BluebirdPromise.resolve([{ - cn: "group1" - }])); - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, connectorStub, Winston); - - return client.searchGroups("user1") - .then(function () { - Assert.equal(connectorStub.searchAsyncStub.getCall(0).args[1].filter, - "member=cn=user1,ou=users,dc=example,dc=com"); - }); - }); - - it("should replace {dn} by user DN when searching for groups in LDAP", function () { - const USER_DN = "cn=user1,ou=users,dc=example,dc=com"; - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn", - mail_attribute: "mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const ldapClient = new ConnectorStub(); - - // Retrieve user DN - ldapClient.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: "cn=user1" - }).returns(BluebirdPromise.resolve([{ - dn: USER_DN - }])); - - // Retrieve groups - ldapClient.searchAsyncStub.withArgs("ou=groups,dc=example,dc=com", { - scope: "sub", - attributes: ["cn"], - filter: "member=" + USER_DN - }).returns(BluebirdPromise.resolve([{ - cn: "group1" - }])); - - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, ldapClient, Winston); - - return client.searchGroups("user1") - .then(function (groups: string[]) { - Assert.deepEqual(groups, ["group1"]); - }); - }); - - it("should retrieve mail from custom attribute", function () { - const USER_DN = "cn=user1,ou=users,dc=example,dc=com"; - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn", - mail_attribute: "custom_mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const connector = new ConnectorStub(); - // Retrieve user DN - connector.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: "cn=user1" - }).returns(BluebirdPromise.resolve([{ - dn: USER_DN - }])); - - // Retrieve email - connector.searchAsyncStub.withArgs("cn=user1,ou=users,dc=example,dc=com", { - scope: "base", - sizeLimit: 1, - attributes: ["custom_mail"], - }).returns(BluebirdPromise.resolve([{ - custom_mail: "user1@example.com" - }])); - - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, connector, Winston); - - return client.searchEmails("user1") - .then(function (emails: string[]) { - Assert.deepEqual(emails, ["user1@example.com"]); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/Session.ts b/themes/black/server/src/lib/authentication/backends/ldap/Session.ts deleted file mode 100644 index e0284b3c4..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/Session.ts +++ /dev/null @@ -1,156 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import exceptions = require("../../../Exceptions"); -import { EventEmitter } from "events"; -import { ISession } from "./ISession"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Winston } from "../../../../../types/Dependencies"; -import Util = require("util"); -import { HashGenerator } from "../../../utils/HashGenerator"; -import { IConnector } from "./connector/IConnector"; - -export class Session implements ISession { - private userDN: string; - private password: string; - private connector: IConnector; - private logger: Winston; - private options: LdapConfiguration; - - private groupsSearchBase: string; - private usersSearchBase: string; - - constructor(userDN: string, password: string, options: LdapConfiguration, - connector: IConnector, logger: Winston) { - this.options = options; - this.logger = logger; - this.userDN = userDN; - this.password = password; - this.connector = connector; - - this.groupsSearchBase = (this.options.additional_groups_dn) - ? Util.format("%s,%s", this.options.additional_groups_dn, this.options.base_dn) - : this.options.base_dn; - - this.usersSearchBase = (this.options.additional_users_dn) - ? Util.format("%s,%s", this.options.additional_users_dn, this.options.base_dn) - : this.options.base_dn; - } - - open(): BluebirdPromise { - this.logger.debug("LDAP: Bind user '%s'", this.userDN); - return this.connector.bindAsync(this.userDN, this.password) - .error(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapBindError(err.message)); - }); - } - - close(): BluebirdPromise { - this.logger.debug("LDAP: Unbind user '%s'", this.userDN); - return this.connector.unbindAsync() - .error(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapBindError(err.message)); - }); - } - - private createGroupsFilter(userGroupsFilter: string, username: string): BluebirdPromise { - if (userGroupsFilter.indexOf("{0}") > 0) { - return BluebirdPromise.resolve(userGroupsFilter.replace("{0}", username)); - } - else if (userGroupsFilter.indexOf("{dn}") > 0) { - return this.searchUserDn(username) - .then(function (userDN: string) { - return BluebirdPromise.resolve(userGroupsFilter.replace("{dn}", userDN)); - }); - } - return BluebirdPromise.resolve(userGroupsFilter); - } - - searchGroups(username: string): BluebirdPromise { - const that = this; - return this.createGroupsFilter(this.options.groups_filter, username) - .then(function (groupsFilter: string) { - that.logger.debug("Computed groups filter is %s", groupsFilter); - const query = { - scope: "sub", - attributes: [that.options.group_name_attribute], - filter: groupsFilter - }; - return that.connector.searchAsync(that.groupsSearchBase, query); - }) - .then(function (docs: { cn: string }[]) { - const groups = docs.map((doc: any) => { return doc.cn; }); - that.logger.debug("LDAP: groups of user %s are [%s]", username, groups.join(",")); - return BluebirdPromise.resolve(groups); - }); - } - - searchUserDn(username: string): BluebirdPromise { - const that = this; - const filter = this.options.users_filter.replace("{0}", username); - this.logger.debug("Computed users filter is %s", filter); - const query = { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: filter - }; - - that.logger.debug("LDAP: searching for user dn of %s", username); - return that.connector.searchAsync(this.usersSearchBase, query) - .then(function (users: { dn: string }[]) { - if (users.length > 0) { - that.logger.debug("LDAP: retrieved user dn is %s", users[0].dn); - return BluebirdPromise.resolve(users[0].dn); - } - return BluebirdPromise.reject(new Error( - Util.format("No user DN found for user '%s'", username))); - }); - } - - searchEmails(username: string): BluebirdPromise { - const that = this; - const query = { - scope: "base", - sizeLimit: 1, - attributes: [this.options.mail_attribute] - }; - - return this.searchUserDn(username) - .then(function (userDN) { - return that.connector.searchAsync(userDN, query); - }) - .then(function (docs: { [mail_attribute: string]: string }[]) { - const emails: string[] = docs - .filter((d) => { return typeof d[that.options.mail_attribute] === "string"; }) - .map((d) => { return d[that.options.mail_attribute]; }); - that.logger.debug("LDAP: emails of user '%s' are %s", username, emails); - return BluebirdPromise.resolve(emails); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapError("Error while searching emails. " + err.stack)); - }); - } - - modifyPassword(username: string, newPassword: string): BluebirdPromise { - const that = this; - this.logger.debug("LDAP: update password of user '%s'", username); - return this.searchUserDn(username) - .then(function (userDN: string) { - return BluebirdPromise.join( - HashGenerator.ssha512(newPassword), - BluebirdPromise.resolve(userDN)); - }) - .then(function (res: string[]) { - const change = { - operation: "replace", - modification: { - userPassword: res[0] - } - }; - that.logger.debug("Password new='%s'", change.modification.userPassword); - return that.connector.modifyAsync(res[1], change); - }) - .then(function () { - return that.connector.unbindAsync(); - }); - } -} diff --git a/themes/black/server/src/lib/authentication/backends/ldap/SessionFactory.ts b/themes/black/server/src/lib/authentication/backends/ldap/SessionFactory.ts deleted file mode 100644 index 0b6c4bff3..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/SessionFactory.ts +++ /dev/null @@ -1,37 +0,0 @@ -import Ldapjs = require("ldapjs"); -import Winston = require("winston"); - -import { IConnectorFactory } from "./connector/IConnectorFactory"; -import { ISessionFactory } from "./ISessionFactory"; -import { ISession } from "./ISession"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Session } from "./Session"; -import { SafeSession } from "./SafeSession"; - - -export class SessionFactory implements ISessionFactory { - private config: LdapConfiguration; - private connectorFactory: IConnectorFactory; - private logger: typeof Winston; - - constructor(ldapConfiguration: LdapConfiguration, - connectorFactory: IConnectorFactory, - logger: typeof Winston) { - this.config = ldapConfiguration; - this.connectorFactory = connectorFactory; - this.logger = logger; - } - - create(userDN: string, password: string): ISession { - const connector = this.connectorFactory.create(); - return new SafeSession( - new Session( - userDN, - password, - this.config, - connector, - this.logger - ) - ); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts b/themes/black/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts deleted file mode 100644 index face3930d..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); - -import { ISession } from "./ISession"; -import { ISessionFactory } from "./ISessionFactory"; - -export class SessionFactoryStub implements ISessionFactory { - createStub: Sinon.SinonStub; - - constructor() { - this.createStub = Sinon.stub(); - } - - create(userDN: string, password: string): ISession { - return this.createStub(userDN, password); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts b/themes/black/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts deleted file mode 100644 index 5faf2ba18..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); - -import { ISession } from "./ISession"; - -export class SessionStub implements ISession { - openStub: Sinon.SinonStub; - closeStub: Sinon.SinonStub; - searchUserDnStub: Sinon.SinonStub; - searchEmailsStub: Sinon.SinonStub; - searchGroupsStub: Sinon.SinonStub; - modifyPasswordStub: Sinon.SinonStub; - - constructor() { - this.openStub = Sinon.stub(); - this.closeStub = Sinon.stub(); - this.searchUserDnStub = Sinon.stub(); - this.searchEmailsStub = Sinon.stub(); - this.searchGroupsStub = Sinon.stub(); - this.modifyPasswordStub = Sinon.stub(); - } - - open(): Bluebird { - return this.openStub(); - } - - close(): Bluebird { - return this.closeStub(); - } - - searchUserDn(username: string): Bluebird { - return this.searchUserDnStub(username); - } - - searchEmails(username: string): Bluebird { - return this.searchEmailsStub(username); - } - - searchGroups(username: string): Bluebird { - return this.searchGroupsStub(username); - } - - modifyPassword(username: string, newPassword: string): Bluebird { - return this.modifyPasswordStub(username, newPassword); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/connector/Connector.ts b/themes/black/server/src/lib/authentication/backends/ldap/connector/Connector.ts deleted file mode 100644 index 2542ea7f9..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/connector/Connector.ts +++ /dev/null @@ -1,69 +0,0 @@ -import LdapJs = require("ldapjs"); -import EventEmitter = require("events"); -import Bluebird = require("bluebird"); -import { IConnector } from "./IConnector"; -import Exceptions = require("../../../../Exceptions"); - -interface SearchEntry { - object: any; -} - -export interface ClientAsync { - on(event: string, callback: (data?: any) => void): void; - bindAsync(username: string, password: string): Bluebird; - unbindAsync(): Bluebird; - searchAsync(base: string, query: LdapJs.SearchOptions): Bluebird; - modifyAsync(userdn: string, change: LdapJs.Change): Bluebird; -} - -export class Connector implements IConnector { - private client: ClientAsync; - - constructor(url: string, ldapjs: typeof LdapJs) { - const ldapClient = ldapjs.createClient({ - url: url, - reconnect: true - }); - - /*const clientLogger = (ldapClient as any).log; - if (clientLogger) { - clientLogger.level("trace"); - }*/ - - this.client = Bluebird.promisifyAll(ldapClient) as any; - } - - bindAsync(username: string, password: string): Bluebird { - return this.client.bindAsync(username, password); - } - - unbindAsync(): Bluebird { - return this.client.unbindAsync(); - } - - searchAsync(base: string, query: any): Bluebird { - const that = this; - return this.client.searchAsync(base, query) - .then(function (res: EventEmitter) { - const doc: SearchEntry[] = []; - return new Bluebird((resolve, reject) => { - res.on("searchEntry", function (entry: SearchEntry) { - doc.push(entry.object); - }); - res.on("error", function (err: Error) { - reject(new Exceptions.LdapSearchError(err.message)); - }); - res.on("end", function () { - resolve(doc); - }); - }); - }) - .catch(function (err: Error) { - return Bluebird.reject(new Exceptions.LdapSearchError(err.message)); - }); - } - - modifyAsync(dn: string, changeRequest: any): Bluebird { - return this.client.modifyAsync(dn, changeRequest); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts b/themes/black/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts deleted file mode 100644 index 61fef07a4..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IConnector } from "./IConnector"; -import { Connector } from "./Connector"; -import { LdapConfiguration } from "../../../../configuration/schema/LdapConfiguration"; -import { Ldapjs } from "Dependencies"; - -export class ConnectorFactory { - private configuration: LdapConfiguration; - private ldapjs: Ldapjs; - - constructor(configuration: LdapConfiguration, ldapjs: Ldapjs) { - this.configuration = configuration; - this.ldapjs = ldapjs; - } - - create(): IConnector { - return new Connector(this.configuration.url, this.ldapjs); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts b/themes/black/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts deleted file mode 100644 index d11fa6386..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); - -import { IConnectorFactory } from "./IConnectorFactory"; -import { IConnector } from "./IConnector"; - -export class ConnectorFactoryStub implements IConnectorFactory { - createStub: Sinon.SinonStub; - - constructor() { - this.createStub = Sinon.stub(); - } - - create(): IConnector { - return this.createStub(); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts b/themes/black/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts deleted file mode 100644 index 0b78225bf..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); - -import { IConnector } from "./IConnector"; - -export class ConnectorStub implements IConnector { - bindAsyncStub: Sinon.SinonStub; - unbindAsyncStub: Sinon.SinonStub; - searchAsyncStub: Sinon.SinonStub; - modifyAsyncStub: Sinon.SinonStub; - - constructor() { - this.bindAsyncStub = Sinon.stub(); - this.unbindAsyncStub = Sinon.stub(); - this.searchAsyncStub = Sinon.stub(); - this.modifyAsyncStub = Sinon.stub(); - } - - bindAsync(username: string, password: string): BluebirdPromise { - return this.bindAsyncStub(username, password); - } - - unbindAsync(): BluebirdPromise { - return this.unbindAsyncStub(); - } - - searchAsync(base: string, query: any): BluebirdPromise { - return this.searchAsyncStub(base, query); - } - - modifyAsync(dn: string, changeRequest: any): BluebirdPromise { - return this.modifyAsyncStub(dn, changeRequest); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/connector/IConnector.ts b/themes/black/server/src/lib/authentication/backends/ldap/connector/IConnector.ts deleted file mode 100644 index 1e63ab193..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/connector/IConnector.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Bluebird = require("bluebird"); -import EventEmitter = require("events"); - -export interface IConnector { - bindAsync(username: string, password: string): Bluebird; - unbindAsync(): Bluebird; - searchAsync(base: string, query: any): Bluebird; - modifyAsync(dn: string, changeRequest: any): Bluebird; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts b/themes/black/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts deleted file mode 100644 index f9ed65eff..000000000 --- a/themes/black/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { IConnector } from "./IConnector"; - -export interface IConnectorFactory { - create(): IConnector; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/totp/ITotpHandler.ts b/themes/black/server/src/lib/authentication/totp/ITotpHandler.ts deleted file mode 100644 index d600d31e4..000000000 --- a/themes/black/server/src/lib/authentication/totp/ITotpHandler.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { TOTPSecret } from "../../../../types/TOTPSecret"; - -export interface ITotpHandler { - generate(label: string, issuer: string): TOTPSecret; - validate(token: string, secret: string): boolean; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/totp/TotpHandler.spec.ts b/themes/black/server/src/lib/authentication/totp/TotpHandler.spec.ts deleted file mode 100644 index 67cffa638..000000000 --- a/themes/black/server/src/lib/authentication/totp/TotpHandler.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import { TotpHandler } from "./TotpHandler"; -import Sinon = require("sinon"); -import Speakeasy = require("speakeasy"); -import Assert = require("assert"); - -describe("authentication/totp/TotpHandler", function() { - let totpValidator: TotpHandler; - let validateStub: Sinon.SinonStub; - - beforeEach(() => { - validateStub = Sinon.stub(Speakeasy.totp, "verify"); - totpValidator = new TotpHandler(Speakeasy); - }); - - afterEach(function() { - validateStub.restore(); - }); - - it("should validate the TOTP token", function() { - const totp_secret = "NBD2ZV64R9UV1O7K"; - const token = "token"; - validateStub.withArgs({ - secret: totp_secret, - token: token, - encoding: "base32", - window: 1 - }).returns(true); - Assert(totpValidator.validate(token, totp_secret)); - }); - - it("should not validate a wrong TOTP token", function() { - const totp_secret = "NBD2ZV64R9UV1O7K"; - const token = "wrong token"; - validateStub.returns(false); - Assert(!totpValidator.validate(token, totp_secret)); - }); -}); - diff --git a/themes/black/server/src/lib/authentication/totp/TotpHandler.ts b/themes/black/server/src/lib/authentication/totp/TotpHandler.ts deleted file mode 100644 index dfab502a7..000000000 --- a/themes/black/server/src/lib/authentication/totp/TotpHandler.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ITotpHandler } from "./ITotpHandler"; -import { TOTPSecret } from "../../../../types/TOTPSecret"; -import Speakeasy = require("speakeasy"); - -const TOTP_ENCODING = "base32"; -const WINDOW: number = 1; - -export class TotpHandler implements ITotpHandler { - private speakeasy: typeof Speakeasy; - - constructor(speakeasy: typeof Speakeasy) { - this.speakeasy = speakeasy; - } - - generate(label: string, issuer: string): TOTPSecret { - const secret = this.speakeasy.generateSecret({ - otpauth_url: false - }) as TOTPSecret; - - secret.otpauth_url = this.speakeasy.otpauthURL({ - secret: secret.ascii, - label: label, - issuer: issuer - }); - return secret; - } - - validate(token: string, secret: string): boolean { - return this.speakeasy.totp.verify({ - secret: secret, - encoding: TOTP_ENCODING, - token: token, - window: WINDOW - }); - } -} diff --git a/themes/black/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts b/themes/black/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts deleted file mode 100644 index ea93330d9..000000000 --- a/themes/black/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import { ITotpHandler } from "./ITotpHandler"; -import { TOTPSecret } from "../../../../types/TOTPSecret"; - -export class TotpHandlerStub implements ITotpHandler { - generateStub: Sinon.SinonStub; - validateStub: Sinon.SinonStub; - - constructor() { - this.generateStub = Sinon.stub(); - this.validateStub = Sinon.stub(); - } - - generate(label: string, issuer: string): TOTPSecret { - return this.generateStub(label, issuer); - } - - validate(token: string, secret: string): boolean { - return this.validateStub(token, secret); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/u2f/IU2fHandler.ts b/themes/black/server/src/lib/authentication/u2f/IU2fHandler.ts deleted file mode 100644 index b9b7d6f26..000000000 --- a/themes/black/server/src/lib/authentication/u2f/IU2fHandler.ts +++ /dev/null @@ -1,9 +0,0 @@ -import U2f = require("u2f"); - -export interface IU2fHandler { - request(appId: string, keyHandle?: string): U2f.Request; - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error; - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authentication/u2f/U2fHandler.ts b/themes/black/server/src/lib/authentication/u2f/U2fHandler.ts deleted file mode 100644 index bf3891e5b..000000000 --- a/themes/black/server/src/lib/authentication/u2f/U2fHandler.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { IU2fHandler } from "./IU2fHandler"; -import U2f = require("u2f"); - -export class U2fHandler implements IU2fHandler { - private u2f: typeof U2f; - - constructor(u2f: typeof U2f) { - this.u2f = u2f; - } - - request(appId: string, keyHandle?: string): U2f.Request { - return this.u2f.request(appId, keyHandle); - } - - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error { - return this.u2f.checkRegistration(registrationRequest, registrationResponse); - } - - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error { - return this.u2f.checkSignature(signatureRequest, signatureResponse, publicKey); - } -} diff --git a/themes/black/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts b/themes/black/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts deleted file mode 100644 index 135d7eb06..000000000 --- a/themes/black/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import U2f = require("u2f"); -import { IU2fHandler } from "./IU2fHandler"; - - -export class U2fHandlerStub implements IU2fHandler { - requestStub: Sinon.SinonStub; - checkRegistrationStub: Sinon.SinonStub; - checkSignatureStub: Sinon.SinonStub; - - constructor() { - this.requestStub = Sinon.stub(); - this.checkRegistrationStub = Sinon.stub(); - this.checkSignatureStub = Sinon.stub(); - } - - request(appId: string, keyHandle?: string): U2f.Request { - return this.requestStub(appId, keyHandle); - } - - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error { - return this.checkRegistrationStub(registrationRequest, registrationResponse); - } - - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error { - return this.checkSignatureStub(signatureRequest, signatureResponse, publicKey); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authorization/Authorizer.spec.ts b/themes/black/server/src/lib/authorization/Authorizer.spec.ts deleted file mode 100644 index 586814042..000000000 --- a/themes/black/server/src/lib/authorization/Authorizer.spec.ts +++ /dev/null @@ -1,372 +0,0 @@ - -import Assert = require("assert"); -import winston = require("winston"); -import { Authorizer } from "./Authorizer"; -import { ACLConfiguration, ACLRule } from "../configuration/schema/AclConfiguration"; -import { Level } from "./Level"; - -describe("authorization/Authorizer", function () { - let authorizer: Authorizer; - let configuration: ACLConfiguration; - - describe("configuration is null", function() { - it("should allow access to anything, anywhere for anybody", function() { - configuration = undefined; - authorizer = new Authorizer(configuration, winston); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc"}, {user: "user1", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user2", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "admin.example.com", resource: "/"}, {user: "user3", groups: ["group3"]}), Level.BYPASS); - }); - }); - - describe("configuration is not null", function () { - beforeEach(function () { - configuration = { - default_policy: "deny", - rules: [] - }; - authorizer = new Authorizer(configuration, winston); - }); - - describe("check access control with default policy to deny", function () { - beforeEach(function () { - configuration.default_policy = "deny"; - }); - - it("should deny access when no rule is provided", function () { - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should control access when multiple domain matcher is provided", function () { - configuration.rules = [{ - domain: "*.mail.example.com", - policy: "two_factor", - subject: "user:user1", - resources: [".*"] - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "mx1.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mx1.server.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should allow access to all resources when resources is not provided", function () { - configuration.rules = [{ - domain: "*.mail.example.com", - policy: "two_factor", - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "mx1.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mx1.server.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - describe("check user rules", function () { - it("should allow access when user has a matching allowing rule", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/another/resource"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should deny to other users", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user2", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/another/resource"}, {user: "user2", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, {user: "user2", groups: ["group1"]}), Level.DENY); - }); - - it("should allow user access only to specific resources", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["/private/.*", "^/begin", "/end$"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/class"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/middle/private/class"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/begin"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/not/begin"}, {user: "user1", groups: ["group1"]}), Level.DENY); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc/end"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc/end/x"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should allow access to multiple domains", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }, { - domain: "home1.example.com", - policy: "one_factor", - resources: [".*"], - subject: "user:user1" - }, { - domain: "home2.example.com", - policy: "deny", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home1.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.ONE_FACTOR); - Assert.equal(authorizer.authorization({domain: "home2.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home3.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should apply rules in order", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "one_factor", - resources: ["/my/private/resource"], - subject: "user:user1" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/my/private/.*"], - subject: "user:user1" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/my/.*"], - subject: "user:user1" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/poney"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/private/duck"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/private/resource"}, {user: "user1", groups: ["group1"]}), Level.ONE_FACTOR); - }); - }); - - describe("check group rules", function () { - it("should allow access when user is in group having a matching allowing rule", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/$"], - subject: "group:group1" - }, { - domain: "home.example.com", - policy: "one_factor", - resources: ["^/test$"], - subject: "group:group2" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/private$"], - subject: "group:group2" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.ONE_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - }); - }); - }); - - describe("check any rules", function () { - it("should control access when any rules are defined", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "bypass", - resources: ["^/public$"] - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/private$"] - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, - {user: "user4", groups: ["group5"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user4", groups: ["group5"]}), Level.DENY); - }); - }); - - describe("check access control with default policy to allow", function () { - beforeEach(function () { - configuration.default_policy = "bypass"; - }); - - it("should allow access to anything when no rule is provided", function () { - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - }); - - it("should deny access to one resource when defined", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["/test"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - }); - }); - - describe("check access control with complete use case", function () { - beforeEach(function () { - configuration.default_policy = "deny"; - }); - - it("should control access of multiple user (real use case)", function () { - // Let say we have three users: admin, john, harry. - // admin is in groups ["admins"] - // john is in groups ["dev", "admin-private"] - // harry is in groups ["dev"] - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/public$", "^/$"] - }, { - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "group:admins" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/?.*"], - subject: "group:admin-private" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/john$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/harry"], - subject: "user:harry" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - }); - - it("should allow when allowed at group level and denied at user level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"], - subject: "group:dev" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should allow access when allowed at 'any' level and denied at user level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should allow access when allowed at 'any' level and denied at group level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "group:dev" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should respect rules precedence", function () { - // the priority from least to most is 'default_policy', 'all', 'group', 'user' - // and the first rules in each category as a lower priority than the latest. - // You can think of it that way: they override themselves inside each category. - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "group:dev" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - }); - }); - }); -}); diff --git a/themes/black/server/src/lib/authorization/Authorizer.ts b/themes/black/server/src/lib/authorization/Authorizer.ts deleted file mode 100644 index 889b7ec20..000000000 --- a/themes/black/server/src/lib/authorization/Authorizer.ts +++ /dev/null @@ -1,85 +0,0 @@ - -import { ACLConfiguration, ACLPolicy, ACLRule } from "../configuration/schema/AclConfiguration"; -import { IAuthorizer } from "./IAuthorizer"; -import { Winston } from "../../../types/Dependencies"; -import { MultipleDomainMatcher } from "./MultipleDomainMatcher"; -import { Level } from "./Level"; -import { Object } from "./Object"; -import { Subject } from "./Subject"; - -function MatchDomain(actualDomain: string) { - return function (rule: ACLRule): boolean { - return MultipleDomainMatcher.match(actualDomain, rule.domain); - }; -} - -function MatchResource(actualResource: string) { - return function (rule: ACLRule): boolean { - // If resources key is not provided, the rule applies to all resources. - if (!rule.resources) return true; - - for (let i = 0; i < rule.resources.length; ++i) { - const regexp = new RegExp(rule.resources[i]); - if (regexp.test(actualResource)) return true; - } - return false; - }; -} - -function MatchSubject(subject: Subject) { - return (rule: ACLRule) => { - // If no subject, matches anybody - if (!rule.subject) return true; - - if (rule.subject.startsWith("user:")) { - const ruleUser = rule.subject.split(":")[1]; - if (subject.user == ruleUser) return true; - } - - if (rule.subject.startsWith("group:")) { - const ruleGroup = rule.subject.split(":")[1]; - if (subject.groups.indexOf(ruleGroup) > -1) return true; - } - return false; - }; -} - -export class Authorizer implements IAuthorizer { - private logger: Winston; - private readonly configuration: ACLConfiguration; - - constructor(configuration: ACLConfiguration, logger_: Winston) { - this.logger = logger_; - this.configuration = configuration; - } - - private getMatchingRules(object: Object, subject: Subject): ACLRule[] { - const rules = this.configuration.rules; - if (!rules) return []; - return rules - .filter(MatchDomain(object.domain)) - .filter(MatchResource(object.resource)) - .filter(MatchSubject(subject)); - } - - private ruleToLevel(policy: string): Level { - if (policy == "bypass") { - return Level.BYPASS; - } else if (policy == "one_factor") { - return Level.ONE_FACTOR; - } else if (policy == "two_factor") { - return Level.TWO_FACTOR; - } - return Level.DENY; - } - - authorization(object: Object, subject: Subject): Level { - if (!this.configuration) return Level.BYPASS; - - const rules = this.getMatchingRules(object, subject); - - return (rules.length > 0) - ? this.ruleToLevel(rules[0].policy) // extract the policy of the first matching rule - : this.ruleToLevel(this.configuration.default_policy); // otherwise use the default policy - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authorization/AuthorizerStub.spec.ts b/themes/black/server/src/lib/authorization/AuthorizerStub.spec.ts deleted file mode 100644 index 9bd6f4a85..000000000 --- a/themes/black/server/src/lib/authorization/AuthorizerStub.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import Sinon = require("sinon"); -import { IAuthorizer } from "./IAuthorizer"; -import { Level } from "./Level"; -import { Object } from "./Object"; -import { Subject } from "./Subject"; - -export class AuthorizerStub implements IAuthorizer { - authorizationMock: Sinon.SinonStub; - - constructor() { - this.authorizationMock = Sinon.stub(); - } - - authorization(object: Object, subject: Subject): Level { - return this.authorizationMock(object, subject); - } -} diff --git a/themes/black/server/src/lib/authorization/IAuthorizer.ts b/themes/black/server/src/lib/authorization/IAuthorizer.ts deleted file mode 100644 index fe7ba367a..000000000 --- a/themes/black/server/src/lib/authorization/IAuthorizer.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Level } from "./Level"; -import { Subject } from "./Subject"; -import { Object } from "./Object"; - -export interface IAuthorizer { - authorization(object: Object, subject: Subject): Level; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authorization/Level.ts b/themes/black/server/src/lib/authorization/Level.ts deleted file mode 100644 index d12802610..000000000 --- a/themes/black/server/src/lib/authorization/Level.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum Level { - BYPASS = 0, - ONE_FACTOR = 1, - TWO_FACTOR = 2, - DENY = 3 -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authorization/MultipleDomainMatcher.ts b/themes/black/server/src/lib/authorization/MultipleDomainMatcher.ts deleted file mode 100644 index 64c647a4a..000000000 --- a/themes/black/server/src/lib/authorization/MultipleDomainMatcher.ts +++ /dev/null @@ -1,12 +0,0 @@ - -export class MultipleDomainMatcher { - static match(domain: string, pattern: string): boolean { - if (pattern.startsWith("*") && - domain.endsWith(pattern.substr(1))) { - return true; - } - else if (domain == pattern) { - return true; - } - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authorization/Object.ts b/themes/black/server/src/lib/authorization/Object.ts deleted file mode 100644 index 5411b0d24..000000000 --- a/themes/black/server/src/lib/authorization/Object.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface Object { - domain: string; - resource: string; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/authorization/Subject.ts b/themes/black/server/src/lib/authorization/Subject.ts deleted file mode 100644 index 310d6b4c0..000000000 --- a/themes/black/server/src/lib/authorization/Subject.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface Subject { - user: string; - groups: string[]; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/ConfigurationParser.spec.ts b/themes/black/server/src/lib/configuration/ConfigurationParser.spec.ts deleted file mode 100644 index 60c0f6182..000000000 --- a/themes/black/server/src/lib/configuration/ConfigurationParser.spec.ts +++ /dev/null @@ -1,171 +0,0 @@ -import * as Assert from "assert"; -import { Configuration } from "./schema/Configuration"; -import { ACLConfiguration } from "./schema/AclConfiguration"; -import { ConfigurationParser } from "./ConfigurationParser"; - -describe("configuration/ConfigurationParser", function () { - function buildYamlConfig(): Configuration { - const yaml_config: Configuration = { - port: 8080, - authentication_backend: { - ldap: { - url: "http://ldap", - base_dn: "dc=example,dc=com", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - user: "user", - password: "pass" - }, - }, - session: { - domain: "example.com", - secret: "secret", - expiration: 40000 - }, - storage: { - local: { - path: "/mydirectory" - } - }, - regulation: { - max_retries: 3, - find_time: 5 * 60, - ban_time: 5 * 60 - }, - logs_level: "debug", - notifier: { - email: { - username: "user", - password: "password", - sender: "admin@example.com", - service: "gmail" - } - } - }; - return yaml_config; - } - - describe("port", function () { - it("should read the port from the yaml file", function () { - const yaml_config = buildYamlConfig(); - yaml_config.port = 7070; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.port, 7070); - }); - - it("should default the port to 8080 if not provided", function () { - const yaml_config = buildYamlConfig(); - delete yaml_config.port; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.port, 8080); - }); - }); - - describe("test session configuration", function() { - it("should get the session attributes", function () { - const yaml_config = buildYamlConfig(); - yaml_config.session = { - domain: "example.com", - secret: "secret", - expiration: 3600, - inactivity: 4000 - }; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.session.domain, "example.com"); - Assert.equal(config.session.secret, "secret"); - Assert.equal(config.session.expiration, 3600); - Assert.equal(config.session.inactivity, 4000); - }); - - it("should be ok not specifying inactivity", function () { - const yaml_config = buildYamlConfig(); - yaml_config.session = { - domain: "example.com", - secret: "secret", - expiration: 3600 - }; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.session.domain, "example.com"); - Assert.equal(config.session.secret, "secret"); - Assert.equal(config.session.expiration, 3600); - Assert.equal(config.session.inactivity, undefined); - }); - }); - - it("should get the log level", function () { - const yaml_config = buildYamlConfig(); - yaml_config.logs_level = "debug"; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.logs_level, "debug"); - }); - - it("should get the notifier config", function () { - const userConfig = buildYamlConfig(); - userConfig.notifier = { - email: { - username: "user", - password: "pass", - sender: "admin@example.com", - service: "gmail" - } - }; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.notifier, { - email: { - username: "user", - password: "pass", - sender: "admin@example.com", - service: "gmail" - } - }); - }); - - describe("access_control", function() { - it("should adapt access_control when it is already ok", function () { - const userConfig = buildYamlConfig(); - userConfig.access_control = { - default_policy: "deny", - rules: [{ - domain: "www.example.com", - policy: "two_factor", - subject: "user:user" - }, { - domain: "public.example.com", - policy: "two_factor" - }] - }; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.access_control, { - default_policy: "deny", - rules: [{ - domain: "www.example.com", - policy: "two_factor", - subject: "user:user" - }, { - domain: "public.example.com", - policy: "two_factor" - }] - } as ACLConfiguration); - }); - - - it("should adapt access_control when it is empty", function () { - const userConfig = buildYamlConfig(); - userConfig.access_control = {} as any; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.access_control, { - default_policy: "bypass", - rules: [] - }); - }); - }); - - describe("default_redirection_url", function() { - it("should parse default_redirection_url", function() { - const userConfig = buildYamlConfig(); - userConfig.default_redirection_url = "dummy_url"; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.default_redirection_url, "dummy_url"); - }); - }); -}); diff --git a/themes/black/server/src/lib/configuration/ConfigurationParser.ts b/themes/black/server/src/lib/configuration/ConfigurationParser.ts deleted file mode 100644 index d92d163c4..000000000 --- a/themes/black/server/src/lib/configuration/ConfigurationParser.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import * as ObjectPath from "object-path"; -import { Configuration, complete } from "./schema/Configuration"; -import Ajv = require("ajv"); -import Path = require("path"); -import Util = require("util"); - -export class ConfigurationParser { - private static parseTypes(configuration: Configuration): string[] { - const schema = require(Path.resolve(__dirname, "./Configuration.schema.json")); - const ajv = new Ajv({ - allErrors: true, - missingRefs: "fail" - }); - ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-06.json")); - const valid = ajv.validate(schema, configuration); - if (!valid) - return ajv.errors.map( - (e: Ajv.ErrorObject) => { return ajv.errorsText([e]); }); - return []; - } - - static parse(configuration: Configuration): Configuration { - const validationErrors = this.parseTypes(configuration); - if (validationErrors.length > 0) { - validationErrors.forEach((e: string) => { console.log(e); }); - throw new Error("Malformed configuration (schema). Please double-check your configuration file."); - } - - const [newConfiguration, completionErrors] = complete(configuration); - - if (completionErrors.length > 0) { - completionErrors.forEach((e: string) => { console.log(e); }); - throw new Error("Malformed configuration (validator). Please double-check your configuration file."); - } - return newConfiguration; - } -} - diff --git a/themes/black/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts b/themes/black/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts deleted file mode 100644 index d4a3093ee..000000000 --- a/themes/black/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { SessionConfigurationBuilder } from "./SessionConfigurationBuilder"; -import { Configuration } from "./schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; - -import ExpressSession = require("express-session"); -import ConnectRedis = require("connect-redis"); -import Sinon = require("sinon"); -import Assert = require("assert"); - -describe("configuration/SessionConfigurationBuilder", function () { - const configuration: Configuration = { - access_control: { - default_policy: "deny", - rules: [] - }, - totp: { - issuer: "authelia.com" - }, - authentication_backend: { - ldap: { - url: "ldap://ldap", - user: "user", - base_dn: "dc=example,dc=com", - password: "password", - additional_groups_dn: "ou=groups", - additional_users_dn: "ou=users", - group_name_attribute: "", - groups_filter: "", - mail_attribute: "", - users_filter: "" - }, - }, - logs_level: "debug", - notifier: { - filesystem: { - filename: "/test" - } - }, - port: 8080, - session: { - name: "authelia_session", - domain: "example.com", - expiration: 3600, - secret: "secret" - }, - regulation: { - max_retries: 3, - ban_time: 5 * 60, - find_time: 5 * 60 - }, - storage: { - local: { - in_memory: true - } - } - }; - - const deps: GlobalDependencies = { - ConnectRedis: Sinon.spy() as any, - ldapjs: Sinon.spy() as any, - nedb: Sinon.spy() as any, - session: Sinon.spy() as any, - speakeasy: Sinon.spy() as any, - u2f: Sinon.spy() as any, - winston: Sinon.spy() as any, - Redis: Sinon.spy() as any - }; - - it("should return session options without redis options", function () { - const options = SessionConfigurationBuilder.build(configuration, deps); - const expectedOptions = { - name: "authelia_session", - secret: "secret", - resave: false, - saveUninitialized: true, - cookie: { - secure: true, - httpOnly: true, - maxAge: 3600, - domain: "example.com" - } - }; - - Assert.deepEqual(expectedOptions, options); - }); - - it("should return session options with redis options", function () { - configuration.session["redis"] = { - host: "redis.example.com", - port: 6379 - }; - const RedisStoreMock = Sinon.spy(); - const redisClient = Sinon.mock().returns({ on: Sinon.spy() }); - - deps.ConnectRedis = Sinon.stub().returns(RedisStoreMock) as any; - deps.Redis = { - createClient: Sinon.mock().returns(redisClient) - } as any; - - const options = SessionConfigurationBuilder.build(configuration, deps); - - const expectedOptions: ExpressSession.SessionOptions = { - secret: "secret", - resave: false, - saveUninitialized: true, - name: "authelia_session", - cookie: { - secure: true, - httpOnly: true, - maxAge: 3600, - domain: "example.com" - }, - store: Sinon.match.object as any - }; - - Assert((deps.ConnectRedis as Sinon.SinonStub).calledWith(deps.session)); - Assert.equal(options.secret, expectedOptions.secret); - Assert.equal(options.resave, expectedOptions.resave); - Assert.equal(options.saveUninitialized, expectedOptions.saveUninitialized); - Assert.deepEqual(options.cookie, expectedOptions.cookie); - Assert(options.store != undefined); - }); - - it("should return session options with redis password", function () { - configuration.session["redis"] = { - host: "redis.example.com", - port: 6379, - password: "authelia_pass" - }; - const RedisStoreMock = Sinon.spy(); - const redisClient = Sinon.mock().returns({ on: Sinon.spy() }); - const createClientStub = Sinon.stub(); - - deps.ConnectRedis = Sinon.stub().returns(RedisStoreMock) as any; - deps.Redis = { - createClient: createClientStub - } as any; - - createClientStub.returns(redisClient); - - const options = SessionConfigurationBuilder.build(configuration, deps); - - Assert(createClientStub.calledWith({ - host: "redis.example.com", - port: 6379, - password: "authelia_pass" - })); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/SessionConfigurationBuilder.ts b/themes/black/server/src/lib/configuration/SessionConfigurationBuilder.ts deleted file mode 100644 index 6ce643d9d..000000000 --- a/themes/black/server/src/lib/configuration/SessionConfigurationBuilder.ts +++ /dev/null @@ -1,52 +0,0 @@ -import ExpressSession = require("express-session"); -import Redis = require("redis"); - -import { Configuration } from "./schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; -import { RedisStoreOptions } from "connect-redis"; - -export class SessionConfigurationBuilder { - - static build(configuration: Configuration, deps: GlobalDependencies): ExpressSession.SessionOptions { - const sessionOptions: ExpressSession.SessionOptions = { - name: configuration.session.name, - secret: configuration.session.secret, - resave: false, - saveUninitialized: true, - cookie: { - secure: true, - httpOnly: true, - maxAge: configuration.session.expiration, - domain: configuration.session.domain - }, - }; - - if (configuration.session.redis) { - let redisOptions; - const options: Redis.ClientOpts = { - host: configuration.session.redis.host, - port: configuration.session.redis.port - }; - - if (configuration.session.redis.password) { - options["password"] = configuration.session.redis.password; - } - const client = deps.Redis.createClient(options); - - client.on("error", function (err: Error) { - console.error("Redis error:", err); - }); - - redisOptions = { - client: client, - logErrors: true - }; - - if (redisOptions) { - const RedisStore = deps.ConnectRedis(deps.session); - sessionOptions.store = new RedisStore(redisOptions); - } - } - return sessionOptions; - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/AclConfiguration.spec.ts b/themes/black/server/src/lib/configuration/schema/AclConfiguration.spec.ts deleted file mode 100644 index d1e2a03a2..000000000 --- a/themes/black/server/src/lib/configuration/schema/AclConfiguration.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ACLConfiguration, complete } from "./AclConfiguration"; -import Assert = require("assert"); - -describe("configuration/schema/AclConfiguration", function() { - it("should complete ACLConfiguration", function() { - const configuration: ACLConfiguration = {}; - const [newConfiguration, errors] = complete(configuration); - - Assert.deepEqual(newConfiguration.default_policy, "bypass"); - Assert.deepEqual(newConfiguration.rules, []); - }); - - it("should return errors when subject is not good", function() { - const configuration: ACLConfiguration = { - default_policy: "deny", - rules: [{ - domain: "dev.example.com", - subject: "user:abc", - policy: "bypass" - }, { - domain: "dev.example.com", - subject: "user:def", - policy: "bypass" - }, { - domain: "dev.example.com", - subject: "badkey:abc", - policy: "bypass" - }] - }; - const [newConfiguration, errors] = complete(configuration); - - Assert.deepEqual(errors, ["Rule 2 has wrong subject. It should be starting with user: or group:."]); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/AclConfiguration.ts b/themes/black/server/src/lib/configuration/schema/AclConfiguration.ts deleted file mode 100644 index 40401dd64..000000000 --- a/themes/black/server/src/lib/configuration/schema/AclConfiguration.ts +++ /dev/null @@ -1,41 +0,0 @@ - -export type ACLPolicy = "deny" | "bypass" | "one_factor" | "two_factor"; - -export type ACLRule = { - domain: string; - resources?: string[]; - subject?: string; - policy: ACLPolicy; -}; - -export interface ACLConfiguration { - default_policy?: ACLPolicy; - rules?: ACLRule[]; -} - -export function complete(configuration: ACLConfiguration): [ACLConfiguration, string[]] { - const newConfiguration: ACLConfiguration = (configuration) - ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.default_policy) { - newConfiguration.default_policy = "bypass"; - } - - if (!newConfiguration.rules) { - newConfiguration.rules = []; - } - - if (newConfiguration.rules.length > 0) { - const errors: string[] = []; - newConfiguration.rules.forEach((r, idx) => { - if (r.subject && !r.subject.match(/^(user|group):[a-zA-Z0-9]+$/)) { - errors.push(`Rule ${idx} has wrong subject. It should be starting with user: or group:.`); - } - }); - if (errors.length > 0) { - return [newConfiguration, errors]; - } - } - - return [newConfiguration, []]; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts b/themes/black/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts deleted file mode 100644 index 3ca86381c..000000000 --- a/themes/black/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { AuthenticationBackendConfiguration, complete } from "./AuthenticationBackendConfiguration"; -import Assert = require("assert"); - -describe("configuration/schema/AuthenticationBackendConfiguration", function() { - it("should ensure there is at least one key", function() { - const configuration: AuthenticationBackendConfiguration = {} as any; - const [newConfiguration, error] = complete(configuration); - - Assert.equal(error, "Authentication backend must have one of the following keys:`ldap` or `file`"); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts b/themes/black/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts deleted file mode 100644 index 7f77f894f..000000000 --- a/themes/black/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { LdapConfiguration } from "./LdapConfiguration"; -import { FileUsersDatabaseConfiguration } from "./FileUsersDatabaseConfiguration"; - -export interface AuthenticationBackendConfiguration { - ldap?: LdapConfiguration; - file?: FileUsersDatabaseConfiguration; -} - -export function complete( - configuration: AuthenticationBackendConfiguration) - : [AuthenticationBackendConfiguration, string] { - - const newConfiguration: AuthenticationBackendConfiguration = (configuration) - ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (Object.keys(newConfiguration).length != 1) { - return [ - newConfiguration, - "Authentication backend must have one of the following keys:" + - "`ldap` or `file`" - ]; - } - - return [newConfiguration, undefined]; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/Configuration.ts b/themes/black/server/src/lib/configuration/schema/Configuration.ts deleted file mode 100644 index 8d16a5fb2..000000000 --- a/themes/black/server/src/lib/configuration/schema/Configuration.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { ACLConfiguration, complete as AclConfigurationComplete } from "./AclConfiguration"; -import { AuthenticationBackendConfiguration, complete as AuthenticationBackendComplete } from "./AuthenticationBackendConfiguration"; -import { NotifierConfiguration, complete as NotifierConfigurationComplete } from "./NotifierConfiguration"; -import { RegulationConfiguration, complete as RegulationConfigurationComplete } from "./RegulationConfiguration"; -import { SessionConfiguration, complete as SessionConfigurationComplete } from "./SessionConfiguration"; -import { StorageConfiguration, complete as StorageConfigurationComplete } from "./StorageConfiguration"; -import { TotpConfiguration, complete as TotpConfigurationComplete } from "./TotpConfiguration"; - -export interface Configuration { - access_control?: ACLConfiguration; - authentication_backend: AuthenticationBackendConfiguration; - default_redirection_url?: string; - logs_level?: string; - notifier?: NotifierConfiguration; - port?: number; - regulation?: RegulationConfiguration; - session?: SessionConfiguration; - storage?: StorageConfiguration; - totp?: TotpConfiguration; -} - -export function complete( - configuration: Configuration): - [Configuration, string[]] { - - const newConfiguration: Configuration = JSON.parse( - JSON.stringify(configuration)); - const errors: string[] = []; - - const [acls, aclsErrors] = AclConfigurationComplete( - newConfiguration.access_control); - - newConfiguration.access_control = acls; - if (aclsErrors.length > 0) { - errors.concat(aclsErrors); - } - - const [backend, error] = - AuthenticationBackendComplete( - newConfiguration.authentication_backend); - - if (error) errors.push(error); - newConfiguration.authentication_backend = backend; - - if (!newConfiguration.logs_level) { - newConfiguration.logs_level = "info"; - } - - const [notifier, notifierError] = NotifierConfigurationComplete( - newConfiguration.notifier); - newConfiguration.notifier = notifier; - if (notifierError) errors.push(notifierError); - - if (!newConfiguration.port) { - newConfiguration.port = 8080; - } - - newConfiguration.regulation = RegulationConfigurationComplete( - newConfiguration.regulation); - newConfiguration.session = SessionConfigurationComplete( - newConfiguration.session); - newConfiguration.storage = StorageConfigurationComplete( - newConfiguration.storage); - newConfiguration.totp = TotpConfigurationComplete( - newConfiguration.totp); - - return [newConfiguration, errors]; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts b/themes/black/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts deleted file mode 100644 index d19002ba9..000000000 --- a/themes/black/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export interface FileUsersDatabaseConfiguration { - path: string; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/LdapConfiguration.spec.ts b/themes/black/server/src/lib/configuration/schema/LdapConfiguration.spec.ts deleted file mode 100644 index cc73d1085..000000000 --- a/themes/black/server/src/lib/configuration/schema/LdapConfiguration.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import Assert = require("assert"); -import { LdapConfiguration, complete } from "./LdapConfiguration"; - -describe("configuration/schema/AuthenticationMethodsConfiguration", function() { - it("should ensure at least one key is provided", function() { - const configuration: LdapConfiguration = { - url: "ldap.example.com", - base_dn: "dc=example,dc=com", - user: "admin", - password: "password" - }; - const newConfiguration = complete(configuration); - - Assert.deepEqual(newConfiguration, { - url: "ldap.example.com", - base_dn: "dc=example,dc=com", - user: "admin", - password: "password", - users_filter: "cn={0}", - group_name_attribute: "cn", - groups_filter: "member={dn}", - mail_attribute: "mail" - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/LdapConfiguration.ts b/themes/black/server/src/lib/configuration/schema/LdapConfiguration.ts deleted file mode 100644 index 5dacb9390..000000000 --- a/themes/black/server/src/lib/configuration/schema/LdapConfiguration.ts +++ /dev/null @@ -1,40 +0,0 @@ -import Util = require("util"); - -export interface LdapConfiguration { - url: string; - base_dn: string; - - additional_users_dn?: string; - users_filter?: string; - - additional_groups_dn?: string; - groups_filter?: string; - - group_name_attribute?: string; - mail_attribute?: string; - - user: string; // admin username - password: string; // admin password -} - -export function complete(configuration: LdapConfiguration): LdapConfiguration { - const newConfiguration: LdapConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.users_filter) { - newConfiguration.users_filter = "cn={0}"; - } - - if (!newConfiguration.groups_filter) { - newConfiguration.groups_filter = "member={dn}"; - } - - if (!newConfiguration.group_name_attribute) { - newConfiguration.group_name_attribute = "cn"; - } - - if (!newConfiguration.mail_attribute) { - newConfiguration.mail_attribute = "mail"; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts b/themes/black/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts deleted file mode 100644 index 6c576e8e0..000000000 --- a/themes/black/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import Assert = require("assert"); -import { NotifierConfiguration, complete } from "./NotifierConfiguration"; - -describe("configuration/schema/NotifierConfiguration", function() { - it("should use a default notifier when none is provided", function() { - const configuration: NotifierConfiguration = {}; - const [newConfiguration, error] = complete(configuration); - - Assert.deepEqual(newConfiguration.filesystem, {filename: "/tmp/authelia/notification.txt"}); - }); - - it("should ensure correct key is provided", function() { - const configuration = { - abc: "badvalue" - }; - const [newConfiguration, error] = complete(configuration as any); - - Assert.equal(error, "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"); - }); - - it("should ensure there is no more than one key", function() { - const configuration: NotifierConfiguration = { - smtp: { - host: "smtp.example.com", - port: 25, - secure: false, - sender: "test@example.com" - }, - email: { - username: "test", - password: "test", - sender: "test@example.com", - service: "gmail" - } - }; - const [newConfiguration, error] = complete(configuration); - - Assert.equal(error, "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/NotifierConfiguration.ts b/themes/black/server/src/lib/configuration/schema/NotifierConfiguration.ts deleted file mode 100644 index 7bcce15c2..000000000 --- a/themes/black/server/src/lib/configuration/schema/NotifierConfiguration.ts +++ /dev/null @@ -1,45 +0,0 @@ - -export interface EmailNotifierConfiguration { - username: string; - password: string; - sender: string; - service: string; -} - -export interface SmtpNotifierConfiguration { - username?: string; - password?: string; - host: string; - port: number; - secure: boolean; - sender: string; -} - -export interface FileSystemNotifierConfiguration { - filename: string; -} - -export interface NotifierConfiguration { - email?: EmailNotifierConfiguration; - smtp?: SmtpNotifierConfiguration; - filesystem?: FileSystemNotifierConfiguration; -} - -export function complete(configuration: NotifierConfiguration): [NotifierConfiguration, string] { - const newConfiguration: NotifierConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (Object.keys(newConfiguration).length == 0) - newConfiguration.filesystem = { filename: "/tmp/authelia/notification.txt" }; - - const ERROR = "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"; - - if (Object.keys(newConfiguration).length != 1) - return [newConfiguration, ERROR]; - - const key = Object.keys(newConfiguration)[0]; - - if (key != "filesystem" && key != "smtp" && key != "email") - return [newConfiguration, ERROR]; - - return [newConfiguration, undefined]; -} diff --git a/themes/black/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts b/themes/black/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts deleted file mode 100644 index dce2caf4e..000000000 --- a/themes/black/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Assert = require("assert"); -import { RegulationConfiguration, complete } from "./RegulationConfiguration"; - -describe("configuration/schema/RegulationConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: RegulationConfiguration = {}; - const newConfiguration = complete(configuration); - - Assert.equal(newConfiguration.ban_time, 300); - Assert.equal(newConfiguration.find_time, 120); - Assert.equal(newConfiguration.max_retries, 3); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/RegulationConfiguration.ts b/themes/black/server/src/lib/configuration/schema/RegulationConfiguration.ts deleted file mode 100644 index 117463f43..000000000 --- a/themes/black/server/src/lib/configuration/schema/RegulationConfiguration.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface RegulationConfiguration { - max_retries?: number; - find_time?: number; - ban_time?: number; -} - -export function complete(configuration: RegulationConfiguration): RegulationConfiguration { - const newConfiguration: RegulationConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.max_retries) { - newConfiguration.max_retries = 3; - } - - if (!newConfiguration.find_time) { - newConfiguration.find_time = 120; // seconds - } - - if (!newConfiguration.ban_time) { - newConfiguration.ban_time = 300; // seconds - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/SessionConfiguration.spec.ts b/themes/black/server/src/lib/configuration/schema/SessionConfiguration.spec.ts deleted file mode 100644 index e54010837..000000000 --- a/themes/black/server/src/lib/configuration/schema/SessionConfiguration.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Assert = require("assert"); -import { SessionConfiguration, complete } from "./SessionConfiguration"; - -describe("configuration/schema/SessionConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: SessionConfiguration = { - domain: "example.com", - secret: "unsecure_secret" - }; - const newConfiguration = complete(configuration); - - Assert.equal(newConfiguration.name, 'authelia_session'); - Assert.equal(newConfiguration.expiration, 3600000); - Assert.equal(newConfiguration.inactivity, undefined); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/SessionConfiguration.ts b/themes/black/server/src/lib/configuration/schema/SessionConfiguration.ts deleted file mode 100644 index 2c88bb215..000000000 --- a/themes/black/server/src/lib/configuration/schema/SessionConfiguration.ts +++ /dev/null @@ -1,32 +0,0 @@ -export interface SessionRedisOptions { - host: string; - port: number; - password?: string; -} - -export interface SessionConfiguration { - name?: string; - domain: string; - secret: string; - expiration?: number; - inactivity?: number; - redis?: SessionRedisOptions; -} - -export function complete(configuration: SessionConfiguration): SessionConfiguration { - const newConfiguration: SessionConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.name) { - newConfiguration.name = "authelia_session"; - } - - if (!newConfiguration.expiration) { - newConfiguration.expiration = 3600000; // 1 hour - } - - if (!newConfiguration.inactivity) { - newConfiguration.inactivity = undefined; // disabled - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/StorageConfiguration.spec.ts b/themes/black/server/src/lib/configuration/schema/StorageConfiguration.spec.ts deleted file mode 100644 index 9d02a11b6..000000000 --- a/themes/black/server/src/lib/configuration/schema/StorageConfiguration.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import Assert = require("assert"); -import { StorageConfiguration, complete } from "./StorageConfiguration"; - -describe("configuration/schema/StorageConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: StorageConfiguration = {}; - const newConfiguration = complete(configuration); - - Assert.deepEqual(newConfiguration, { - local: { - in_memory: true - } - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/StorageConfiguration.ts b/themes/black/server/src/lib/configuration/schema/StorageConfiguration.ts deleted file mode 100644 index 47e356ef4..000000000 --- a/themes/black/server/src/lib/configuration/schema/StorageConfiguration.ts +++ /dev/null @@ -1,30 +0,0 @@ -export interface MongoStorageConfiguration { - url: string; - database: string; - auth?: { - username: string; - password: string; - }; -} - -export interface LocalStorageConfiguration { - path?: string; - in_memory?: boolean; -} - -export interface StorageConfiguration { - local?: LocalStorageConfiguration; - mongo?: MongoStorageConfiguration; -} - -export function complete(configuration: StorageConfiguration): StorageConfiguration { - const newConfiguration: StorageConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.local && !newConfiguration.mongo) { - newConfiguration.local = { - in_memory: true - }; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/TotpConfiguration.ts b/themes/black/server/src/lib/configuration/schema/TotpConfiguration.ts deleted file mode 100644 index 683135639..000000000 --- a/themes/black/server/src/lib/configuration/schema/TotpConfiguration.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface TotpConfiguration { - issuer: string; -} - -export function complete(configuration: TotpConfiguration): TotpConfiguration { - const newConfiguration: TotpConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.issuer) { - newConfiguration.issuer = "authelia.com"; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts b/themes/black/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts deleted file mode 100644 index 8008b4833..000000000 --- a/themes/black/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts +++ /dev/null @@ -1,9 +0,0 @@ - -export interface UserInfo { - username: string; - password_hash: string; - email: string; - groups?: string[]; -} - -export type UserDatabaseConfiguration = UserInfo[]; \ No newline at end of file diff --git a/themes/black/server/src/lib/connectors/mongo/IMongoClient.d.ts b/themes/black/server/src/lib/connectors/mongo/IMongoClient.d.ts deleted file mode 100644 index 36cb4b8bf..000000000 --- a/themes/black/server/src/lib/connectors/mongo/IMongoClient.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import MongoDB = require("mongodb"); -import Bluebird = require("bluebird"); - -export interface IMongoClient { - collection(name: string): Bluebird -} \ No newline at end of file diff --git a/themes/black/server/src/lib/connectors/mongo/MongoClient.spec.ts b/themes/black/server/src/lib/connectors/mongo/MongoClient.spec.ts deleted file mode 100644 index ca0c68593..000000000 --- a/themes/black/server/src/lib/connectors/mongo/MongoClient.spec.ts +++ /dev/null @@ -1,119 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); -import MongoDB = require("mongodb"); -import Sinon = require("sinon"); - -import { MongoClient } from "./MongoClient"; -import { GlobalLoggerStub } from "../../logging/GlobalLoggerStub.spec"; -import { MongoStorageConfiguration } from "../../configuration/schema/StorageConfiguration"; - -describe("connectors/mongo/MongoClient", function () { - let MongoClientStub: any; - let mongoClientStub: any; - let mongoDatabaseStub: any; - let logger: GlobalLoggerStub = new GlobalLoggerStub(); - - const configuration: MongoStorageConfiguration = { - url: "mongo://url", - database: "databasename" - }; - - describe("connection", () => { - before(() => { - mongoClientStub = { - db: Sinon.stub() - }; - mongoDatabaseStub = { - on: Sinon.stub(), - collection: Sinon.stub() - } - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - undefined, mongoClientStub); - mongoClientStub.db.returns( - mongoDatabaseStub); - }); - - after(() => { - MongoClientStub.restore(); - }); - - it("should use credentials from configuration", () => { - configuration.auth = { - username: "authelia", - password: "authelia_pass" - }; - - const client = new MongoClient(configuration, logger); - return client.collection("test") - .then(() => { - Assert(MongoClientStub.calledWith("mongo://url", { - auth: { - user: "authelia", - password: "authelia_pass" - } - })) - }); - }); - }); - - describe("collection", () => { - before(function() { - mongoClientStub = { - db: Sinon.stub() - }; - mongoDatabaseStub = { - on: Sinon.stub(), - collection: Sinon.stub() - } - }); - - describe("Connection to mongo is ok", function() { - before(function () { - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - undefined, mongoClientStub); - mongoClientStub.db.returns( - mongoDatabaseStub); - }); - - after(function () { - MongoClientStub.restore(); - }); - - it("should create a collection", function () { - const COLLECTION_NAME = "mycollection"; - const client = new MongoClient(configuration, logger); - - mongoDatabaseStub.collection.returns("COL"); - return client.collection(COLLECTION_NAME) - .then((collection) => mongoDatabaseStub.collection.calledWith(COLLECTION_NAME)); - }); - }); - - describe("Connection to mongo is broken", function() { - before(function () { - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - new Error("Failed connection"), undefined); - }); - - after(function () { - MongoClientStub.restore(); - }); - - it("should fail creating the collection", function() { - const COLLECTION_NAME = "mycollection"; - const client = new MongoClient(configuration, logger); - - mongoDatabaseStub.collection.returns("COL"); - return client.collection(COLLECTION_NAME) - .then((collection) => Bluebird.reject(new Error("should not be here."))) - .catch((err) => Bluebird.resolve()); - }); - }) - }); -}); diff --git a/themes/black/server/src/lib/connectors/mongo/MongoClient.ts b/themes/black/server/src/lib/connectors/mongo/MongoClient.ts deleted file mode 100644 index d15731e97..000000000 --- a/themes/black/server/src/lib/connectors/mongo/MongoClient.ts +++ /dev/null @@ -1,76 +0,0 @@ - -import MongoDB = require("mongodb"); -import { IMongoClient } from "./IMongoClient"; -import Bluebird = require("bluebird"); -import { AUTHENTICATION_FAILED } from "../../../../../shared/UserMessages"; -import { IGlobalLogger } from "../../logging/IGlobalLogger"; -import { MongoStorageConfiguration } from "../../configuration/schema/StorageConfiguration"; - -export class MongoClient implements IMongoClient { - private configuration: MongoStorageConfiguration; - - private database: MongoDB.Db; - private client: MongoDB.MongoClient; - private logger: IGlobalLogger; - - constructor( - configuration: MongoStorageConfiguration, - logger: IGlobalLogger) { - - this.configuration = configuration; - this.logger = logger; - } - - connect(): Bluebird { - const that = this; - const options: MongoDB.MongoClientOptions = {}; - if (that.configuration.auth) { - options["auth"] = { - user: that.configuration.auth.username, - password: that.configuration.auth.password - }; - } - - return new Bluebird((resolve, reject) => { - MongoDB.MongoClient.connect( - this.configuration.url, - options, - function(err, client) { - if (err) { - reject(err); - return; - } - resolve(client); - }); - }) - .then(function (client: MongoDB.MongoClient) { - that.database = client.db(that.configuration.database); - that.database.on("close", () => { - that.logger.info("[MongoClient] Lost connection."); - }); - that.database.on("reconnect", () => { - that.logger.info("[MongoClient] Reconnected."); - }); - that.client = client; - }); - } - - close(): Bluebird { - if (this.client) { - this.client.close(); - this.database = undefined; - this.client = undefined; - } - return Bluebird.resolve(); - } - - collection(name: string): Bluebird { - if (!this.client) { - const that = this; - return this.connect() - .then(() => Bluebird.resolve(that.database.collection(name))); - } - - return Bluebird.resolve(this.database.collection(name)); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/connectors/mongo/MongoClientStub.spec.ts b/themes/black/server/src/lib/connectors/mongo/MongoClientStub.spec.ts deleted file mode 100644 index 1cfd48e32..000000000 --- a/themes/black/server/src/lib/connectors/mongo/MongoClientStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import MongoDB = require("mongodb"); -import Bluebird = require("bluebird"); -import { IMongoClient } from "../../../../src/lib/connectors/mongo/IMongoClient"; - -export class MongoClientStub implements IMongoClient { - public collectionStub: Sinon.SinonStub; - - constructor() { - this.collectionStub = Sinon.stub(); - } - - collection(name: string): Bluebird { - return this.collectionStub(name); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/logging/GlobalLogger.ts b/themes/black/server/src/lib/logging/GlobalLogger.ts deleted file mode 100644 index 4da7acf49..000000000 --- a/themes/black/server/src/lib/logging/GlobalLogger.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { IGlobalLogger } from "./IGlobalLogger"; -import Util = require("util"); -import Express = require("express"); -import Winston = require("winston"); - -declare module "express" { - interface Request { - id: string; - } -} - -export class GlobalLogger implements IGlobalLogger { - private winston: typeof Winston; - constructor(winston: typeof Winston) { - this.winston = winston; - } - - private buildMessage(message: string, ...args: any[]): string { - return Util.format("date='%s' message='%s'", new Date(), - Util.format(message, ...args)); - } - - info(message: string, ...args: any[]): void { - this.winston.info(this.buildMessage(message, ...args)); - } - - debug(message: string, ...args: any[]): void { - this.winston.debug(this.buildMessage(message, ...args)); - } - - error(message: string, ...args: any[]): void { - this.winston.debug(this.buildMessage(message, ...args)); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/logging/GlobalLoggerStub.spec.ts b/themes/black/server/src/lib/logging/GlobalLoggerStub.spec.ts deleted file mode 100644 index d4bb13710..000000000 --- a/themes/black/server/src/lib/logging/GlobalLoggerStub.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import Sinon = require("sinon"); -import { GlobalLogger } from "./GlobalLogger"; -import Winston = require("winston"); -import Express = require("express"); -import { IGlobalLogger } from "./IGlobalLogger"; - -export class GlobalLoggerStub implements IGlobalLogger { - infoStub: Sinon.SinonStub; - debugStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - private globalLogger: IGlobalLogger; - - constructor(enableLogging?: boolean) { - this.infoStub = Sinon.stub(); - this.debugStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - if (enableLogging) - this.globalLogger = new GlobalLogger(Winston); - } - - info(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.infoStub(message, ...args); - } - - debug(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.debugStub(message, ...args); - } - - error(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.errorStub(message, ...args); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/logging/IGlobalLogger.ts b/themes/black/server/src/lib/logging/IGlobalLogger.ts deleted file mode 100644 index 548515ec7..000000000 --- a/themes/black/server/src/lib/logging/IGlobalLogger.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface IGlobalLogger { - info(message: string, ...args: any[]): void; - debug(message: string, ...args: any[]): void; - error(message: string, ...args: any[]): void; -} diff --git a/themes/black/server/src/lib/logging/IRequestLogger.ts b/themes/black/server/src/lib/logging/IRequestLogger.ts deleted file mode 100644 index 126a601fe..000000000 --- a/themes/black/server/src/lib/logging/IRequestLogger.ts +++ /dev/null @@ -1,7 +0,0 @@ -import Express = require("express"); - -export interface IRequestLogger { - info(req: Express.Request, message: string, ...args: any[]): void; - debug(req: Express.Request, message: string, ...args: any[]): void; - error(req: Express.Request, message: string, ...args: any[]): void; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/logging/RequestLogger.ts b/themes/black/server/src/lib/logging/RequestLogger.ts deleted file mode 100644 index c45c66018..000000000 --- a/themes/black/server/src/lib/logging/RequestLogger.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { IRequestLogger } from "./IRequestLogger"; -import Util = require("util"); -import Express = require("express"); -import Winston = require("winston"); - -declare module "express" { - interface Request { - id: string; - } -} - -export class RequestLogger implements IRequestLogger { - private winston: typeof Winston; - - constructor(winston: typeof Winston) { - this.winston = winston; - } - - private formatHeader(req: Express.Request) { - const clientIP = req.ip; // The IP of the original client going through the proxy chain. - return Util.format("date='%s' method='%s', path='%s' requestId='%s' sessionId='%s' ip='%s'", - new Date(), req.method, req.path, req.id, req.sessionID, clientIP); - } - - private formatBody(message: string) { - return Util.format("message='%s'", message); - } - - private formatMessage(req: Express.Request, message: string) { - return Util.format("%s %s", this.formatHeader(req), - this.formatBody(message)); - } - - info(req: Express.Request, message: string, ...args: any[]): void { - this.winston.info(this.formatMessage(req, message), ...args); - } - - debug(req: Express.Request, message: string, ...args: any[]): void { - this.winston.debug(this.formatMessage(req, message), ...args); - } - - error(req: Express.Request, message: string, ...args: any[]): void { - this.winston.error(this.formatMessage(req, message), ...args); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/logging/RequestLoggerStub.spec.ts b/themes/black/server/src/lib/logging/RequestLoggerStub.spec.ts deleted file mode 100644 index b0e375210..000000000 --- a/themes/black/server/src/lib/logging/RequestLoggerStub.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { IRequestLogger } from "./IRequestLogger"; -import Sinon = require("sinon"); -import { RequestLogger } from "./RequestLogger"; -import Winston = require("winston"); -import Express = require("express"); - -export class RequestLoggerStub implements IRequestLogger { - infoStub: Sinon.SinonStub; - debugStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - private requestLogger: RequestLogger; - - constructor(enableLogging?: boolean) { - this.infoStub = Sinon.stub(); - this.debugStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - if (enableLogging) - this.requestLogger = new RequestLogger(Winston); - } - - info(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.infoStub(req, message, ...args); - } - - debug(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.debugStub(req, message, ...args); - } - - error(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.errorStub(req, message, ...args); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/notifiers/AbstractEmailNotifier.ts b/themes/black/server/src/lib/notifiers/AbstractEmailNotifier.ts deleted file mode 100644 index 198e4e5db..000000000 --- a/themes/black/server/src/lib/notifiers/AbstractEmailNotifier.ts +++ /dev/null @@ -1,23 +0,0 @@ - -import { INotifier } from "../notifiers/INotifier"; -import { Identity } from "../../../types/Identity"; - -import Fs = require("fs"); -import Path = require("path"); -import Ejs = require("ejs"); -import BluebirdPromise = require("bluebird"); - -const email_template = Fs.readFileSync(Path.join(__dirname, "../../resources/email-template.ejs"), "UTF-8"); - -export abstract class AbstractEmailNotifier implements INotifier { - notify(to: string, subject: string, link: string): BluebirdPromise { - const d = { - url: link, - button_title: "Continue", - title: subject - }; - return this.sendEmail(to, subject, Ejs.render(email_template, d)); - } - - abstract sendEmail(to: string, subject: string, content: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/notifiers/EmailNotifier.spec.ts b/themes/black/server/src/lib/notifiers/EmailNotifier.spec.ts deleted file mode 100644 index 8211bbc02..000000000 --- a/themes/black/server/src/lib/notifiers/EmailNotifier.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as sinon from "sinon"; -import * as Assert from "assert"; -import BluebirdPromise = require("bluebird"); - -import { MailSenderStub } from "./MailSenderStub.spec"; -import EmailNotifier = require("./EmailNotifier"); - - -describe("notifiers/EmailNotifier", function () { - it("should send an email to given user", function () { - const mailSender = new MailSenderStub(); - const options = { - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }; - - mailSender.sendStub.returns(BluebirdPromise.resolve()); - const sender = new EmailNotifier.EmailNotifier(options, mailSender); - const subject = "subject"; - const url = "http://test.com"; - - return sender.notify("user@example.com", subject, url) - .then(function () { - Assert.equal(mailSender.sendStub.getCall(0).args[0].to, "user@example.com"); - Assert.equal(mailSender.sendStub.getCall(0).args[0].subject, "subject"); - return BluebirdPromise.resolve(); - }); - }); - - it("should fail while sending an email", function () { - const mailSender = new MailSenderStub(); - const options = { - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }; - - mailSender.sendStub.returns(BluebirdPromise.reject(new Error("Failed to send mail"))); - const sender = new EmailNotifier.EmailNotifier(options, mailSender); - const subject = "subject"; - const url = "http://test.com"; - - return sender.notify("user@example.com", subject, url) - .then(function () { - return BluebirdPromise.reject(new Error()); - }, function() { - Assert.equal(mailSender.sendStub.getCall(0).args[0].from, "admin@example.com"); - return BluebirdPromise.resolve(); - }); - }); -}); diff --git a/themes/black/server/src/lib/notifiers/EmailNotifier.ts b/themes/black/server/src/lib/notifiers/EmailNotifier.ts deleted file mode 100644 index 4df7c861e..000000000 --- a/themes/black/server/src/lib/notifiers/EmailNotifier.ts +++ /dev/null @@ -1,27 +0,0 @@ - -import * as BluebirdPromise from "bluebird"; - -import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier"; -import { EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; -import { IMailSender } from "./IMailSender"; - -export class EmailNotifier extends AbstractEmailNotifier { - private mailSender: IMailSender; - private sender: string; - - constructor(options: EmailNotifierConfiguration, mailSender: IMailSender) { - super(); - this.mailSender = mailSender; - this.sender = options.sender; - } - - sendEmail(to: string, subject: string, content: string) { - const mailOptions = { - from: this.sender, - to: to, - subject: subject, - html: content - }; - return this.mailSender.send(mailOptions); - } -} diff --git a/themes/black/server/src/lib/notifiers/FileSystemNotifier.ts b/themes/black/server/src/lib/notifiers/FileSystemNotifier.ts deleted file mode 100644 index 23f6242c4..000000000 --- a/themes/black/server/src/lib/notifiers/FileSystemNotifier.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as BluebirdPromise from "bluebird"; -import * as util from "util"; -import * as Fs from "fs"; -import { INotifier } from "./INotifier"; -import { Identity } from "../../../types/Identity"; - -import { FileSystemNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class FileSystemNotifier implements INotifier { - private filename: string; - - constructor(options: FileSystemNotifierConfiguration) { - this.filename = options.filename; - } - - notify(to: string, subject: string, link: string): BluebirdPromise { - const content = util.format("Date: %s\nEmail: %s\nSubject: %s\nLink: %s", - new Date().toString(), to, subject, link); - const writeFilePromised: any = BluebirdPromise.promisify(Fs.writeFile); - return writeFilePromised(this.filename, content); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/notifiers/IMailSender.ts b/themes/black/server/src/lib/notifiers/IMailSender.ts deleted file mode 100644 index 34ac464a8..000000000 --- a/themes/black/server/src/lib/notifiers/IMailSender.ts +++ /dev/null @@ -1,6 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); - -export interface IMailSender { - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/notifiers/IMailSenderBuilder.ts b/themes/black/server/src/lib/notifiers/IMailSenderBuilder.ts deleted file mode 100644 index 36d4dcdf7..000000000 --- a/themes/black/server/src/lib/notifiers/IMailSenderBuilder.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export interface IMailSenderBuilder { - buildEmail(options: EmailNotifierConfiguration): IMailSender; - buildSmtp(options: SmtpNotifierConfiguration): IMailSender; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/notifiers/INotifier.ts b/themes/black/server/src/lib/notifiers/INotifier.ts deleted file mode 100644 index b9a6b138c..000000000 --- a/themes/black/server/src/lib/notifiers/INotifier.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as BluebirdPromise from "bluebird"; - -export interface INotifier { - notify(to: string, subject: string, link: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/notifiers/MailSender.ts b/themes/black/server/src/lib/notifiers/MailSender.ts deleted file mode 100644 index 536a88e63..000000000 --- a/themes/black/server/src/lib/notifiers/MailSender.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import Nodemailer = require("nodemailer"); -import NodemailerDirectTransport = require("nodemailer-direct-transport"); -import NodemailerSmtpTransport = require("nodemailer-smtp-transport"); -import BluebirdPromise = require("bluebird"); - -export class MailSender implements IMailSender { - private transporter: Nodemailer.Transporter; - - constructor(options: NodemailerDirectTransport.DirectOptions | - NodemailerSmtpTransport.SmtpOptions, nodemailer: typeof Nodemailer) { - this.transporter = nodemailer.createTransport(options); - } - - verify(): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - that.transporter.verify(function (error: Error, success: any) { - if (error) { - reject(new Error("Unable to connect to SMTP server. \ - Please check the service is running and your credentials are correct.")); - return; - } - resolve(); - }); - }); - } - - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - that.transporter.sendMail(mailOptions, (error: Error, - data: Nodemailer.SentMessageInfo) => { - if (error) { - reject(new Error("Error while sending email: " + error.message)); - return; - } - resolve(); - }); - }); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/notifiers/MailSenderBuilder.spec.ts b/themes/black/server/src/lib/notifiers/MailSenderBuilder.spec.ts deleted file mode 100644 index 41e0db426..000000000 --- a/themes/black/server/src/lib/notifiers/MailSenderBuilder.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ - -import { MailSenderBuilder } from ".//MailSenderBuilder"; -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); -import Assert = require("assert"); - -describe("notifiers/MailSenderBuilder", function() { - let createTransportStub: Sinon.SinonStub; - beforeEach(function() { - createTransportStub = Sinon.stub(Nodemailer, "createTransport"); - }); - - afterEach(function() { - createTransportStub.restore(); - }); - - it("should create a email mail sender", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildEmail({ - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }); - Assert.equal(createTransportStub.getCall(0).args[0].auth.user, "user_gmail"); - Assert.equal(createTransportStub.getCall(0).args[0].auth.pass, "pass_gmail"); - Assert.equal(createTransportStub.getCall(0).args[0].service, "gmail"); - }); - - describe("build smtp mail sender", function() { - it("should create a smtp mail sender with authenticated user", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildSmtp({ - host: "mail.example.com", - password: "password", - port: 25, - secure: true, - username: "user", - sender: "admin@example.com" - }); - Assert.deepStrictEqual(createTransportStub.getCall(0).args[0], { - host: "mail.example.com", - auth: { - pass: "password", - user: "user" - }, - port: 25, - secure: true, - }); - }); - - it("should create a smtp mail sender with anonymous user", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildSmtp({ - host: "mail.example.com", - port: 25, - secure: true, - sender: "admin@example.com" - }); - Assert.deepStrictEqual(createTransportStub.getCall(0).args[0], { - host: "mail.example.com", - port: 25, - secure: true, - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/notifiers/MailSenderBuilder.ts b/themes/black/server/src/lib/notifiers/MailSenderBuilder.ts deleted file mode 100644 index 1d06be52f..000000000 --- a/themes/black/server/src/lib/notifiers/MailSenderBuilder.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import { IMailSenderBuilder } from "./IMailSenderBuilder"; -import { MailSender } from "./MailSender"; -import Nodemailer = require("nodemailer"); -import NodemailerSmtpTransport = require("nodemailer-smtp-transport"); -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class MailSenderBuilder implements IMailSenderBuilder { - private nodemailer: typeof Nodemailer; - - constructor(nodemailer: typeof Nodemailer) { - this.nodemailer = nodemailer; - } - - buildEmail(options: EmailNotifierConfiguration): IMailSender { - const emailOptions = { - service: options.service, - auth: { - user: options.username, - pass: options.password - } - }; - return new MailSender(emailOptions, this.nodemailer); - } - - buildSmtp(options: SmtpNotifierConfiguration): IMailSender { - const smtpOptions: NodemailerSmtpTransport.SmtpOptions = { - host: options.host, - port: options.port, - secure: options.secure, // upgrade later with STARTTLS - }; - - if (options.username && options.password) { - smtpOptions.auth = { - user: options.username, - pass: options.password - }; - } - - return new MailSender(smtpOptions, this.nodemailer); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts b/themes/black/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts deleted file mode 100644 index 5b76f6e56..000000000 --- a/themes/black/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { IMailSenderBuilder } from "../../../src/lib/notifiers/IMailSenderBuilder"; -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); -import { IMailSender } from "../../../src/lib/notifiers/IMailSender"; -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../../../src/lib/configuration/schema/NotifierConfiguration"; - -export class MailSenderBuilderStub implements IMailSenderBuilder { - buildEmailStub: Sinon.SinonStub; - buildSmtpStub: Sinon.SinonStub; - - constructor() { - this.buildEmailStub = Sinon.stub(); - this.buildSmtpStub = Sinon.stub(); - } - - buildEmail(options: EmailNotifierConfiguration): IMailSender { - return this.buildEmailStub(options); - } - - buildSmtp(options: SmtpNotifierConfiguration): IMailSender { - return this.buildSmtpStub(options); - } - -} \ No newline at end of file diff --git a/themes/black/server/src/lib/notifiers/MailSenderStub.spec.ts b/themes/black/server/src/lib/notifiers/MailSenderStub.spec.ts deleted file mode 100644 index d57c458fc..000000000 --- a/themes/black/server/src/lib/notifiers/MailSenderStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { IMailSender } from "../../../src/lib/notifiers/IMailSender"; -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); - -export class MailSenderStub implements IMailSender { - sendStub: Sinon.SinonStub; - - constructor() { - this.sendStub = Sinon.stub(); - } - - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise { - return this.sendStub(mailOptions); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/notifiers/NotifierFactory.spec.ts b/themes/black/server/src/lib/notifiers/NotifierFactory.spec.ts deleted file mode 100644 index f15e7667f..000000000 --- a/themes/black/server/src/lib/notifiers/NotifierFactory.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ - -import * as sinon from "sinon"; -import * as BluebirdPromise from "bluebird"; -import * as assert from "assert"; - -import { NotifierFactory } from "./NotifierFactory"; -import { EmailNotifier } from "./EmailNotifier"; -import { SmtpNotifier } from "./SmtpNotifier"; -import { MailSenderBuilderStub } from "./MailSenderBuilderStub.spec"; - - -describe("notifiers/NotifierFactory", function () { - let mailSenderBuilderStub: MailSenderBuilderStub; - it("should build a Email Notifier", function () { - const options = { - email: { - username: "abc", - password: "password", - sender: "admin@example.com", - service: "gmail" - } - }; - mailSenderBuilderStub = new MailSenderBuilderStub(); - assert(NotifierFactory.build(options, mailSenderBuilderStub) instanceof EmailNotifier); - }); - - it("should build a SMTP Notifier", function () { - const options = { - smtp: { - username: "user", - password: "pass", - secure: true, - host: "localhost", - port: 25, - sender: "admin@example.com" - } - }; - - mailSenderBuilderStub = new MailSenderBuilderStub(); - assert(NotifierFactory.build(options, mailSenderBuilderStub) instanceof SmtpNotifier); - }); -}); diff --git a/themes/black/server/src/lib/notifiers/NotifierFactory.ts b/themes/black/server/src/lib/notifiers/NotifierFactory.ts deleted file mode 100644 index a89155feb..000000000 --- a/themes/black/server/src/lib/notifiers/NotifierFactory.ts +++ /dev/null @@ -1,33 +0,0 @@ - -import { NotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; -import Nodemailer = require("nodemailer"); -import { INotifier } from "./INotifier"; - -import { FileSystemNotifier } from "./FileSystemNotifier"; -import { EmailNotifier } from "./EmailNotifier"; -import { SmtpNotifier } from "./SmtpNotifier"; -import { IMailSender } from "./IMailSender"; -import { IMailSenderBuilder } from "./IMailSenderBuilder"; - -export class NotifierFactory { - static build(options: NotifierConfiguration, mailSenderBuilder: IMailSenderBuilder): INotifier { - if ("email" in options) { - const mailSender = mailSenderBuilder.buildEmail(options.email); - return new EmailNotifier(options.email, mailSender); - } - else if ("smtp" in options) { - const mailSender = mailSenderBuilder.buildSmtp(options.smtp); - return new SmtpNotifier(options.smtp, mailSender); - } - else if ("filesystem" in options) { - return new FileSystemNotifier(options.filesystem); - } - else { - throw new Error("No available notifier option detected."); - } - } -} - - - - diff --git a/themes/black/server/src/lib/notifiers/NotifierStub.spec.ts b/themes/black/server/src/lib/notifiers/NotifierStub.spec.ts deleted file mode 100644 index f99231b50..000000000 --- a/themes/black/server/src/lib/notifiers/NotifierStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); - -import { INotifier } from "./INotifier"; - -export class NotifierStub implements INotifier { - notifyStub: Sinon.SinonStub; - - constructor() { - this.notifyStub = Sinon.stub(); - } - - notify(to: string, subject: string, link: string): BluebirdPromise { - return this.notifyStub(to, subject, link); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/notifiers/SmtpNotifier.ts b/themes/black/server/src/lib/notifiers/SmtpNotifier.ts deleted file mode 100644 index f93a6d4a0..000000000 --- a/themes/black/server/src/lib/notifiers/SmtpNotifier.ts +++ /dev/null @@ -1,30 +0,0 @@ - - -import * as BluebirdPromise from "bluebird"; - -import { IMailSender } from "./IMailSender"; -import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier"; -import { SmtpNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class SmtpNotifier extends AbstractEmailNotifier { - private mailSender: IMailSender; - private sender: string; - - constructor(options: SmtpNotifierConfiguration, - mailSender: IMailSender) { - super(); - this.mailSender = mailSender; - this.sender = options.sender; - } - - sendEmail(to: string, subject: string, content: string) { - const mailOptions = { - from: this.sender, - to: to, - subject: subject, - html: content - }; - const that = this; - return this.mailSender.send(mailOptions); - } -} diff --git a/themes/black/server/src/lib/regulation/IRegulator.ts b/themes/black/server/src/lib/regulation/IRegulator.ts deleted file mode 100644 index c49425b24..000000000 --- a/themes/black/server/src/lib/regulation/IRegulator.ts +++ /dev/null @@ -1,6 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -export interface IRegulator { - mark(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise; - regulate(userId: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/regulation/Regulator.spec.ts b/themes/black/server/src/lib/regulation/Regulator.spec.ts deleted file mode 100644 index f9c6e6086..000000000 --- a/themes/black/server/src/lib/regulation/Regulator.spec.ts +++ /dev/null @@ -1,186 +0,0 @@ - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -import { Regulator } from "./Regulator"; -import MockDate = require("mockdate"); -import exceptions = require("../Exceptions"); -import { UserDataStoreStub } from "../storage/UserDataStoreStub.spec"; - -describe("regulation/Regulator", function () { - const USER1 = "USER1"; - const USER2 = "USER2"; - let userDataStoreStub: UserDataStoreStub; - - beforeEach(function () { - userDataStoreStub = new UserDataStoreStub(); - const dataStore: { [userId: string]: { userId: string, date: Date, isAuthenticationSuccessful: boolean }[] } = { - [USER1]: [], - [USER2]: [] - }; - - userDataStoreStub.saveAuthenticationTraceStub.callsFake(function (userId, isAuthenticationSuccessful) { - dataStore[userId].unshift({ - userId: userId, - date: new Date(), - isAuthenticationSuccessful: isAuthenticationSuccessful, - }); - return BluebirdPromise.resolve(); - }); - - userDataStoreStub.retrieveLatestAuthenticationTracesStub.callsFake(function (userId, count) { - const ret = (dataStore[userId].length <= count) ? dataStore[userId] : dataStore[userId].slice(0, 3); - return BluebirdPromise.resolve(ret); - }); - }); - - afterEach(function () { - MockDate.reset(); - }); - - function markAuthenticationAt(regulator: Regulator, user: string, time: string, success: boolean) { - MockDate.set(time); - return regulator.mark(user, success); - } - - it("should mark 2 authentication and regulate (accept)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 10, 10); - - return regulator.mark(USER1, false) - .then(function () { - return regulator.mark(USER1, true); - }) - .then(function () { - return regulator.regulate(USER1); - }); - }); - - it("should mark 3 authentications and regulate (reject)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 10, 10); - - return regulator.mark(USER1, false) - .then(function () { - return regulator.mark(USER1, false); - }) - .then(function () { - return regulator.mark(USER1, false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { return BluebirdPromise.reject(new Error("should not be here!")); }) - .catch(exceptions.AuthenticationRegulationError, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should mark 1 failed, 1 successful and 1 failed authentications within minimum time and regulate (accept)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 60, 30); - - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:10", true); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:20", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:30", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:39", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here!")); - }, - function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should regulate user if number of failures is greater than 3 in allowed time lapse", function () { - function markAuthentications(regulator: Regulator, user: string) { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:45", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:01:05", false); - }) - .then(function () { - return regulator.regulate(user); - }); - } - - const regulator1 = new Regulator(userDataStoreStub, 3, 60, 60); - const regulator2 = new Regulator(userDataStoreStub, 3, 2 * 60, 60); - - const p1 = markAuthentications(regulator1, USER1); - const p2 = markAuthentications(regulator2, USER2); - - return BluebirdPromise.join(p1, p2) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here...")); - }, function () { - Assert(p1.isFulfilled()); - Assert(p2.isRejected()); - }); - }); - - it("should user wait after regulation to authenticate again", function () { - function markAuthentications(regulator: Regulator, user: string) { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:10", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:15", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:25", false); - }) - .then(function () { - MockDate.set("1/2/2000 00:00:54"); - return regulator.regulate(user); - }) - .then(function () { - return BluebirdPromise.reject(new Error("should fail at this time")); - }, function () { - MockDate.set("1/2/2000 00:00:56"); - return regulator.regulate(user); - }); - } - - const regulator = new Regulator(userDataStoreStub, 4, 30, 30); - return markAuthentications(regulator, USER1); - }); - - it("should disable regulation when max_retries is set to 0", function () { - const maxRetries = 0; - const regulator = new Regulator(userDataStoreStub, maxRetries, 60, 30); - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:10", false); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:15", false); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:25", false); - }) - .then(function () { - MockDate.set("1/2/2000 00:00:26"); - return regulator.regulate(USER1); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/regulation/Regulator.ts b/themes/black/server/src/lib/regulation/Regulator.ts deleted file mode 100644 index 1037a6a17..000000000 --- a/themes/black/server/src/lib/regulation/Regulator.ts +++ /dev/null @@ -1,55 +0,0 @@ - -import * as BluebirdPromise from "bluebird"; -import exceptions = require("../Exceptions"); -import { IUserDataStore } from "../storage/IUserDataStore"; -import { AuthenticationTraceDocument } from "../storage/AuthenticationTraceDocument"; -import { IRegulator } from "./IRegulator"; - -export class Regulator implements IRegulator { - private userDataStore: IUserDataStore; - private banTime: number; - private findTime: number; - private maxRetries: number; - - constructor(userDataStore: any, maxRetries: number, findTime: number, banTime: number) { - this.userDataStore = userDataStore; - this.banTime = banTime; - this.findTime = findTime; - this.maxRetries = maxRetries; - } - - // Mark authentication - mark(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - return this.userDataStore.saveAuthenticationTrace(userId, isAuthenticationSuccessful); - } - - regulate(userId: string): BluebirdPromise { - const that = this; - - if (that.maxRetries <= 0) return BluebirdPromise.resolve(); - - return this.userDataStore.retrieveLatestAuthenticationTraces(userId, that.maxRetries) - .then((docs: AuthenticationTraceDocument[]) => { - // less than the max authorized number of authentication in time range, thus authorizing access - if (docs.length < that.maxRetries) return BluebirdPromise.resolve(); - - const numberOfFailedAuth = docs - .map(function (d: AuthenticationTraceDocument) { return d.isAuthenticationSuccessful == false ? 1 : 0; }) - .reduce(function (acc, v) { return acc + v; }, 0); - - if (numberOfFailedAuth < this.maxRetries) return BluebirdPromise.resolve(); - - const newestDocument = docs[0]; - const oldestDocument = docs[that.maxRetries - 1]; - - const authenticationsTimeRangeInSeconds = (newestDocument.date.getTime() - oldestDocument.date.getTime()) / 1000; - const tooManyAuthInTimelapse = (authenticationsTimeRangeInSeconds < this.findTime); - const stillInBannedTimeRange = (new Date(new Date().getTime() - this.banTime * 1000) < newestDocument.date); - - if (tooManyAuthInTimelapse && stillInBannedTimeRange) - throw new exceptions.AuthenticationRegulationError("Max number of authentication. Please retry in few minutes."); - - return BluebirdPromise.resolve(); - }); - } -} diff --git a/themes/black/server/src/lib/regulation/RegulatorStub.spec.ts b/themes/black/server/src/lib/regulation/RegulatorStub.spec.ts deleted file mode 100644 index ca8a00fb1..000000000 --- a/themes/black/server/src/lib/regulation/RegulatorStub.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); -import { IRegulator } from "./IRegulator"; - - -export class RegulatorStub implements IRegulator { - markStub: Sinon.SinonStub; - regulateStub: Sinon.SinonStub; - - constructor() { - this.markStub = Sinon.stub(); - this.regulateStub = Sinon.stub(); - } - - mark(userId: string, isAuthenticationSuccessful: boolean): Bluebird { - return this.markStub(userId, isAuthenticationSuccessful); - } - - regulate(userId: string): Bluebird { - return this.regulateStub(userId); - } -} diff --git a/themes/black/server/src/lib/routes/error/401/get.spec.ts b/themes/black/server/src/lib/routes/error/401/get.spec.ts deleted file mode 100644 index 9fdac9c3c..000000000 --- a/themes/black/server/src/lib/routes/error/401/get.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get401 from "./get"; -import { ServerVariables } from "../../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../../ServerVariablesMockBuilder.spec"; - -describe("routes/error/401/get", function () { - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let req: any; - let res: any; - let renderSpy: Sinon.SinonSpy; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - - renderSpy = Sinon.spy(); - req = { - headers: {} - }; - res = { - render: renderSpy - }; - }); - - it("should set redirection url to the default redirection url", function () { - vars.config.default_redirection_url = "http://default-redirection"; - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: "http://default-redirection" - })); - }); - }); - - it("should set redirection url to the referer", function () { - req.headers["referer"] = "http://redirection"; - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: "http://redirection" - })); - }); - }); - - it("should render without redirecting the user", function () { - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: undefined - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/error/401/get.ts b/themes/black/server/src/lib/routes/error/401/get.ts deleted file mode 100644 index ca4a3963d..000000000 --- a/themes/black/server/src/lib/routes/error/401/get.ts +++ /dev/null @@ -1,15 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import redirector from "../redirector"; -import { ServerVariables } from "../../../ServerVariables"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - const redirectionUrl = redirector(req, vars); - res.render("errors/401", { - redirection_url: redirectionUrl - }); - return BluebirdPromise.resolve(); - }; -} diff --git a/themes/black/server/src/lib/routes/error/403/get.spec.ts b/themes/black/server/src/lib/routes/error/403/get.spec.ts deleted file mode 100644 index 22eb84853..000000000 --- a/themes/black/server/src/lib/routes/error/403/get.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get403 from "./get"; -import { ServerVariables } from "../../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../../ServerVariablesMockBuilder.spec"; - -describe("routes/error/403/get", function () { - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let req: any; - let res: any; - let renderSpy: Sinon.SinonSpy; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - - renderSpy = Sinon.spy(); - req = { - headers: {} - }; - res = { - render: renderSpy - }; - }); - - it("should set redirection url to the default redirection url", function () { - vars.config.default_redirection_url = "http://default-redirection"; - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: "http://default-redirection" - })); - }); - }); - - it("should set redirection url to the referer", function () { - req.headers["referer"] = "http://redirection"; - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: "http://redirection" - })); - }); - }); - - it("should render without redirecting the user", function () { - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: undefined - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/error/403/get.ts b/themes/black/server/src/lib/routes/error/403/get.ts deleted file mode 100644 index 3ab0319e5..000000000 --- a/themes/black/server/src/lib/routes/error/403/get.ts +++ /dev/null @@ -1,15 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import redirector from "../redirector"; -import { ServerVariables } from "../../../ServerVariables"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - const redirectionUrl = redirector(req, vars); - res.render("errors/403", { - redirection_url: redirectionUrl - }); - return BluebirdPromise.resolve(); - }; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/error/404/get.spec.ts b/themes/black/server/src/lib/routes/error/404/get.spec.ts deleted file mode 100644 index 73e4e6cef..000000000 --- a/themes/black/server/src/lib/routes/error/404/get.spec.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get404 from "./get"; - -describe("routes/error/404/get", function () { - it("should render the page", function () { - const req = {} as Express.Request; - const res = { - render: Sinon.stub() - }; - - return Get404(req, res as any) - .then(function () { - Assert(res.render.calledOnce); - Assert(res.render.calledWith("errors/404")); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/error/404/get.ts b/themes/black/server/src/lib/routes/error/404/get.ts deleted file mode 100644 index 6693b6fc8..000000000 --- a/themes/black/server/src/lib/routes/error/404/get.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); - -export default function (req: express.Request, res: express.Response): BluebirdPromise { - res.render("errors/404"); - return BluebirdPromise.resolve(); -} \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/error/redirector.ts b/themes/black/server/src/lib/routes/error/redirector.ts deleted file mode 100644 index b1a3ccc11..000000000 --- a/themes/black/server/src/lib/routes/error/redirector.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Express = require("express"); -import { ServerVariables } from "../../ServerVariables"; - -export default function (req: Express.Request, vars: ServerVariables): string { - let redirectionUrl: string; - - if (req.headers && req.headers["referer"]) - redirectionUrl = "" + req.headers["referer"]; - else if (vars.config.default_redirection_url) - redirectionUrl = vars.config.default_redirection_url; - - return redirectionUrl; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/firstfactor/get.ts b/themes/black/server/src/lib/routes/firstfactor/get.ts deleted file mode 100644 index d94f656c8..000000000 --- a/themes/black/server/src/lib/routes/firstfactor/get.ts +++ /dev/null @@ -1,72 +0,0 @@ - -import express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import Util = require("util"); -import { ServerVariables } from "../../ServerVariables"; -import { SafeRedirector } from "../../utils/SafeRedirection"; -import { Level } from "../../authentication/Level"; - -function getRedirectParam( - req: express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -function redirectToSecondFactorPage( - req: express.Request, - res: express.Response) { - - const redirectUrl = getRedirectParam(req); - if (!redirectUrl) - res.redirect(Endpoints.SECOND_FACTOR_GET); - else - res.redirect( - Util.format("%s?%s=%s", - Endpoints.SECOND_FACTOR_GET, - Constants.REDIRECT_QUERY_PARAM, - redirectUrl)); -} - -function redirectToService( - req: express.Request, - res: express.Response, - redirector: SafeRedirector) { - const redirectUrl = getRedirectParam(req); - if (!redirectUrl) { - res.redirect(Endpoints.LOGGED_IN); - } else { - redirector.redirectOrElse(res, redirectUrl, Endpoints.LOGGED_IN); - } -} - -function renderFirstFactor( - res: express.Response) { - - res.render("firstfactor", { - first_factor_post_endpoint: Endpoints.FIRST_FACTOR_POST, - reset_password_request_endpoint: Endpoints.RESET_PASSWORD_REQUEST_GET - }); -} - -export default function ( - vars: ServerVariables) { - - const redirector = new SafeRedirector(vars.config.session.domain); - return function (req: express.Request, res: express.Response): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (authSession.authentication_level == Level.ONE_FACTOR) { - redirectToSecondFactorPage(req, res); - } else if (authSession.authentication_level == Level.TWO_FACTOR) { - redirectToService(req, res, redirector); - } else { - renderFirstFactor(res); - } - resolve(); - }); - }; -} diff --git a/themes/black/server/src/lib/routes/firstfactor/post.spec.ts b/themes/black/server/src/lib/routes/firstfactor/post.spec.ts deleted file mode 100644 index e1d078cdd..000000000 --- a/themes/black/server/src/lib/routes/firstfactor/post.spec.ts +++ /dev/null @@ -1,136 +0,0 @@ - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import FirstFactorPost = require("./post"); -import exceptions = require("../../Exceptions"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import Endpoints = require("../../../../../shared/api"); -import AuthenticationRegulatorMock = require("../../regulation/RegulatorStub.spec"); -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; - -describe("routes/firstfactor/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let emails: string[]; - let groups: string[]; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let authSession: AuthenticationSession; - - beforeEach(function () { - emails = ["test_ok@example.com"]; - groups = ["group1", "group2" ]; - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - mocks.authorizer.authorizationMock.returns(true); - mocks.regulator.regulateStub.returns(BluebirdPromise.resolve()); - mocks.regulator.markStub.returns(BluebirdPromise.resolve()); - - req = { - originalUrl: "/api/firstfactor", - body: { - username: "username", - password: "password" - }, - query: { - redirect: "http://redirect.url" - }, - session: { - cookie: {} - }, - headers: { - host: "home.example.com" - } - }; - - res = ExpressMock.ResponseMock(); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - it("should reply with 204 if success", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal("username", authSession.userid); - Assert(res.send.calledOnce); - }); - }); - - describe("keep me logged in", () => { - beforeEach(() => { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - req.body.keepMeLoggedIn = "true"; - return FirstFactorPost.default(vars)(req as any, res as any); - }); - - it("should set keep_me_logged_in session variable to true", function () { - Assert.equal(authSession.keep_me_logged_in, true); - }); - - it("should set cookie maxAge to one year", function () { - Assert.equal(req.session.cookie.maxAge, 365 * 24 * 60 * 60 * 1000); - }); - }); - - it("should retrieve email from LDAP", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve([{ mail: ["test@example.com"] }])); - return FirstFactorPost.default(vars)(req as any, res as any); - }); - - it("should set first email address as user session variable", function () { - const emails = ["test_ok@example.com"]; - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal("test_ok@example.com", authSession.email); - }); - }); - - it("should return error message when LDAP authenticator throws", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.reject(new exceptions.LdapBindError("Bad credentials"))); - - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.equal(mocks.regulator.markStub.getCall(0).args[0], "username"); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); - - it("should return error message when regulator rejects authentication", function () { - const err = new exceptions.AuthenticationRegulationError("Authentication regulation..."); - mocks.regulator.regulateStub.returns(BluebirdPromise.reject(err)); - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); -}); - - diff --git a/themes/black/server/src/lib/routes/firstfactor/post.ts b/themes/black/server/src/lib/routes/firstfactor/post.ts deleted file mode 100644 index 565681d6a..000000000 --- a/themes/black/server/src/lib/routes/firstfactor/post.ts +++ /dev/null @@ -1,101 +0,0 @@ - -import Exceptions = require("../../Exceptions"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import Endpoint = require("../../../../../shared/api"); -import ErrorReplies = require("../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import UserMessages = require("../../../../../shared/UserMessages"); -import { ServerVariables } from "../../ServerVariables"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import { GroupsAndEmails } from "../../authentication/backends/GroupsAndEmails"; -import { Level as AuthenticationLevel } from "../../authentication/Level"; -import { Level as AuthorizationLevel } from "../../authorization/Level"; -import { URLDecomposer } from "../../utils/URLDecomposer"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response) - : BluebirdPromise { - const username: string = req.body.username; - const password: string = req.body.password; - const keepMeLoggedIn: boolean = req.body.keepMeLoggedIn && - req.body.keepMeLoggedIn === "true"; - let authSession: AuthenticationSession; - - if (keepMeLoggedIn) { - // Stay connected for 1 year. - vars.logger.debug(req, "User requested to stay logged in for one year."); - req.session.cookie.maxAge = 365 * 24 * 60 * 60 * 1000; - } - - return BluebirdPromise.resolve() - .then(function () { - if (!username || !password) { - return BluebirdPromise.reject(new Error("No username or password.")); - } - vars.logger.info(req, "Starting authentication of user \"%s\"", username); - authSession = AuthenticationSessionHandler.get(req, vars.logger); - return vars.regulator.regulate(username); - }) - .then(function () { - vars.logger.info(req, "No regulation applied."); - return vars.usersDatabase.checkUserPassword(username, password); - }) - .then(function (groupsAndEmails: GroupsAndEmails) { - vars.logger.info(req, - "LDAP binding successful. Retrieved information about user are %s", - JSON.stringify(groupsAndEmails)); - authSession.userid = username; - authSession.keep_me_logged_in = keepMeLoggedIn; - authSession.authentication_level = AuthenticationLevel.ONE_FACTOR; - const redirectUrl: string = req.query[Constants.REDIRECT_QUERY_PARAM] !== "undefined" - // Fuck, don't know why it is a string! - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : ""; - - const emails: string[] = groupsAndEmails.emails; - const groups: string[] = groupsAndEmails.groups; - const decomposition = URLDecomposer.fromUrl(redirectUrl); - const authorizationLevel = (decomposition) - ? vars.authorizer.authorization( - {domain: decomposition.domain, resource: decomposition.path}, - {user: username, groups: groups}) - : AuthorizationLevel.TWO_FACTOR; - - if (emails.length > 0) - authSession.email = emails[0]; - authSession.groups = groups; - - vars.logger.debug(req, "Mark successful authentication to regulator."); - vars.regulator.mark(username, true); - - if (authorizationLevel <= AuthorizationLevel.ONE_FACTOR) { - let newRedirectionUrl: string = redirectUrl; - if (!newRedirectionUrl) - newRedirectionUrl = Endpoint.LOGGED_IN; - res.send({ - redirect: newRedirectionUrl - }); - vars.logger.debug(req, "Redirect to '%s'", redirectUrl); - } - else { - let newRedirectUrl = Endpoint.SECOND_FACTOR_GET; - if (redirectUrl) { - newRedirectUrl += "?" + Constants.REDIRECT_QUERY_PARAM + "=" - + redirectUrl; - } - vars.logger.debug(req, "Redirect to '%s'", newRedirectUrl); - res.send({ - redirect: newRedirectUrl - }); - } - return BluebirdPromise.resolve(); - }) - .catch(Exceptions.LdapBindError, function (err: Error) { - vars.regulator.mark(username, false); - return ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.OPERATION_FAILED)(err); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.OPERATION_FAILED)); - }; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/loggedin/get.ts b/themes/black/server/src/lib/routes/loggedin/get.ts deleted file mode 100644 index 283a041b1..000000000 --- a/themes/black/server/src/lib/routes/loggedin/get.ts +++ /dev/null @@ -1,23 +0,0 @@ -import Express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; -import ErrorReplies = require("../../ErrorReplies"); - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - res.render("already-logged-in", { - logout_endpoint: Endpoints.LOGOUT_GET, - username: authSession.userid, - redirection_url: vars.config.default_redirection_url - }); - resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - } - - return handler; -} diff --git a/themes/black/server/src/lib/routes/logout/get.ts b/themes/black/server/src/lib/routes/logout/get.ts deleted file mode 100644 index 4d5112146..000000000 --- a/themes/black/server/src/lib/routes/logout/get.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import express = require("express"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import { ServerVariables } from "../../ServerVariables"; - -function getRedirectParam(req: express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -export default function (vars: ServerVariables) { - return function(req: express.Request, res: express.Response) { - const redirect_param = getRedirectParam(req); - const redirect_url = redirect_param || "/"; - AuthenticationSessionHandler.reset(req); - res.redirect(redirect_url); - }; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/password-reset/constants.ts b/themes/black/server/src/lib/routes/password-reset/constants.ts deleted file mode 100644 index 5c639e92a..000000000 --- a/themes/black/server/src/lib/routes/password-reset/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const CHALLENGE = "reset-password"; \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/password-reset/form/post.spec.ts b/themes/black/server/src/lib/routes/password-reset/form/post.spec.ts deleted file mode 100644 index ed029c906..000000000 --- a/themes/black/server/src/lib/routes/password-reset/form/post.spec.ts +++ /dev/null @@ -1,122 +0,0 @@ - -import PasswordResetFormPost = require("./post"); -import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../types/AuthenticationSession"; -import { UserDataStore } from "../../../storage/UserDataStore"; -import Sinon = require("sinon"); -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../ServerVariables"; -import { Level } from "../../../authentication/Level"; - -describe("routes/password-reset/form/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = { - originalUrl: "/api/password-reset", - body: { - userid: "user" - }, - session: {}, - headers: { - host: "localhost" - } - }; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - - mocks.config.authentication_backend.ldap = { - url: "ldap://ldapjs", - mail_attribute: "mail", - user: "user", - password: "password", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "user", - group_name_attribute: "cn", - groups_filter: "groups" - }; - - res = ExpressMock.ResponseMock(); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - authSession.userid = "user"; - authSession.email = "user@example.com"; - authSession.authentication_level = Level.ONE_FACTOR; - }); - - describe("test reset password post", () => { - it("should update the password and reset auth_session for reauthentication", function () { - req.body = {}; - req.body.password = "new-password"; - - mocks.usersDatabase.updatePasswordStub.returns(BluebirdPromise.resolve()); - - authSession.identity_check = { - userid: "user", - challenge: "reset-password" - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }).then(function (_authSession) { - Assert.equal(res.status.getCall(0).args[0], 204); - Assert.equal(_authSession.authentication_level, Level.NOT_AUTHENTICATED); - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if identity_challenge does not exist", function () { - authSession.identity_check = { - userid: "user", - challenge: undefined - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "An error occurred during password reset. Your password has not been changed." - }); - }); - }); - - it("should fail when ldap fails", function () { - req.body = {}; - req.body.password = "new-password"; - - mocks.usersDatabase.updatePasswordStub - .returns(BluebirdPromise.reject("Internal error with LDAP")); - - authSession.identity_check = { - challenge: "reset-password", - userid: "user" - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "An error occurred during password reset. Your password has not been changed." - }); - return BluebirdPromise.resolve(); - }); - }); - }); -}); diff --git a/themes/black/server/src/lib/routes/password-reset/form/post.ts b/themes/black/server/src/lib/routes/password-reset/form/post.ts deleted file mode 100644 index fccd7471b..000000000 --- a/themes/black/server/src/lib/routes/password-reset/form/post.ts +++ /dev/null @@ -1,50 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); -import exceptions = require("../../../Exceptions"); -import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../types/AuthenticationSession"; -import ErrorReplies = require("../../../ErrorReplies"); -import UserMessages = require("../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../ServerVariables"; - -import Constants = require("./../constants"); - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const newPassword = objectPath.get(req, "body.password"); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.identity_check) { - reject(new Error("No identity check initiated")); - return; - } - - vars.logger.info(req, "User %s wants to reset his/her password.", - authSession.identity_check.userid); - vars.logger.debug(req, "Challenge %s", authSession.identity_check.challenge); - - if (authSession.identity_check.challenge != Constants.CHALLENGE) { - reject(new Error("Bad challenge.")); - return; - } - resolve(); - }) - .then(function () { - return vars.usersDatabase.updatePassword(authSession.identity_check.userid, newPassword); - }) - .then(function () { - vars.logger.info(req, "Password reset for user '%s'", - authSession.identity_check.userid); - AuthenticationSessionHandler.reset(req); - res.status(204); - res.send(); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.RESET_PASSWORD_FAILED)); - }; -} diff --git a/themes/black/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts b/themes/black/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts deleted file mode 100644 index ac6a41754..000000000 --- a/themes/black/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts +++ /dev/null @@ -1,92 +0,0 @@ - -import PasswordResetHandler - from "./PasswordResetHandler"; -import { UserDataStore } from "../../../storage/UserDataStore"; -import Sinon = require("sinon"); -import winston = require("winston"); -import assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../ServerVariables"; - -describe("routes/password-reset/identity/PasswordResetHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = { - originalUrl: "/non-api/xxx", - query: { - userid: "user" - }, - session: { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false - } - }, - headers: { - host: "localhost" - } - }; - - const options = { - inMemoryOnly: true - }; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - mocks.userDataStore.saveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - res = ExpressMock.ResponseMock(); - }); - - describe("test reset password identity pre check", () => { - it("should fail when no userid is provided", function () { - req.query.userid = undefined; - const handler = new PasswordResetHandler(vars.logger, - vars.usersDatabase); - return handler.preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject("It should fail"); - }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if ldap fail", function () { - mocks.usersDatabase.getEmailsStub - .returns(BluebirdPromise.reject("Internal error")); - new PasswordResetHandler(vars.logger, vars.usersDatabase) - .preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should returns identity when ldap replies", function () { - mocks.usersDatabase.getEmailsStub - .returns(BluebirdPromise.resolve(["test@example.com"])); - return new PasswordResetHandler(vars.logger, vars.usersDatabase) - .preValidationInit(req as any); - }); - }); -}); diff --git a/themes/black/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts b/themes/black/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts deleted file mode 100644 index 42ae92cda..000000000 --- a/themes/black/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts +++ /dev/null @@ -1,69 +0,0 @@ -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); - -import exceptions = require("../../../Exceptions"); -import { Identity } from "../../../../../types/Identity"; -import { IdentityValidable } from "../../../IdentityValidable"; -import { PRE_VALIDATION_TEMPLATE } from "../../../IdentityCheckPreValidationTemplate"; -import Constants = require("../constants"); -import { IRequestLogger } from "../../../logging/IRequestLogger"; -import { IUsersDatabase } from "../../../authentication/backends/IUsersDatabase"; - -export const TEMPLATE_NAME = "password-reset-form"; - -export default class PasswordResetHandler implements IdentityValidable { - private logger: IRequestLogger; - private usersDatabase: IUsersDatabase; - - constructor(logger: IRequestLogger, usersDatabase: IUsersDatabase) { - this.logger = logger; - this.usersDatabase = usersDatabase; - } - - challenge(): string { - return Constants.CHALLENGE; - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - const userid: string = - objectPath.get(req, "query.userid"); - return BluebirdPromise.resolve() - .then(function () { - that.logger.debug(req, "User '%s' requested a password reset", userid); - if (!userid) - return BluebirdPromise.reject( - new exceptions.AccessDeniedError("No user id provided")); - - return that.usersDatabase.getEmails(userid); - }) - .then(function (emails: string[]) { - if (!emails && emails.length <= 0) throw new Error("No email found"); - const identity = { - email: emails[0], - userid: userid - }; - return BluebirdPromise.resolve(identity); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject(new exceptions.IdentityError(err.message)); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return BluebirdPromise.resolve(); - } - - postValidationResponse(req: express.Request, res: express.Response) { - res.render(TEMPLATE_NAME); - } - - mailSubject(): string { - return "Reset your password"; - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/password-reset/request/get.ts b/themes/black/server/src/lib/routes/password-reset/request/get.ts deleted file mode 100644 index 8f3ae2b4b..000000000 --- a/themes/black/server/src/lib/routes/password-reset/request/get.ts +++ /dev/null @@ -1,13 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); -import exceptions = require("../../../Exceptions"); - -import Constants = require("./../constants"); - -const TEMPLATE_NAME = "password-reset-request"; - -export default function (req: express.Request, res: express.Response) { - res.render(TEMPLATE_NAME); -} \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/secondfactor/get.spec.ts b/themes/black/server/src/lib/routes/secondfactor/get.spec.ts deleted file mode 100644 index 6c77e1f69..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/get.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import SecondFactorGet from "./get"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; -import Sinon = require("sinon"); -import ExpressMock = require("../../stubs/express.spec"); -import Assert = require("assert"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); - -describe("routes/secondfactor/get", function () { - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false - } - }; - }); - - describe("test rendering", function () { - it("should render second factor page", function () { - req.session.auth.second_factor = false; - return SecondFactorGet(vars)(req as any, res as any) - .then(function () { - Assert(res.render.calledWith("secondfactor")); - return BluebirdPromise.resolve(); - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/secondfactor/get.ts b/themes/black/server/src/lib/routes/secondfactor/get.ts deleted file mode 100644 index 9f6deb4c6..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/get.ts +++ /dev/null @@ -1,28 +0,0 @@ - -import Express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; - -const TEMPLATE_NAME = "secondfactor"; - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response) - : BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - - res.render(TEMPLATE_NAME, { - username: authSession.userid, - totp_identity_start_endpoint: - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - u2f_identity_start_endpoint: - Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET - }); - resolve(); - }); - } - return handler; -} diff --git a/themes/black/server/src/lib/routes/secondfactor/redirect.spec.ts b/themes/black/server/src/lib/routes/secondfactor/redirect.spec.ts deleted file mode 100644 index ea66e6dca..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/redirect.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import Redirect from "./redirect"; -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariablesMockBuilder, ServerVariablesMock } -from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; -import Assert = require("assert"); - -describe("routes/secondfactor/redirect", function() { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - }); - - it("should redirect to default_redirection_url", function() { - vars.config.default_redirection_url = "http://default_redirection_url"; - Redirect(vars)(req as any, res as any) - .then(function() { - Assert(res.json.calledWith({ - redirect: "http://default_redirection_url" - })); - }); - }); - - it("should redirect to /", function() { - Redirect(vars)(req as any, res as any) - .then(function() { - Assert(res.json.calledWith({ - redirect: "/" - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/secondfactor/redirect.ts b/themes/black/server/src/lib/routes/secondfactor/redirect.ts deleted file mode 100644 index 5d84d9ebf..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/redirect.ts +++ /dev/null @@ -1,30 +0,0 @@ - -import express = require("express"); -import objectPath = require("object-path"); -import Endpoints = require("../../../../../shared/api"); -import { ServerVariables } from "../../ServerVariables"; -import BluebirdPromise = require("bluebird"); -import ErrorReplies = require("../../ErrorReplies"); -import UserMessages = require("../../../../../shared/UserMessages"); -import { RedirectionMessage } from "../../../../../shared/RedirectionMessage"; -import Constants = require("../../../../../shared/constants"); - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response) - : BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - let redirectUrl: string = "/"; - if (vars.config.default_redirection_url) { - redirectUrl = vars.config.default_redirection_url; - } - vars.logger.debug(req, "Request redirection to \"%s\".", redirectUrl); - res.json({ - redirect: redirectUrl - } as RedirectionMessage); - return resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - }; -} diff --git a/themes/black/server/src/lib/routes/secondfactor/totp/constants.ts b/themes/black/server/src/lib/routes/secondfactor/totp/constants.ts deleted file mode 100644 index 7b5a1dcfe..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/totp/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export const CHALLENGE = "totp-register"; -export const TEMPLATE_NAME = "totp-register"; - diff --git a/themes/black/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts b/themes/black/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts deleted file mode 100644 index 78b8ea3ea..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts +++ /dev/null @@ -1,116 +0,0 @@ -import Sinon = require("sinon"); -import RegistrationHandler from "./RegistrationHandler"; -import { Identity } from "../../../../../../types/Identity"; -import { UserDataStore } from "../../../../storage/UserDataStore"; -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; -import Assert = require("assert"); - -describe("routes/secondfactor/totp/identity/RegistrationHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - req.session = { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false, - identity_check: { - userid: "user", - challenge: "totp-register" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.saveTOTPSecretStub - .returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - }); - - describe("test totp registration pre validation", function () { - it("should fail if first_factor has not been passed", function () { - req.session.auth.first_factor = false; - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("It should fail")); - }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if userid is missing", function (done) { - req.session.auth.first_factor = false; - req.session.auth.userid = undefined; - - new RegistrationHandler(vars.logger, vars.userDataStore, vars.totpHandler, - vars.config.totp) - .preValidationInit(req as any) - .catch(function (err: Error) { - done(); - }); - }); - - it("should fail if email is missing", function (done) { - req.session.auth.first_factor = false; - req.session.auth.email = undefined; - - new RegistrationHandler(vars.logger, vars.userDataStore, vars.totpHandler, - vars.config.totp) - .preValidationInit(req as any) - .catch(function (err: Error) { - done(); - }); - }); - - it("should succeed if first factor passed, userid and email are provided", - function () { - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .preValidationInit(req as any); - }); - }); - - describe("test totp registration post validation", function () { - it("should generate a secret using userId as label and issuer defined in config", function () { - vars.config.totp = { - issuer: "issuer" - }; - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .postValidationResponse(req as any, res as any) - .then(function() { - Assert(mocks.totpHandler.generateStub.calledWithExactly("user", "issuer")); - }); - }); - }); -}); diff --git a/themes/black/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts b/themes/black/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts deleted file mode 100644 index b39b6d045..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts +++ /dev/null @@ -1,112 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); - -import { Identity } from "../../../../../../types/Identity"; -import { IdentityValidable } from "../../../../IdentityValidable"; -import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate"; -import Constants = require("../constants"); -import Endpoints = require("../../../../../../../shared/api"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import FirstFactorValidator = require("../../../../FirstFactorValidator"); -import { IRequestLogger } from "../../../../logging/IRequestLogger"; -import { IUserDataStore } from "../../../../storage/IUserDataStore"; -import { ITotpHandler } from "../../../../authentication/totp/ITotpHandler"; -import { TOTPSecret } from "../../../../../../types/TOTPSecret"; -import { TotpConfiguration } from "../../../../configuration/schema/TotpConfiguration"; - - -export default class RegistrationHandler implements IdentityValidable { - private logger: IRequestLogger; - private userDataStore: IUserDataStore; - private totp: ITotpHandler; - private configuration: TotpConfiguration; - - constructor(logger: IRequestLogger, - userDataStore: IUserDataStore, - totp: ITotpHandler, configuration: TotpConfiguration) { - this.logger = logger; - this.userDataStore = userDataStore; - this.totp = totp; - this.configuration = configuration; - } - - challenge(): string { - return Constants.CHALLENGE; - } - - private retrieveIdentity(req: express.Request): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - const userid = authSession.userid; - const email = authSession.email; - - if (!(userid && email)) { - return reject(new Error("User ID or email is missing")); - } - - const identity = { - email: email, - userid: userid - }; - return resolve(identity); - }); - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - return FirstFactorValidator.validate(req, this.logger) - .then(function () { - return that.retrieveIdentity(req); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return FirstFactorValidator.validate(req, this.logger); - } - - postValidationResponse(req: express.Request, res: express.Response) - : BluebirdPromise { - const that = this; - let secret: TOTPSecret; - let userId: string; - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - userId = authSession.userid; - - if (authSession.identity_check.challenge != Constants.CHALLENGE - || !userId) - return reject(new Error("Bad challenge.")); - - resolve(); - }) - .then(function () { - secret = that.totp.generate(userId, - that.configuration.issuer); - that.logger.debug(req, "Save the TOTP secret in DB"); - return that.userDataStore.saveTOTPSecret(userId, secret); - }) - .then(function () { - AuthenticationSessionHandler.reset(req); - - res.render(Constants.TEMPLATE_NAME, { - base32_secret: secret.base32, - otpauth_url: secret.otpauth_url, - login_endpoint: Endpoints.FIRST_FACTOR_GET - }); - }) - .catch(ErrorReplies.replyWithError200(req, res, that.logger, UserMessages.OPERATION_FAILED)); - } - - mailSubject(): string { - return "Set up Authelia's one-time password"; - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts b/themes/black/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts deleted file mode 100644 index 70a20d39d..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import Assert = require("assert"); -import Exceptions = require("../../../../Exceptions"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import SignPost = require("./post"); -import { ServerVariables } from "../../../../ServerVariables"; - -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { Level } from "../../../../authentication/Level"; - -describe("routes/secondfactor/totp/sign/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let authSession: AuthenticationSession; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - const app_get = Sinon.stub(); - req = { - originalUrl: "/api/totp-register", - app: {}, - body: { - token: "abc" - }, - session: {}, - query: { - redirect: "http://redirect" - } - }; - res = ExpressMock.ResponseMock(); - - const doc = { - userid: "user", - secret: { - base32: "ABCDEF" - } - }; - mocks.userDataStore.retrieveTOTPSecretStub.returns(BluebirdPromise.resolve(doc)); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - authSession.userid = "user"; - authSession.authentication_level = Level.ONE_FACTOR; - }); - - - it("should send status code 200 when totp is valid", function () { - mocks.totpHandler.validateStub.returns(true); - return SignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(authSession.authentication_level, Level.TWO_FACTOR); - return BluebirdPromise.resolve(); - }); - }); - - it("should send error message when totp is not valid", function () { - mocks.totpHandler.validateStub.returns(false); - return SignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.notEqual(authSession.authentication_level, Level.TWO_FACTOR); - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); -}); - diff --git a/themes/black/server/src/lib/routes/secondfactor/totp/sign/post.ts b/themes/black/server/src/lib/routes/secondfactor/totp/sign/post.ts deleted file mode 100644 index 34a276d12..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/totp/sign/post.ts +++ /dev/null @@ -1,42 +0,0 @@ -import Bluebird = require("bluebird"); -import Express = require("express"); - -import { TOTPSecretDocument } from "../../../../storage/TOTPSecretDocument"; -import Endpoints = require("../../../../../../../shared/api"); -import Redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; -import { Level } from "../../../../authentication/Level"; - -const UNAUTHORIZED_MESSAGE = "Unauthorized access"; - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response): Bluebird { - let authSession: AuthenticationSession; - const token = req.body.token; - - return new Bluebird(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - vars.logger.info(req, "Initiate TOTP validation for user \"%s\".", authSession.userid); - resolve(); - }) - .then(function () { - return vars.userDataStore.retrieveTOTPSecret(authSession.userid); - }) - .then(function (doc: TOTPSecretDocument) { - if (!vars.totpHandler.validate(token, doc.secret.base32)) - return Bluebird.reject(new Error("Invalid TOTP token.")); - - vars.logger.debug(req, "TOTP validation succeeded."); - authSession.authentication_level = Level.TWO_FACTOR; - Redirect(vars)(req, res); - return Bluebird.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/black/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts b/themes/black/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts deleted file mode 100644 index 7f16c0ee8..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts +++ /dev/null @@ -1,11 +0,0 @@ - -import util = require("util"); -import express = require("express"); - -function extract_app_id(req: express.Request): string { - return util.format("https://%s", req.headers.host); -} - -export = { - extract_app_id: extract_app_id -}; \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts b/themes/black/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts deleted file mode 100644 index a54bfbfe6..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); - -import { Identity } from "../../../../../../types/Identity"; -import RegistrationHandler from "./RegistrationHandler"; -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -describe("routes/secondfactor/u2f/identity/RegistrationHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - req.app = {}; - req.session = { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = Sinon.spy(); - res.json = Sinon.spy(); - res.status = Sinon.spy(); - }); - - describe("test u2f registration check", test_registration_check); - - function test_registration_check() { - it("should fail if first_factor has not been passed", function () { - req.session.auth.first_factor = false; - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if userid is missing", function () { - req.session.auth.first_factor = false; - req.session.auth.userid = undefined; - - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if email is missing", function () { - req.session.auth.first_factor = false; - req.session.auth.email = undefined; - - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should succeed if first factor passed, userid and email are provided", function () { - req.session.auth.first_factor = true; - req.session.auth.email = "admin@example.com"; - req.session.auth.userid = "user"; - return new RegistrationHandler(vars.logger).preValidationInit(req as any); - }); - } -}); diff --git a/themes/black/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts b/themes/black/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts deleted file mode 100644 index bc4713c77..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts +++ /dev/null @@ -1,73 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import objectPath = require("object-path"); - -import { IdentityValidable } from "../../../../IdentityValidable"; -import { Identity } from "../../../../../../types/Identity"; -import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate"; -import FirstFactorValidator = require("../../../../FirstFactorValidator"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { IRequestLogger } from "../../../../logging/IRequestLogger"; - -const CHALLENGE = "u2f-register"; -const MAIL_SUBJECT = "Register your security key with Authelia"; - -const POST_VALIDATION_TEMPLATE_NAME = "u2f-register"; - - -export default class RegistrationHandler implements IdentityValidable { - private logger: IRequestLogger; - - constructor(logger: IRequestLogger) { - this.logger = logger; - } - - challenge(): string { - return CHALLENGE; - } - - private retrieveIdentity(req: express.Request): BluebirdPromise { - const that = this; - return new BluebirdPromise(function(resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - const userid = authSession.userid; - const email = authSession.email; - - if (!(userid && email)) { - return reject(new Error("User ID or email is missing")); - } - - const identity = { - email: email, - userid: userid - }; - return resolve(identity); - }); - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - return FirstFactorValidator.validate(req, this.logger) - .then(function () { - return that.retrieveIdentity(req); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return FirstFactorValidator.validate(req, this.logger); - } - - postValidationResponse(req: express.Request, res: express.Response) { - res.render(POST_VALIDATION_TEMPLATE_NAME); - } - - mailSubject(): string { - return MAIL_SUBJECT; - } -} - diff --git a/themes/black/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts b/themes/black/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts deleted file mode 100644 index de3347a21..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts +++ /dev/null @@ -1,146 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import assert = require("assert"); -import U2FRegisterPost = require("./post"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - - -describe("routes/secondfactor/u2f/register/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - describe("test registration", test_registration); - - - function test_registration() { - it("should save u2f meta and return status code 200", function () { - const expectedStatus = { - keyHandle: "keyHandle", - publicKey: "pbk", - certificate: "cert" - }; - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve(expectedStatus)); - - authSession.register_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { - assert.equal("user", mocks.userDataStore.saveU2FRegistrationStub.getCall(0).args[0]); - assert.equal(authSession.identity_check, undefined); - }); - }); - - it("should return error message on finishRegistration error", function () { - mocks.u2f.checkRegistrationStub.returns({ errorCode: 500 }); - - authSession.register_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when register_request is not provided", function () { - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve()); - authSession.register_request = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when no auth request has been initiated", function () { - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve()); - authSession.register_request = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when identity has not been verified", function () { - authSession.identity_check = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - } -}); - diff --git a/themes/black/server/src/lib/routes/secondfactor/u2f/register/post.ts b/themes/black/server/src/lib/routes/secondfactor/u2f/register/post.ts deleted file mode 100644 index 7296ccbe5..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/u2f/register/post.ts +++ /dev/null @@ -1,64 +0,0 @@ - -import { UserDataStore } from "../../../../storage/UserDataStore"; -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import U2f = require("u2f"); -import { U2FRegistration } from "../../../../../../types/U2FRegistration"; -import redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; - - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appid = u2f_common.extract_app_id(req); - const registrationResponse: U2f.RegistrationData = req.body; - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - const registrationRequest = authSession.register_request; - - if (!registrationRequest) { - return reject(new Error("No registration request")); - } - - if (!authSession.identity_check - || authSession.identity_check.challenge != "u2f-register") { - return reject(new Error("Bad challenge for registration request")); - } - - vars.logger.info(req, "Finishing registration"); - vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest)); - vars.logger.debug(req, "RegistrationResponse = %s", JSON.stringify(registrationResponse)); - - return resolve(vars.u2f.checkRegistration(registrationRequest, registrationResponse)); - }) - .then(function (u2fResult: U2f.RegistrationResult | U2f.Error): BluebirdPromise { - if (objectPath.has(u2fResult, "errorCode")) - return BluebirdPromise.reject(new Error("Error while registering.")); - - const registrationResult: U2f.RegistrationResult = u2fResult as U2f.RegistrationResult; - vars.logger.info(req, "Store registration and reply"); - vars.logger.debug(req, "RegistrationResult = %s", JSON.stringify(registrationResult)); - const registration: U2FRegistration = { - keyHandle: registrationResult.keyHandle, - publicKey: registrationResult.publicKey - }; - return vars.userDataStore.saveU2FRegistration(authSession.userid, appid, registration); - }) - .then(function () { - authSession.identity_check = undefined; - redirect(vars)(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/black/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts b/themes/black/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts deleted file mode 100644 index a207c9109..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts +++ /dev/null @@ -1,86 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import U2FRegisterRequestGet = require("./get"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -describe("routes/secondfactor/u2f/register_request/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - describe("test registration request", () => { - it("should send back the registration request and save it in the session", function () { - const expectedRequest = { - test: "abc" - }; - mocks.u2f.requestStub.returns(BluebirdPromise.resolve(expectedRequest)); - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.deepEqual(expectedRequest, res.json.getCall(0).args[0]); - }); - }); - - it("should return internal error on registration request", function () { - res.send = sinon.spy(); - const user_key_container = {}; - mocks.u2f.requestStub.returns(BluebirdPromise.reject("Internal error")); - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); - - it("should return forbidden if identity has not been verified", function () { - req.session.auth.identity_check = undefined; - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(403, res.status.getCall(0).args[0]); - }); - }); - }); -}); - diff --git a/themes/black/server/src/lib/routes/secondfactor/u2f/register_request/get.ts b/themes/black/server/src/lib/routes/secondfactor/u2f/register_request/get.ts deleted file mode 100644 index f611af933..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/u2f/register_request/get.ts +++ /dev/null @@ -1,43 +0,0 @@ - -import { UserDataStore } from "../../../../storage/UserDataStore"; - -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import U2f = require("u2f"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appid: string = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.identity_check - || authSession.identity_check.challenge != "u2f-register") { - res.status(403); - res.send(); - return reject(new Error("Bad challenge.")); - } - - vars.logger.info(req, "Starting registration for appId '%s'", appid); - return resolve(vars.u2f.request(appid)); - }) - .then(function (registrationRequest: U2f.Request) { - vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest)); - authSession.register_request = registrationRequest; - res.json(registrationRequest); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - - return handler; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts b/themes/black/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts deleted file mode 100644 index 9b137e66d..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts +++ /dev/null @@ -1,101 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import U2FSignPost = require("./post"); -import { ServerVariables } from "../../../../ServerVariables"; -import winston = require("winston"); - -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import ExpressMock = require("../../../../stubs/express.spec"); -import U2FMock = require("../../../../stubs/u2f.spec"); -import U2f = require("u2f"); -import { Level } from "../../../../authentication/Level"; - -describe("routes/secondfactor/u2f/sign/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.app = {}; - req.originalUrl = "/api/xxxx"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req.session = { - auth: { - userid: "user", - authentication_level: Level.ONE_FACTOR, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - it("should return status code 204", function () { - const expectedStatus = { - keyHandle: "keyHandle", - publicKey: "pbk", - certificate: "cert" - }; - mocks.u2f.checkSignatureStub.returns(expectedStatus); - - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({ - registration: { - publicKey: "PUBKEY" - } - })); - - req.session.auth.sign_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FSignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(req.session.auth.authentication_level, Level.TWO_FACTOR); - }); - }); - - it("should return unauthorized error on registration request internal error", function () { - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({ - registration: { - publicKey: "PUBKEY" - } - })); - mocks.u2f.checkSignatureStub.returns({ errorCode: 500 }); - - req.session.auth.sign_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FSignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], - { error: "Operation failed." }); - }); - }); -}); - diff --git a/themes/black/server/src/lib/routes/secondfactor/u2f/sign/post.ts b/themes/black/server/src/lib/routes/secondfactor/u2f/sign/post.ts deleted file mode 100644 index 7ee711c2c..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/u2f/sign/post.ts +++ /dev/null @@ -1,57 +0,0 @@ - -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import { UserDataStore } from "../../../../storage/UserDataStore"; -import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocument"; -import { Winston } from "../../../../../../types/Dependencies"; -import U2f = require("u2f"); -import exceptions = require("../../../../Exceptions"); -import redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import { Level } from "../../../../authentication/Level"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appId = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.sign_request) { - const err = new Error("No sign request"); - ErrorReplies.replyWithError401(req, res, vars.logger)(err); - return reject(err); - } - resolve(); - }) - .then(function () { - const userid = authSession.userid; - return vars.userDataStore.retrieveU2FRegistration(userid, appId); - }) - .then(function (doc: U2FRegistrationDocument): BluebirdPromise { - const signRequest = authSession.sign_request; - const signData: U2f.SignatureData = req.body; - vars.logger.info(req, "Finish authentication"); - return BluebirdPromise.resolve(vars.u2f.checkSignature(signRequest, signData, doc.registration.publicKey)); - }) - .then(function (result: U2f.SignatureResult | U2f.Error): BluebirdPromise { - if (objectPath.has(result, "errorCode")) - return BluebirdPromise.reject(new Error("Error while signing")); - vars.logger.info(req, "Successful authentication"); - authSession.authentication_level = Level.TWO_FACTOR; - redirect(vars)(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - - return handler; -} - diff --git a/themes/black/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts b/themes/black/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts deleted file mode 100644 index dd52b27e0..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import assert = require("assert"); -import U2FSignRequestGet = require("./get"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { Request } from "u2f"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -import { SignMessage } from "../../../../../../../shared/SignMessage"; - -describe("routes/secondfactor/u2f/sign_request/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - it("should send back the sign request and save it in the session", function () { - const expectedRequest: Request = { - version: "U2F_V2", - appId: 'app', - challenge: 'challenge!' - }; - mocks.u2f.requestStub.returns(expectedRequest); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({ - registration: { - keyHandle: "KeyHandle" - } - })); - - return U2FSignRequestGet.default(vars)(req as any, res as any) - .then(() => { - assert.deepEqual(expectedRequest, req.session.auth.sign_request); - assert.deepEqual(expectedRequest, res.json.getCall(0).args[0]); - }); - }); -}); - diff --git a/themes/black/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts b/themes/black/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts deleted file mode 100644 index 9e93dde06..000000000 --- a/themes/black/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts +++ /dev/null @@ -1,42 +0,0 @@ - -import u2f_common = require("../../../secondfactor/u2f/U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocument"; -import exceptions = require("../../../../Exceptions"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appId = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - resolve(); - }) - .then(function () { - return vars.userDataStore.retrieveU2FRegistration(authSession.userid, appId); - }) - .then(function (doc: U2FRegistrationDocument): BluebirdPromise { - if (!doc) - return BluebirdPromise.reject(new exceptions.AccessDeniedError("No U2F registration document found.")); - - const appId: string = u2f_common.extract_app_id(req); - vars.logger.info(req, "Start authentication of app '%s'", appId); - vars.logger.debug(req, "AppId = %s, keyHandle = %s", appId, JSON.stringify(doc.registration.keyHandle)); - - const request = vars.u2f.request(appId, doc.registration.keyHandle); - authSession.sign_request = request; - res.json(request); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/black/server/src/lib/routes/verify/access_control.ts b/themes/black/server/src/lib/routes/verify/access_control.ts deleted file mode 100644 index 136239aeb..000000000 --- a/themes/black/server/src/lib/routes/verify/access_control.ts +++ /dev/null @@ -1,51 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import Util = require("util"); - -import Exceptions = require("../../Exceptions"); - -import { Level as AuthorizationLevel } from "../../authorization/Level"; -import { Level as AuthenticationLevel } from "../../authentication/Level"; -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; - -function isAuthorized( - authorization: AuthorizationLevel, - authentication: AuthenticationLevel): boolean { - - if (authorization == AuthorizationLevel.BYPASS) { - return true; - } else if (authorization == AuthorizationLevel.ONE_FACTOR && - authentication >= AuthenticationLevel.ONE_FACTOR) { - return true; - } else if (authorization == AuthorizationLevel.TWO_FACTOR && - authentication >= AuthenticationLevel.TWO_FACTOR) { - return true; - } - return false; -} - -export default function ( - req: Express.Request, - vars: ServerVariables, - domain: string, resource: string, - user: string, groups: string[], - authenticationLevel: AuthenticationLevel) { - - return new BluebirdPromise(function (resolve, reject) { - const authorizationLevel = vars.authorizer - .authorization({domain, resource}, {user, groups}); - - if (!isAuthorized(authorizationLevel, authenticationLevel)) { - if (authorizationLevel == AuthorizationLevel.DENY) { - reject(new Exceptions.NotAuthorizedError( - Util.format("User %s is not authorized to access %s%s", user, domain, resource))); - return; - } - reject(new Exceptions.NotAuthenticatedError(Util.format( - "User '%s' is not sufficiently authorized to access %s%s.", user, domain, resource))); - return; - } - resolve(); - }); -} \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/verify/get.spec.ts b/themes/black/server/src/lib/routes/verify/get.spec.ts deleted file mode 100644 index 67cf19fb6..000000000 --- a/themes/black/server/src/lib/routes/verify/get.spec.ts +++ /dev/null @@ -1,320 +0,0 @@ - -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import Express = require("express"); -import Sinon = require("sinon"); -import winston = require("winston"); - -import VerifyGet = require("./get"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariables } from "../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../ServerVariablesMockBuilder.spec"; -import { Level } from "../../authentication/Level"; -import { Level as AuthorizationLevel } from "../../authorization/Level"; - -describe("routes/verify/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - req.originalUrl = "/api/xxxx"; - req.query = { - redirect: "undefined" - }; - AuthenticationSessionHandler.reset(req as any); - req.headers["x-original-url"] = "https://secret.example.com/"; - const s = ServerVariablesMockBuilder.build(false); - mocks = s.mocks; - vars = s.variables; - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - describe("with session cookie", function () { - it("should be already authenticated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser"); - Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup"); - Assert.equal(204, res.status.getCall(0).args[0]); - }); - }); - - function test_session(_authSession: AuthenticationSession, status_code: number) { - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert.equal(status_code, res.status.getCall(0).args[0]); - }); - } - - function test_non_authenticated_401(authSession: AuthenticationSession) { - return test_session(authSession, 401); - } - - function test_unauthorized_403(authSession: AuthenticationSession) { - return test_session(authSession, 403); - } - - function test_authorized(authSession: AuthenticationSession) { - return test_session(authSession, 204); - } - - describe("given user tries to access a 2-factor endpoint", function () { - before(function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - }); - - describe("given different cases of session", function () { - it("should not be authenticated when second factor is missing", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: "user", - authentication_level: Level.ONE_FACTOR, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when userid is missing", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: undefined, - authentication_level: Level.TWO_FACTOR, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when level is insufficient", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: "user", - authentication_level: Level.NOT_AUTHENTICATED, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when session has not be initiated", function () { - return test_non_authenticated_401(undefined); - }); - - it("should not be authenticated when domain is not allowed for user", function () { - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - req.headers["x-original-url"] = "https://test.example.com/"; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.DENY); - - return test_unauthorized_403({ - keep_me_logged_in: false, - authentication_level: Level.TWO_FACTOR, - userid: "user", - groups: ["group1", "group2"], - email: undefined, - last_activity_datetime: new Date().getTime() - }); - }); - }); - }); - - describe("given user tries to access a single factor endpoint", function () { - beforeEach(function () { - req.headers["x-original-url"] = "https://redirect.url/"; - }); - - it("should be authenticated when first factor is validated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - authSession.authentication_level = Level.ONE_FACTOR; - authSession.userid = "user1"; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWith(204)); - Assert(res.send.calledOnce); - }); - }); - - it("should be rejected with 401 when not authenticated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - authSession.authentication_level = Level.NOT_AUTHENTICATED; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWith(401)); - }); - }); - }); - - describe("inactivity period", function () { - it("should update last inactivity period on requests on /api/verify", function () { - mocks.config.session.inactivity = 200000; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - const currentTime = new Date().getTime() - 1000; - AuthenticationSessionHandler.reset(req as any); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - authSession.last_activity_datetime = currentTime; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }) - .then(function (authSession) { - Assert(authSession.last_activity_datetime > currentTime); - }); - }); - - it("should reset session when max inactivity period has been reached", function () { - mocks.config.session.inactivity = 1; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - const currentTime = new Date().getTime() - 1000; - AuthenticationSessionHandler.reset(req as any); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - authSession.last_activity_datetime = currentTime; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }) - .then(function (authSession) { - Assert.equal(authSession.authentication_level, Level.NOT_AUTHENTICATED); - Assert.equal(authSession.userid, undefined); - }); - }); - }); - }); - - describe("response type 401 | 302", function() { - it("should return error code 401", function() { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should redirect to provided redirection url", function() { - const REDIRECT_URL = "http://redirection_url.com"; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - req.query["rd"] = REDIRECT_URL; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.redirect.calledWithExactly(REDIRECT_URL)); - }); - }); - }); - - describe("with basic auth", function () { - it("should authenticate correctly", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.returns({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "john"); - Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup"); - Assert.equal(204, res.status.getCall(0).args[0]); - }); - }); - - it("should fail when endpoint is protected by two factors", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.config.access_control.rules = [{ - domain: "secret.example.com", - policy: "two_factor" - }]; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when base64 token is not valid", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic i_m*not_a_base64*token"; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when base64 token has not format user:psswd", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzOmJhZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when bad user password is provided", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when resource is restricted", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - }); -}); - diff --git a/themes/black/server/src/lib/routes/verify/get.ts b/themes/black/server/src/lib/routes/verify/get.ts deleted file mode 100644 index f73861696..000000000 --- a/themes/black/server/src/lib/routes/verify/get.ts +++ /dev/null @@ -1,91 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Express = require("express"); -import Exceptions = require("../../Exceptions"); -import ErrorReplies = require("../../ErrorReplies"); -import { ServerVariables } from "../../ServerVariables"; -import GetWithSessionCookieMethod from "./get_session_cookie"; -import GetWithBasicAuthMethod from "./get_basic_auth"; -import Constants = require("../../../../../shared/constants"); -import ObjectPath = require("object-path"); - -import { AuthenticationSessionHandler } - from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; - -const REMOTE_USER = "Remote-User"; -const REMOTE_GROUPS = "Remote-Groups"; - - -function verifyWithSelectedMethod(req: Express.Request, res: Express.Response, - vars: ServerVariables, authSession: AuthenticationSession) - : () => BluebirdPromise<{ username: string, groups: string[] }> { - return function () { - const authorization: string = "" + req.headers["proxy-authorization"]; - if (authorization && authorization.startsWith("Basic ")) - return GetWithBasicAuthMethod(req, res, vars, authorization); - - return GetWithSessionCookieMethod(req, res, vars, authSession); - }; -} - -function setRedirectHeader(req: Express.Request, res: Express.Response) { - return function () { - const originalUrl = ObjectPath.get( - req, "headers.x-original-url"); - res.set("Redirect", originalUrl); - return BluebirdPromise.resolve(); - }; -} - -function setUserAndGroupsHeaders(res: Express.Response) { - return function (u: { username: string, groups: string[] }) { - res.setHeader(REMOTE_USER, u.username); - res.setHeader(REMOTE_GROUPS, u.groups.join(",")); - return BluebirdPromise.resolve(); - }; -} - -function replyWith200(res: Express.Response) { - return function () { - res.status(204); - res.send(); - }; -} - -function getRedirectParam(req: Express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -export default function (vars: ServerVariables) { - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - let authSession: AuthenticationSession; - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - resolve(); - }) - .then(setRedirectHeader(req, res)) - .then(verifyWithSelectedMethod(req, res, vars, authSession)) - .then(setUserAndGroupsHeaders(res)) - .then(replyWith200(res)) - // The user is authenticated but has restricted access -> 403 - .catch(Exceptions.NotAuthorizedError, - ErrorReplies.replyWithError403(req, res, vars.logger)) - .catch(Exceptions.NotAuthenticatedError, - ErrorReplies.replyWithError401(req, res, vars.logger)) - // The user is not yet authenticated -> 401 - .catch((err) => { - const redirectUrl = getRedirectParam(req); - if (redirectUrl) { - ErrorReplies.redirectTo(redirectUrl, req, res, vars.logger)(err); - } - else { - ErrorReplies.replyWithError401(req, res, vars.logger)(err); - } - }); - }; -} - diff --git a/themes/black/server/src/lib/routes/verify/get_basic_auth.ts b/themes/black/server/src/lib/routes/verify/get_basic_auth.ts deleted file mode 100644 index af23c76c9..000000000 --- a/themes/black/server/src/lib/routes/verify/get_basic_auth.ts +++ /dev/null @@ -1,55 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import ObjectPath = require("object-path"); -import { ServerVariables } from "../../ServerVariables"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; -import AccessControl from "./access_control"; -import { URLDecomposer } from "../../utils/URLDecomposer"; -import { Level } from "../../authentication/Level"; - -export default function (req: Express.Request, res: Express.Response, - vars: ServerVariables, authorizationHeader: string) - : BluebirdPromise<{ username: string, groups: string[] }> { - let username: string; - const uri = ObjectPath.get(req, "headers.x-original-url"); - const urlDecomposition = URLDecomposer.fromUrl(uri); - - return BluebirdPromise.resolve() - .then(() => { - const base64Re = new RegExp("^Basic ((?:[A-Za-z0-9+/]{4})*" + - "(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)$"); - const isTokenValidBase64 = base64Re.test(authorizationHeader); - - if (!isTokenValidBase64) { - return BluebirdPromise.reject(new Error("No valid base64 token found in the header")); - } - - const tokenMatches = authorizationHeader.match(base64Re); - const base64Token = tokenMatches[1]; - const decodedToken = Buffer.from(base64Token, "base64").toString(); - const splittedToken = decodedToken.split(":"); - - if (splittedToken.length != 2) { - return BluebirdPromise.reject(new Error( - "The authorization token is invalid. Expecting 'userid:password'")); - } - - username = splittedToken[0]; - const password = splittedToken[1]; - return vars.usersDatabase.checkUserPassword(username, password); - }) - .then(function (groupsAndEmails) { - return AccessControl(req, vars, urlDecomposition.domain, urlDecomposition.path, - username, groupsAndEmails.groups, Level.ONE_FACTOR) - .then(() => BluebirdPromise.resolve({ - username: username, - groups: groupsAndEmails.groups - })); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject( - new Error("Unable to authenticate the user with basic auth. Cause: " - + err.message)); - }); -} \ No newline at end of file diff --git a/themes/black/server/src/lib/routes/verify/get_session_cookie.ts b/themes/black/server/src/lib/routes/verify/get_session_cookie.ts deleted file mode 100644 index 070344812..000000000 --- a/themes/black/server/src/lib/routes/verify/get_session_cookie.ts +++ /dev/null @@ -1,78 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import Util = require("util"); -import ObjectPath = require("object-path"); - -import Exceptions = require("../../Exceptions"); -import { Configuration } from "../../configuration/schema/Configuration"; -import { ServerVariables } from "../../ServerVariables"; -import { IRequestLogger } from "../../logging/IRequestLogger"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; -import { AuthenticationSessionHandler } - from "../../AuthenticationSessionHandler"; -import AccessControl from "./access_control"; -import { URLDecomposer } from "../../utils/URLDecomposer"; - -function verify_inactivity(req: Express.Request, - authSession: AuthenticationSession, - configuration: Configuration, logger: IRequestLogger) - : BluebirdPromise { - - // If inactivity is not specified, then inactivity timeout does not apply - if (!configuration.session.inactivity || authSession.keep_me_logged_in) { - return BluebirdPromise.resolve(); - } - - const lastActivityTime = authSession.last_activity_datetime; - const currentTime = new Date().getTime(); - authSession.last_activity_datetime = currentTime; - - const inactivityPeriodMs = currentTime - lastActivityTime; - logger.debug(req, "Inactivity period was %s s and max period was %s.", - inactivityPeriodMs / 1000, configuration.session.inactivity / 1000); - if (inactivityPeriodMs < configuration.session.inactivity) { - return BluebirdPromise.resolve(); - } - - logger.debug(req, "Session has been reset after too long inactivity period."); - AuthenticationSessionHandler.reset(req); - return BluebirdPromise.reject(new Error("Inactivity period exceeded.")); -} - -export default function (req: Express.Request, res: Express.Response, - vars: ServerVariables, authSession: AuthenticationSession) - : BluebirdPromise<{ username: string, groups: string[] }> { - - return BluebirdPromise.resolve() - .then(() => { - const username = authSession.userid; - const groups = authSession.groups; - - if (!authSession.userid) { - return BluebirdPromise.reject(new Exceptions.AccessDeniedError( - "userid is missing")); - } - - const originalUrl = ObjectPath.get( - req, "headers.x-original-url"); - const originalUri = - ObjectPath.get(req, "headers.x-original-uri"); - - const d = URLDecomposer.fromUrl(originalUrl); - vars.logger.debug(req, "domain=%s, path=%s, user=%s, groups=%s", d.domain, - d.path, username, groups.join(",")); - return AccessControl(req, vars, d.domain, d.path, username, groups, - authSession.authentication_level); - }) - .then(() => { - return verify_inactivity(req, authSession, - vars.config, vars.logger); - }) - .then(() => { - return BluebirdPromise.resolve({ - username: authSession.userid, - groups: authSession.groups - }); - }); -} \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/AuthenticationTraceDocument.d.ts b/themes/black/server/src/lib/storage/AuthenticationTraceDocument.d.ts deleted file mode 100644 index 69818c055..000000000 --- a/themes/black/server/src/lib/storage/AuthenticationTraceDocument.d.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export interface AuthenticationTraceDocument { - userId: string; - date: Date; - isAuthenticationSuccessful: boolean; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/CollectionFactoryFactory.ts b/themes/black/server/src/lib/storage/CollectionFactoryFactory.ts deleted file mode 100644 index 92b29abfa..000000000 --- a/themes/black/server/src/lib/storage/CollectionFactoryFactory.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ICollectionFactory } from "./ICollectionFactory"; -import { NedbCollectionFactory } from "./nedb/NedbCollectionFactory"; -import { MongoCollectionFactory } from "./mongo/MongoCollectionFactory"; -import { IMongoClient } from "../connectors/mongo/IMongoClient"; - - -export class CollectionFactoryFactory { - static createNedb(options: Nedb.DataStoreOptions): ICollectionFactory { - return new NedbCollectionFactory(options); - } - - static createMongo(client: IMongoClient): ICollectionFactory { - return new MongoCollectionFactory(client); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/CollectionFactoryStub.spec.ts b/themes/black/server/src/lib/storage/CollectionFactoryStub.spec.ts deleted file mode 100644 index 17f8bb021..000000000 --- a/themes/black/server/src/lib/storage/CollectionFactoryStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import { ICollection } from "./ICollection"; -import { ICollectionFactory } from "./ICollectionFactory"; - -export class CollectionFactoryStub implements ICollectionFactory { - buildStub: Sinon.SinonStub; - - constructor() { - this.buildStub = Sinon.stub(); - } - - build(collectionName: string): ICollection { - return this.buildStub(collectionName); - } -} diff --git a/themes/black/server/src/lib/storage/CollectionStub.spec.ts b/themes/black/server/src/lib/storage/CollectionStub.spec.ts deleted file mode 100644 index 42895d672..000000000 --- a/themes/black/server/src/lib/storage/CollectionStub.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import { ICollection } from "./ICollection"; - -export class CollectionStub implements ICollection { - findStub: Sinon.SinonStub; - findOneStub: Sinon.SinonStub; - updateStub: Sinon.SinonStub; - removeStub: Sinon.SinonStub; - insertStub: Sinon.SinonStub; - - constructor() { - this.findStub = Sinon.stub(); - this.findOneStub = Sinon.stub(); - this.updateStub = Sinon.stub(); - this.removeStub = Sinon.stub(); - this.insertStub = Sinon.stub(); - } - - find(filter: any, sortKeys: any, count: number): BluebirdPromise { - return this.findStub(filter, sortKeys, count); - } - - findOne(filter: any): BluebirdPromise { - return this.findOneStub(filter); - } - - update(filter: any, document: any, options: any): BluebirdPromise { - return this.updateStub(filter, document, options); - } - - remove(filter: any): BluebirdPromise { - return this.removeStub(filter); - } - - insert(document: any): BluebirdPromise { - return this.insertStub(document); - } -} diff --git a/themes/black/server/src/lib/storage/ICollection.d.ts b/themes/black/server/src/lib/storage/ICollection.d.ts deleted file mode 100644 index caa6c2a87..000000000 --- a/themes/black/server/src/lib/storage/ICollection.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* istanbul ignore next */ -import BluebirdPromise = require("bluebird"); - -/* istanbul ignore next */ -export interface ICollection { - find(query: any, sortKeys: any, count: number): BluebirdPromise; - findOne(query: any): BluebirdPromise; - update(query: any, updateQuery: any, options?: any): BluebirdPromise; - remove(query: any): BluebirdPromise; - insert(document: any): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/ICollectionFactory.d.ts b/themes/black/server/src/lib/storage/ICollectionFactory.d.ts deleted file mode 100644 index 39eb42c77..000000000 --- a/themes/black/server/src/lib/storage/ICollectionFactory.d.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import { ICollection } from "./ICollection"; - -export interface ICollectionFactory { - build(collectionName: string): ICollection; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/IUserDataStore.d.ts b/themes/black/server/src/lib/storage/IUserDataStore.d.ts deleted file mode 100644 index 81df482aa..000000000 --- a/themes/black/server/src/lib/storage/IUserDataStore.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; - -export interface IUserDataStore { - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise; - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise; - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise; - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise; - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise; - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise; - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise; - retrieveTOTPSecret(userId: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/IdentityValidationDocument.d.ts b/themes/black/server/src/lib/storage/IdentityValidationDocument.d.ts deleted file mode 100644 index e7fd7b3f4..000000000 --- a/themes/black/server/src/lib/storage/IdentityValidationDocument.d.ts +++ /dev/null @@ -1,7 +0,0 @@ - -export interface IdentityValidationDocument { - userId: string; - token: string; - challenge: string; - maxDate: Date; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/TOTPSecretDocument.d.ts b/themes/black/server/src/lib/storage/TOTPSecretDocument.d.ts deleted file mode 100644 index a6c0bf9e6..000000000 --- a/themes/black/server/src/lib/storage/TOTPSecretDocument.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { TOTPSecret } from "../../../types/TOTPSecret"; - -export interface TOTPSecretDocument { - userid: string; - secret: TOTPSecret; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/U2FRegistrationDocument.d.ts b/themes/black/server/src/lib/storage/U2FRegistrationDocument.d.ts deleted file mode 100644 index efec6cb1d..000000000 --- a/themes/black/server/src/lib/storage/U2FRegistrationDocument.d.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import { U2FRegistration } from "../../../types/U2FRegistration"; - -export interface U2FRegistrationDocument { - userId: string; - appId: string; - registration: U2FRegistration; -} \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/UserDataStore.spec.ts b/themes/black/server/src/lib/storage/UserDataStore.spec.ts deleted file mode 100644 index 66fb85461..000000000 --- a/themes/black/server/src/lib/storage/UserDataStore.spec.ts +++ /dev/null @@ -1,264 +0,0 @@ - -import * as Assert from "assert"; -import * as Sinon from "sinon"; -import * as MockDate from "mockdate"; -import BluebirdPromise = require("bluebird"); - -import { UserDataStore } from "./UserDataStore"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { CollectionStub } from "./CollectionStub.spec"; -import { CollectionFactoryStub } from "./CollectionFactoryStub.spec"; - -describe("storage/UserDataStore", function () { - let factory: CollectionFactoryStub; - let collection: CollectionStub; - let userId: string; - let appId: string; - let totpSecret: TOTPSecret; - let u2fRegistration: U2FRegistration; - - beforeEach(function () { - factory = new CollectionFactoryStub(); - collection = new CollectionStub(); - - userId = "user"; - appId = "https://myappId"; - - totpSecret = { - ascii: "abc", - base32: "ABCDKZLEFZGREJK", - otpauth_url: "totp://test", - google_auth_qr: "dummy", - hex: "dummy", - qr_code_ascii: "dummy", - qr_code_base32: "dummy", - qr_code_hex: "dummy" - }; - - u2fRegistration = { - keyHandle: "KEY_HANDLE", - publicKey: "publickey" - }; - }); - - it("should correctly creates collections", function () { - new UserDataStore(factory); - - Assert.equal(4, factory.buildStub.callCount); - Assert(factory.buildStub.calledWith("authentication_traces")); - Assert(factory.buildStub.calledWith("identity_validation_tokens")); - Assert(factory.buildStub.calledWith("u2f_registrations")); - Assert(factory.buildStub.calledWith("totp_secrets")); - }); - - describe("TOTP secrets collection", function () { - it("should save a totp secret", function () { - factory.buildStub.returns(collection); - collection.updateStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveTOTPSecret(userId, totpSecret) - .then(function (doc) { - Assert(collection.updateStub.calledOnce); - Assert(collection.updateStub.calledWith({ userId: userId }, { - userId: userId, - secret: totpSecret - }, { upsert: true })); - return BluebirdPromise.resolve(); - }); - }); - - it("should retrieve a totp secret", function () { - factory.buildStub.returns(collection); - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveTOTPSecret(userId) - .then(function (doc) { - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ userId: userId })); - return BluebirdPromise.resolve(); - }); - }); - }); - - describe("U2F secrets collection", function () { - it("should save a U2F secret", function () { - factory.buildStub.returns(collection); - collection.updateStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveU2FRegistration(userId, appId, u2fRegistration) - .then(function (doc) { - Assert(collection.updateStub.calledOnce); - Assert(collection.updateStub.calledWith({ - userId: userId, - appId: appId - }, { - userId: userId, - appId: appId, - registration: u2fRegistration - }, { upsert: true })); - return BluebirdPromise.resolve(); - }); - }); - - it("should retrieve a U2F secret", function () { - factory.buildStub.returns(collection); - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveU2FRegistration(userId, appId) - .then(function (doc) { - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - userId: userId, - appId: appId - })); - return BluebirdPromise.resolve(); - }); - }); - }); - - - describe("Regulator traces collection", function () { - it("should save a trace", function () { - factory.buildStub.returns(collection); - collection.insertStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveAuthenticationTrace(userId, true) - .then(function (doc) { - Assert(collection.insertStub.calledOnce); - Assert(collection.insertStub.calledWith({ - userId: userId, - date: Sinon.match.date, - isAuthenticationSuccessful: true - })); - return BluebirdPromise.resolve(); - }); - }); - - function should_retrieve_latest_authentication_traces(count: number) { - factory.buildStub.returns(collection); - collection.findStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveLatestAuthenticationTraces(userId, count) - .then(function (doc: AuthenticationTraceDocument[]) { - Assert(collection.findStub.calledOnce); - Assert(collection.findStub.calledWith({ - userId: userId, - }, { date: -1 }, count)); - return BluebirdPromise.resolve(); - }); - } - - it("should retrieve 3 latest failed authentication traces", function () { - should_retrieve_latest_authentication_traces(3); - }); - }); - - - describe("Identity validation collection", function () { - it("should save a identity validation token", function () { - factory.buildStub.returns(collection); - collection.insertStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - const maxAge = 400; - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - return dataStore.produceIdentityValidationToken(userId, token, challenge, maxAge) - .then(function (doc) { - Assert(collection.insertStub.calledOnce); - Assert(collection.insertStub.calledWith({ - userId: userId, - token: token, - challenge: challenge, - maxDate: Sinon.match.date - })); - return BluebirdPromise.resolve(); - }); - }); - - it("should consume an identity token successfully", function () { - factory.buildStub.returns(collection); - - MockDate.set(100); - - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve({ - userId: "USER", - token: token, - challenge: challenge, - maxDate: new Date() - })); - collection.removeStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - MockDate.set(80); - - return dataStore.consumeIdentityValidationToken(token, challenge) - .then(function (doc) { - MockDate.reset(); - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - token: token, - challenge: challenge - })); - - Assert(collection.removeStub.calledOnce); - Assert(collection.removeStub.calledWith({ - token: token, - challenge: challenge - })); - return BluebirdPromise.resolve(); - }); - }); - - it("should consume an expired identity token", function () { - factory.buildStub.returns(collection); - - MockDate.set(0); - - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve({ - userId: "USER", - token: token, - challenge: challenge, - maxDate: new Date() - })); - - const dataStore = new UserDataStore(factory); - - MockDate.set(80000); - - return dataStore.consumeIdentityValidationToken(token, challenge) - .then(function () { return BluebirdPromise.reject(new Error("should not be here")); }) - .catch(function () { - MockDate.reset(); - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - token: token, - challenge: challenge - })); - return BluebirdPromise.resolve(); - }); - }); - }); -}); diff --git a/themes/black/server/src/lib/storage/UserDataStore.ts b/themes/black/server/src/lib/storage/UserDataStore.ts deleted file mode 100644 index 27b0cddbd..000000000 --- a/themes/black/server/src/lib/storage/UserDataStore.ts +++ /dev/null @@ -1,143 +0,0 @@ -import * as BluebirdPromise from "bluebird"; -import * as path from "path"; -import { IUserDataStore } from "./IUserDataStore"; -import { ICollection } from "./ICollection"; -import { ICollectionFactory } from "./ICollectionFactory"; -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; - -// Constants - -const IDENTITY_VALIDATION_TOKENS_COLLECTION_NAME = "identity_validation_tokens"; -const AUTHENTICATION_TRACES_COLLECTION_NAME = "authentication_traces"; - -const U2F_REGISTRATIONS_COLLECTION_NAME = "u2f_registrations"; -const TOTP_SECRETS_COLLECTION_NAME = "totp_secrets"; - - -export interface U2FRegistrationKey { - userId: string; - appId: string; -} - -// Source - -export class UserDataStore implements IUserDataStore { - private u2fSecretCollection: ICollection; - private identityCheckTokensCollection: ICollection; - private authenticationTracesCollection: ICollection; - private totpSecretCollection: ICollection; - - private collectionFactory: ICollectionFactory; - - constructor(collectionFactory: ICollectionFactory) { - this.collectionFactory = collectionFactory; - - this.u2fSecretCollection = this.collectionFactory.build(U2F_REGISTRATIONS_COLLECTION_NAME); - this.identityCheckTokensCollection = this.collectionFactory.build(IDENTITY_VALIDATION_TOKENS_COLLECTION_NAME); - this.authenticationTracesCollection = this.collectionFactory.build(AUTHENTICATION_TRACES_COLLECTION_NAME); - this.totpSecretCollection = this.collectionFactory.build(TOTP_SECRETS_COLLECTION_NAME); - } - - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise { - const newDocument: U2FRegistrationDocument = { - userId: userId, - appId: appId, - registration: registration - }; - - const filter: U2FRegistrationKey = { - userId: userId, - appId: appId - }; - - return this.u2fSecretCollection.update(filter, newDocument, { upsert: true }); - } - - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise { - const filter: U2FRegistrationKey = { - userId: userId, - appId: appId - }; - return this.u2fSecretCollection.findOne(filter); - } - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - const newDocument: AuthenticationTraceDocument = { - userId: userId, - date: new Date(), - isAuthenticationSuccessful: isAuthenticationSuccessful, - }; - - return this.authenticationTracesCollection.insert(newDocument); - } - - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise { - const q = { - userId: userId - }; - - return this.authenticationTracesCollection.find(q, { date: -1 }, count); - } - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise { - const newDocument: IdentityValidationDocument = { - userId: userId, - token: token, - challenge: challenge, - maxDate: new Date(new Date().getTime() + maxAge) - }; - - return this.identityCheckTokensCollection.insert(newDocument); - } - - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise { - const that = this; - const filter = { - token: token, - challenge: challenge - }; - - let identityValidationDocument: IdentityValidationDocument; - - return this.identityCheckTokensCollection.findOne(filter) - .then(function (doc: IdentityValidationDocument) { - if (!doc) { - return BluebirdPromise.reject(new Error("Registration token does not exist")); - } - - identityValidationDocument = doc; - const current_date = new Date(); - if (current_date > doc.maxDate) - return BluebirdPromise.reject(new Error("Registration token is not valid anymore")); - - return that.identityCheckTokensCollection.remove(filter); - }) - .then(() => { - return BluebirdPromise.resolve(identityValidationDocument); - }); - } - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise { - const doc = { - userId: userId, - secret: secret - }; - - const filter = { - userId: userId - }; - return this.totpSecretCollection.update(filter, doc, { upsert: true }); - } - - retrieveTOTPSecret(userId: string): BluebirdPromise { - const filter = { - userId: userId - }; - return this.totpSecretCollection.findOne(filter); - } -} diff --git a/themes/black/server/src/lib/storage/UserDataStoreStub.spec.ts b/themes/black/server/src/lib/storage/UserDataStoreStub.spec.ts deleted file mode 100644 index 5ea27a2de..000000000 --- a/themes/black/server/src/lib/storage/UserDataStoreStub.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); - -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; -import { IUserDataStore } from "./IUserDataStore"; - -export class UserDataStoreStub implements IUserDataStore { - saveU2FRegistrationStub: Sinon.SinonStub; - retrieveU2FRegistrationStub: Sinon.SinonStub; - saveAuthenticationTraceStub: Sinon.SinonStub; - retrieveLatestAuthenticationTracesStub: Sinon.SinonStub; - produceIdentityValidationTokenStub: Sinon.SinonStub; - consumeIdentityValidationTokenStub: Sinon.SinonStub; - saveTOTPSecretStub: Sinon.SinonStub; - retrieveTOTPSecretStub: Sinon.SinonStub; - - constructor() { - this.saveU2FRegistrationStub = Sinon.stub(); - this.retrieveU2FRegistrationStub = Sinon.stub(); - this.saveAuthenticationTraceStub = Sinon.stub(); - this.retrieveLatestAuthenticationTracesStub = Sinon.stub(); - this.produceIdentityValidationTokenStub = Sinon.stub(); - this.consumeIdentityValidationTokenStub = Sinon.stub(); - this.saveTOTPSecretStub = Sinon.stub(); - this.retrieveTOTPSecretStub = Sinon.stub(); - } - - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise { - return this.saveU2FRegistrationStub(userId, appId, registration); - } - - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise { - return this.retrieveU2FRegistrationStub(userId, appId); - } - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - return this.saveAuthenticationTraceStub(userId, isAuthenticationSuccessful); - } - - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise { - return this.retrieveLatestAuthenticationTracesStub(userId, count); - } - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise { - return this.produceIdentityValidationTokenStub(userId, token, challenge, maxAge); - } - - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise { - return this.consumeIdentityValidationTokenStub(token, challenge); - } - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise { - return this.saveTOTPSecretStub(userId, secret); - } - - retrieveTOTPSecret(userId: string): BluebirdPromise { - return this.retrieveTOTPSecretStub(userId); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/mongo/MongoCollection.spec.ts b/themes/black/server/src/lib/storage/mongo/MongoCollection.spec.ts deleted file mode 100644 index 74a773a1e..000000000 --- a/themes/black/server/src/lib/storage/mongo/MongoCollection.spec.ts +++ /dev/null @@ -1,110 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import MongoDB = require("mongodb"); -import BluebirdPromise = require("bluebird"); -import { MongoClientStub } from "../../connectors/mongo/MongoClientStub.spec"; -import { MongoCollection } from "./MongoCollection"; - -describe("storage/mongo/MongoCollection", function () { - let mongoCollectionStub: any; - let mongoClientStub: MongoClientStub; - let findStub: Sinon.SinonStub; - let findOneStub: Sinon.SinonStub; - let insertOneStub: Sinon.SinonStub; - let updateStub: Sinon.SinonStub; - let removeStub: Sinon.SinonStub; - let countStub: Sinon.SinonStub; - const COLLECTION_NAME = "collection"; - - before(function () { - mongoClientStub = new MongoClientStub(); - mongoCollectionStub = Sinon.createStubInstance(require("mongodb").Collection as any); - findStub = mongoCollectionStub.find as Sinon.SinonStub; - findOneStub = mongoCollectionStub.findOne as Sinon.SinonStub; - insertOneStub = mongoCollectionStub.insertOne as Sinon.SinonStub; - updateStub = mongoCollectionStub.update as Sinon.SinonStub; - removeStub = mongoCollectionStub.remove as Sinon.SinonStub; - countStub = mongoCollectionStub.count as Sinon.SinonStub; - mongoClientStub.collectionStub.returns( - BluebirdPromise.resolve(mongoCollectionStub) - ); - }); - - describe("find", function () { - it("should find a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - findStub.returns({ - sort: Sinon.stub().returns({ - limit: Sinon.stub().returns({ - toArray: Sinon.stub().returns(BluebirdPromise.resolve([])) - }) - }) - }); - - return collection.find({ key: "KEY" }) - .then(function () { - Assert(findStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("findOne", function () { - it("should find one document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - findOneStub.returns(BluebirdPromise.resolve({})); - - return collection.findOne({ key: "KEY" }) - .then(function () { - Assert(findOneStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("insert", function () { - it("should insert a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - insertOneStub.returns(BluebirdPromise.resolve({})); - - return collection.insert({ key: "KEY" }) - .then(function () { - Assert(insertOneStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("update", function () { - it("should update a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - updateStub.returns(BluebirdPromise.resolve({})); - - return collection.update({ key: "KEY" }, { key: "KEY", value: 1 }) - .then(function () { - Assert(updateStub.calledWith({ key: "KEY" }, { key: "KEY", value: 1 })); - }); - }); - }); - - describe("remove", function () { - it("should remove a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - removeStub.returns(BluebirdPromise.resolve({})); - - return collection.remove({ key: "KEY" }) - .then(function () { - Assert(removeStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("count", function () { - it("should count documents in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - countStub.returns(BluebirdPromise.resolve({})); - - return collection.count({ key: "KEY" }) - .then(function () { - Assert(countStub.calledWith({ key: "KEY" })); - }); - }); - }); -}); diff --git a/themes/black/server/src/lib/storage/mongo/MongoCollection.ts b/themes/black/server/src/lib/storage/mongo/MongoCollection.ts deleted file mode 100644 index 9771389f1..000000000 --- a/themes/black/server/src/lib/storage/mongo/MongoCollection.ts +++ /dev/null @@ -1,50 +0,0 @@ -import Bluebird = require("bluebird"); -import { ICollection } from "../ICollection"; -import MongoDB = require("mongodb"); -import { IMongoClient } from "../../connectors/mongo/IMongoClient"; - - -export class MongoCollection implements ICollection { - private mongoClient: IMongoClient; - private collectionName: string; - - constructor(collectionName: string, mongoClient: IMongoClient) { - this.collectionName = collectionName; - this.mongoClient = mongoClient; - } - - private collection(): Bluebird { - return this.mongoClient.collection(this.collectionName); - } - - find(query: any, sortKeys?: any, count?: number): Bluebird { - return this.collection() - .then((collection) => collection.find(query).sort(sortKeys).limit(count)) - .then((query) => query.toArray()); - } - - findOne(query: any): Bluebird { - return this.collection() - .then((collection) => collection.findOne(query)); - } - - update(query: any, updateQuery: any, options?: any): Bluebird { - return this.collection() - .then((collection) => collection.update(query, updateQuery, options)); - } - - remove(query: any): Bluebird { - return this.collection() - .then((collection) => collection.remove(query)); - } - - insert(document: any): Bluebird { - return this.collection() - .then((collection) => collection.insertOne(document)); - } - - count(query: any): Bluebird { - return this.collection() - .then((collection) => collection.count(query)); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts b/themes/black/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts deleted file mode 100644 index bd959cacb..000000000 --- a/themes/black/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import { MongoClientStub } from "../../connectors/mongo/MongoClientStub.spec"; -import { MongoCollectionFactory } from "./MongoCollectionFactory"; - -describe("storage/mongo/MongoCollectionFactory", function () { - let mongoClient: MongoClientStub; - - before(function() { - mongoClient = new MongoClientStub(); - }); - - describe("create", function () { - it("should create a collection", function () { - const COLLECTION_NAME = "COLLECTION_NAME"; - - const factory = new MongoCollectionFactory(mongoClient); - Assert(factory.build(COLLECTION_NAME)); - }); - }); -}); diff --git a/themes/black/server/src/lib/storage/mongo/MongoCollectionFactory.ts b/themes/black/server/src/lib/storage/mongo/MongoCollectionFactory.ts deleted file mode 100644 index 14a8262c9..000000000 --- a/themes/black/server/src/lib/storage/mongo/MongoCollectionFactory.ts +++ /dev/null @@ -1,19 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ICollection } from "../ICollection"; -import { ICollectionFactory } from "../ICollectionFactory"; -import { MongoCollection } from "./MongoCollection"; -import path = require("path"); -import MongoDB = require("mongodb"); -import { IMongoClient } from "../../connectors/mongo/IMongoClient"; - -export class MongoCollectionFactory implements ICollectionFactory { - private mongoClient: IMongoClient; - - constructor(mongoClient: IMongoClient) { - this.mongoClient = mongoClient; - } - - build(collectionName: string): ICollection { - return new MongoCollection(collectionName, this.mongoClient); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/nedb/NedbCollection.spec.ts b/themes/black/server/src/lib/storage/nedb/NedbCollection.spec.ts deleted file mode 100644 index a69962b67..000000000 --- a/themes/black/server/src/lib/storage/nedb/NedbCollection.spec.ts +++ /dev/null @@ -1,136 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); - -import { NedbCollection } from "./NedbCollection"; - -describe("storage/nedb/NedbCollection", function () { - describe("insert", function () { - it("should insert one entry", function () { - const nedbOptions = { - inMemoryOnly: true - }; - const collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - - return collection.count({}).then(function (count: number) { - Assert.equal(1, count); - }); - }); - - it("should insert three entries", function () { - const nedbOptions = { - inMemoryOnly: true - }; - const collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - collection.insert({ key: "hello" }); - collection.insert({ key: "hey" }); - - return collection.count({}).then(function (count: number) { - Assert.equal(3, count); - }); - }); - }); - - describe("find", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "hello" }); - collection.insert({ key: "hey" }); - collection.insert({ key: "coucou", value: 2 }); - }); - - it("should find one hello", function () { - return collection.find({ key: "hello" }, { key: 1 }) - .then(function (docs: { key: string }[]) { - Assert.equal(1, docs.length); - Assert(docs[0].key == "hello"); - }); - }); - - it("should find two coucou", function () { - return collection.find({ key: "coucou" }, { value: 1 }) - .then(function (docs: { value: number }[]) { - Assert.equal(2, docs.length); - }); - }); - }); - - describe("findOne", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - }); - - it("should find two coucou", function () { - const doc = { key: "coucou", value: 1 }; - return collection.count(doc) - .then(function (count: number) { - Assert.equal(4, count); - return collection.findOne(doc); - }); - }); - }); - - describe("update", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - }); - - it("should update the value", function () { - return collection.update({ key: "coucou" }, { key: "coucou", value: 2 }, { multi: true }) - .then(function () { - return collection.find({ key: "coucou" }); - }) - .then(function (docs: { key: string, value: number }[]) { - Assert.equal(1, docs.length); - Assert.equal(2, docs[0].value); - }); - }); - }); - - describe("update", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - collection.insert({ key: "hello" }); - }); - - it("should update the value", function () { - return collection.remove({ key: "coucou" }) - .then(function () { - return collection.count({}); - }) - .then(function (count: number) { - Assert.equal(1, count); - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/nedb/NedbCollection.ts b/themes/black/server/src/lib/storage/nedb/NedbCollection.ts deleted file mode 100644 index 88a93ad05..000000000 --- a/themes/black/server/src/lib/storage/nedb/NedbCollection.ts +++ /dev/null @@ -1,47 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ICollection } from "../ICollection"; -import Nedb = require("nedb"); - -declare module "nedb" { - export class NedbAsync extends Nedb { - constructor(pathOrOptions?: string | Nedb.DataStoreOptions); - updateAsync(query: any, updateQuery: any, options?: Nedb.UpdateOptions): BluebirdPromise; - findOneAsync(query: any): BluebirdPromise; - insertAsync(newDoc: T): BluebirdPromise; - removeAsync(query: any): BluebirdPromise; - countAsync(query: any): BluebirdPromise; - } -} - -export class NedbCollection implements ICollection { - private collection: Nedb.NedbAsync; - - constructor(options: Nedb.DataStoreOptions) { - this.collection = BluebirdPromise.promisifyAll(new Nedb(options)) as Nedb.NedbAsync; - } - - find(query: any, sortKeys?: any, count?: number): BluebirdPromise { - const q = this.collection.find(query).sort(sortKeys).limit(count); - return BluebirdPromise.promisify(q.exec, { context: q })(); - } - - findOne(query: any): BluebirdPromise { - return this.collection.findOneAsync(query); - } - - update(query: any, updateQuery: any, options?: any): BluebirdPromise { - return this.collection.updateAsync(query, updateQuery, options); - } - - remove(query: any): BluebirdPromise { - return this.collection.removeAsync(query); - } - - insert(document: any): BluebirdPromise { - return this.collection.insertAsync(document); - } - - count(query: any): BluebirdPromise { - return this.collection.countAsync(query); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts b/themes/black/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts deleted file mode 100644 index da90c661f..000000000 --- a/themes/black/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); - -import { NedbCollectionFactory } from "./NedbCollectionFactory"; - -describe("storage/nedb/NedbCollectionFactory", function() { - it("should create a nedb collection", function() { - const nedbOptions = { - inMemoryOnly: true - }; - const factory = new NedbCollectionFactory(nedbOptions); - - const collection = factory.build("mycollection"); - Assert(collection); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/storage/nedb/NedbCollectionFactory.ts b/themes/black/server/src/lib/storage/nedb/NedbCollectionFactory.ts deleted file mode 100644 index 49c4dc853..000000000 --- a/themes/black/server/src/lib/storage/nedb/NedbCollectionFactory.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ICollection } from "../ICollection"; -import { ICollectionFactory } from "../ICollectionFactory"; -import { NedbCollection } from "./NedbCollection"; -import path = require("path"); -import Nedb = require("nedb"); - -export interface NedbOptions { - inMemoryOnly?: boolean; - directory?: string; -} - -export class NedbCollectionFactory implements ICollectionFactory { - private options: Nedb.DataStoreOptions; - - constructor(options: Nedb.DataStoreOptions) { - this.options = options; - } - - build(collectionName: string): ICollection { - const datastoreOptions: Nedb.DataStoreOptions = { - inMemoryOnly: this.options.inMemoryOnly || false, - autoload: true, - filename: (this.options.filename) ? path.resolve(this.options.filename, collectionName) : undefined - }; - - return new NedbCollection(datastoreOptions); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/stubs/express.spec.ts b/themes/black/server/src/lib/stubs/express.spec.ts deleted file mode 100644 index 48f15d7e1..000000000 --- a/themes/black/server/src/lib/stubs/express.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ - -import sinon = require("sinon"); -import express = require("express"); - -export interface RequestMock { - app?: any; - body?: any; - session?: any; - headers?: any; - get?: any; - query?: any; - originalUrl: string; -} - -export interface ResponseMock { - send: sinon.SinonStub | sinon.SinonSpy; - sendStatus: sinon.SinonStub; - sendFile: sinon.SinonStub; - sendfile: sinon.SinonStub; - status: sinon.SinonStub | sinon.SinonSpy; - json: sinon.SinonStub | sinon.SinonSpy; - links: sinon.SinonStub; - jsonp: sinon.SinonStub; - download: sinon.SinonStub; - contentType: sinon.SinonStub; - type: sinon.SinonStub; - format: sinon.SinonStub; - attachment: sinon.SinonStub; - set: sinon.SinonStub; - header: sinon.SinonStub; - headersSent: boolean; - get: sinon.SinonStub; - clearCookie: sinon.SinonStub; - cookie: sinon.SinonStub; - location: sinon.SinonStub; - redirect: sinon.SinonStub | sinon.SinonSpy; - render: sinon.SinonStub | sinon.SinonSpy; - locals: sinon.SinonStub; - charset: string; - vary: sinon.SinonStub; - app: any; - write: sinon.SinonStub; - writeContinue: sinon.SinonStub; - writeHead: sinon.SinonStub; - statusCode: number; - statusMessage: string; - setHeader: sinon.SinonStub; - setTimeout: sinon.SinonStub; - sendDate: boolean; - getHeader: sinon.SinonStub; -} - -export function RequestMock(): RequestMock { - return { - originalUrl: "/non-api/xxx", - app: { - get: sinon.stub() - }, - headers: { - "x-forwarded-for": "127.0.0.1" - }, - session: {} - }; -} -export function ResponseMock(): ResponseMock { - return { - send: sinon.stub(), - status: sinon.stub(), - json: sinon.stub(), - sendStatus: sinon.stub(), - links: sinon.stub(), - jsonp: sinon.stub(), - sendFile: sinon.stub(), - sendfile: sinon.stub(), - download: sinon.stub(), - contentType: sinon.stub(), - type: sinon.stub(), - format: sinon.stub(), - attachment: sinon.stub(), - set: sinon.stub(), - header: sinon.stub(), - headersSent: true, - get: sinon.stub(), - clearCookie: sinon.stub(), - cookie: sinon.stub(), - location: sinon.stub(), - redirect: sinon.stub(), - render: sinon.stub(), - locals: sinon.stub(), - charset: "utf-8", - vary: sinon.stub(), - app: sinon.stub(), - write: sinon.stub(), - writeContinue: sinon.stub(), - writeHead: sinon.stub(), - statusCode: 200, - statusMessage: "message", - setHeader: sinon.stub(), - setTimeout: sinon.stub(), - sendDate: true, - getHeader: sinon.stub() - }; -} diff --git a/themes/black/server/src/lib/stubs/ldapjs.spec.ts b/themes/black/server/src/lib/stubs/ldapjs.spec.ts deleted file mode 100644 index 045c0e11b..000000000 --- a/themes/black/server/src/lib/stubs/ldapjs.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ - -import Sinon = require("sinon"); - -export class LdapjsMock { - createClientStub: sinon.SinonStub; - - constructor() { - this.createClientStub = Sinon.stub(); - } - - createClient(params: any) { - return this.createClientStub(params); - } -} - -export class LdapjsClientMock { - bindStub: sinon.SinonStub; - unbindStub: sinon.SinonStub; - searchStub: sinon.SinonStub; - modifyStub: sinon.SinonStub; - onStub: sinon.SinonStub; - - constructor() { - this.bindStub = Sinon.stub(); - this.unbindStub = Sinon.stub(); - this.searchStub = Sinon.stub(); - this.modifyStub = Sinon.stub(); - this.onStub = Sinon.stub(); - } - - bind() { - return this.bindStub(); - } - - unbind() { - return this.unbindStub(); - } - - search() { - return this.searchStub(); - } - - modify() { - return this.modifyStub(); - } - - on() { - return this.onStub(); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/stubs/speakeasy.spec.ts b/themes/black/server/src/lib/stubs/speakeasy.spec.ts deleted file mode 100644 index 023614dcd..000000000 --- a/themes/black/server/src/lib/stubs/speakeasy.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import sinon = require("sinon"); - -export = { - totp: sinon.stub(), - generateSecret: sinon.stub() -}; diff --git a/themes/black/server/src/lib/stubs/u2f.spec.ts b/themes/black/server/src/lib/stubs/u2f.spec.ts deleted file mode 100644 index 234b28c11..000000000 --- a/themes/black/server/src/lib/stubs/u2f.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ - -import sinon = require("sinon"); - -export interface U2FMock { - request: sinon.SinonStub; - checkSignature: sinon.SinonStub; - checkRegistration: sinon.SinonStub; -} - -export function U2FMock(): U2FMock { - return { - request: sinon.stub(), - checkSignature: sinon.stub(), - checkRegistration: sinon.stub() - }; -} diff --git a/themes/black/server/src/lib/utils/HashGenerator.spec.ts b/themes/black/server/src/lib/utils/HashGenerator.spec.ts deleted file mode 100644 index f19619a65..000000000 --- a/themes/black/server/src/lib/utils/HashGenerator.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Assert = require("assert"); -import { HashGenerator } from "./HashGenerator"; - -describe("utils/HashGenerator", function () { - it("should compute correct ssha512 (password)", function () { - return HashGenerator.ssha512("password", 500000, "jgiCMRyGXzoqpxS3") - .then(function (hash: string) { - Assert.equal(hash, "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"); - }); - }); - - it("should compute correct ssha512 (test)", function () { - return HashGenerator.ssha512("test", 500000, "abcdefghijklmnop") - .then(function (hash: string) { - Assert.equal(hash, "{CRYPT}$6$rounds=500000$abcdefghijklmnop$sTlNGf0VO/HTQIOXemmaBbV28HUch/qhWOA1/4dsDj6CDQYhUgXbYSPL6gccAsWMr2zD5fFWwhKmPdG.yxphs."); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/utils/HashGenerator.ts b/themes/black/server/src/lib/utils/HashGenerator.ts deleted file mode 100644 index e67de32b7..000000000 --- a/themes/black/server/src/lib/utils/HashGenerator.ts +++ /dev/null @@ -1,23 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import RandomString = require("randomstring"); -import Util = require("util"); -const crypt = require("crypt3"); - -export class HashGenerator { - static ssha512( - password: string, - rounds: number = 500000, - salt?: string): BluebirdPromise { - const saltSize = 16; - // $6 means SHA512 - const _salt = Util.format("$6$rounds=%d$%s", rounds, - (salt) ? salt : RandomString.generate(16)); - - const cryptAsync = BluebirdPromise.promisify(crypt); - - return cryptAsync(password, _salt) - .then(function (hash: string) { - return BluebirdPromise.resolve(Util.format("{CRYPT}%s", hash)); - }); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/utils/ObjectCloner.ts b/themes/black/server/src/lib/utils/ObjectCloner.ts deleted file mode 100644 index 3e125d749..000000000 --- a/themes/black/server/src/lib/utils/ObjectCloner.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export class ObjectCloner { - static clone(obj: any): any { - return JSON.parse(JSON.stringify(obj)); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/utils/SafeRedirection.spec.ts b/themes/black/server/src/lib/utils/SafeRedirection.spec.ts deleted file mode 100644 index 4126949fd..000000000 --- a/themes/black/server/src/lib/utils/SafeRedirection.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import { SafeRedirector } from "./SafeRedirection"; - -describe("web_server/middlewares/SafeRedirection", () => { - describe("Url is in protected domain", () => { - before(() => { - this.redirector = new SafeRedirector("example.com"); - this.res = {redirect: Sinon.stub()}; - }); - - it("should redirect to provided url", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.example.com:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://mysubdomain.example.com:8080/abc")); - }); - - it("should redirect to default url when wrong domain", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.domain.rtf:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://authelia.example.com")); - }); - - it("should redirect to default url when not terminating by domain", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.example.com.rtf:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://authelia.example.com")); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/utils/SafeRedirection.ts b/themes/black/server/src/lib/utils/SafeRedirection.ts deleted file mode 100644 index 9e6a32e0c..000000000 --- a/themes/black/server/src/lib/utils/SafeRedirection.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Express = require("express"); -import { DomainExtractor } from "../../../../shared/DomainExtractor"; -import { BelongToDomain } from "../../../../shared/BelongToDomain"; - - -export class SafeRedirector { - private domain: string; - - constructor(domain: string) { - this.domain = domain; - } - - redirectOrElse( - res: Express.Response, - url: string, - defaultUrl: string): void { - if (BelongToDomain(url, this.domain)) { - res.redirect(url); - } - res.redirect(defaultUrl); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/utils/URLDecomposer.spec.ts b/themes/black/server/src/lib/utils/URLDecomposer.spec.ts deleted file mode 100644 index cbb038738..000000000 --- a/themes/black/server/src/lib/utils/URLDecomposer.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { URLDecomposer } from "./URLDecomposer"; -import Assert = require("assert"); - -describe("utils/URLDecomposer", function () { - describe("test fromUrl", function () { - it("should return domain from https url", function () { - const d = URLDecomposer.fromUrl("https://www.example.com/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return domain from http url", function () { - const d = URLDecomposer.fromUrl("http://www.example.com/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return domain when url contains port", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return default path when no path provided", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/"); - }); - - it("should return default path when provided", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080/"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/"); - }); - - it("should return undefined when does not match", function () { - const d = URLDecomposer.fromUrl("https:///abc/test"); - Assert.equal(d, undefined); - }); - - it("should return undefined when does not match", function () { - const d = URLDecomposer.fromUrl("https:///abc/test"); - Assert.equal(d, undefined); - }); - }); -}); \ No newline at end of file diff --git a/themes/black/server/src/lib/utils/URLDecomposer.ts b/themes/black/server/src/lib/utils/URLDecomposer.ts deleted file mode 100644 index 9bdf2e9d2..000000000 --- a/themes/black/server/src/lib/utils/URLDecomposer.ts +++ /dev/null @@ -1,15 +0,0 @@ -export class URLDecomposer { - static fromUrl(url: string): {domain: string, path: string} { - if (!url) return; - const match = url.match(/https?:\/\/([a-z0-9_.-]+)(:[0-9]+)?(.*)/); - - if (!match) return; - - if (match[1] && !match[3]) { - return {domain: match[1], path: "/"}; - } else if (match[1] && match[3]) { - return {domain: match[1], path: match[3]}; - } - return; - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/web_server/Configurator.ts b/themes/black/server/src/lib/web_server/Configurator.ts deleted file mode 100644 index 6e404874a..000000000 --- a/themes/black/server/src/lib/web_server/Configurator.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Configuration } from "../configuration/schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; -import { SessionConfigurationBuilder } from - "../configuration/SessionConfigurationBuilder"; -import Path = require("path"); -import Express = require("express"); -import * as BodyParser from "body-parser"; -import { RestApi } from "./RestApi"; -import { WithHeadersLogged } from "./middlewares/WithHeadersLogged"; -import { ServerVariables } from "../ServerVariables"; -import Helmet = require("helmet"); - -const addRequestId = require("express-request-id")(); - -// Constants -const TRUST_PROXY = "trust proxy"; -const X_POWERED_BY = "x-powered-by"; -const VIEWS = "views"; -const VIEW_ENGINE = "view engine"; -const PUG = "pug"; - -export class Configurator { - static configure(config: Configuration, - app: Express.Application, - vars: ServerVariables, - deps: GlobalDependencies): void { - const viewsDirectory = Path.resolve(__dirname, "../../views"); - const publicHtmlDirectory = Path.resolve(__dirname, "../../public_html"); - - const expressSessionOptions = SessionConfigurationBuilder.build(config, deps); - - app.use(Express.static(publicHtmlDirectory)); - app.use(BodyParser.urlencoded({ extended: false })); - app.use(BodyParser.json()); - app.use(deps.session(expressSessionOptions)); - app.use(addRequestId); - app.use(WithHeadersLogged.middleware(vars.logger)); - app.disable(X_POWERED_BY); - app.enable(TRUST_PROXY); - app.use(Helmet()); - - app.set(VIEWS, viewsDirectory); - app.set(VIEW_ENGINE, PUG); - - RestApi.setup(app, vars); - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/web_server/RestApi.ts b/themes/black/server/src/lib/web_server/RestApi.ts deleted file mode 100644 index 9144a15b9..000000000 --- a/themes/black/server/src/lib/web_server/RestApi.ts +++ /dev/null @@ -1,125 +0,0 @@ -import Express = require("express"); - -import FirstFactorGet = require("../routes/firstfactor/get"); -import SecondFactorGet = require("../routes/secondfactor/get"); - -import FirstFactorPost = require("../routes/firstfactor/post"); -import LogoutGet = require("../routes/logout/get"); -import VerifyGet = require("../routes/verify/get"); -import TOTPSignGet = require("../routes/secondfactor/totp/sign/post"); - -import IdentityCheckMiddleware = require("../IdentityCheckMiddleware"); - -import TOTPRegistrationIdentityHandler from "../routes/secondfactor/totp/identity/RegistrationHandler"; -import U2FRegistrationIdentityHandler from "../routes/secondfactor/u2f/identity/RegistrationHandler"; -import ResetPasswordIdentityHandler from "../routes/password-reset/identity/PasswordResetHandler"; - -import U2FSignPost = require("../routes/secondfactor/u2f/sign/post"); -import U2FSignRequestGet = require("../routes/secondfactor/u2f/sign_request/get"); - -import U2FRegisterPost = require("../routes/secondfactor/u2f/register/post"); -import U2FRegisterRequestGet = require("../routes/secondfactor/u2f/register_request/get"); - -import ResetPasswordFormPost = require("../routes/password-reset/form/post"); -import ResetPasswordRequestPost = require("../routes/password-reset/request/get"); - -import Error401Get = require("../routes/error/401/get"); -import Error403Get = require("../routes/error/403/get"); -import Error404Get = require("../routes/error/404/get"); - -import LoggedIn = require("../routes/loggedin/get"); - -import { ServerVariables } from "../ServerVariables"; -import Endpoints = require("../../../../shared/api"); -import { RequireValidatedFirstFactor } from "./middlewares/RequireValidatedFirstFactor"; - -function setupTotp(app: Express.Application, vars: ServerVariables) { - app.post(Endpoints.SECOND_FACTOR_TOTP_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - TOTPSignGet.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - app.get(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - IdentityCheckMiddleware.register(app, - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET, - new TOTPRegistrationIdentityHandler(vars.logger, - vars.userDataStore, vars.totpHandler, vars.config.totp), - vars); -} - -function setupU2f(app: Express.Application, vars: ServerVariables) { - app.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FSignRequestGet.default(vars)); - - app.post(Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FSignPost.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FRegisterRequestGet.default(vars)); - - app.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FRegisterPost.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - app.get(Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - IdentityCheckMiddleware.register(app, - Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, - Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, - new U2FRegistrationIdentityHandler(vars.logger), vars); -} - -function setupResetPassword(app: Express.Application, vars: ServerVariables) { - IdentityCheckMiddleware.register(app, - Endpoints.RESET_PASSWORD_IDENTITY_START_GET, - Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET, - new ResetPasswordIdentityHandler(vars.logger, vars.usersDatabase), - vars); - - app.get(Endpoints.RESET_PASSWORD_REQUEST_GET, - ResetPasswordRequestPost.default); - app.post(Endpoints.RESET_PASSWORD_FORM_POST, - ResetPasswordFormPost.default(vars)); -} - -function setupErrors(app: Express.Application, vars: ServerVariables) { - app.get(Endpoints.ERROR_401_GET, Error401Get.default(vars)); - app.get(Endpoints.ERROR_403_GET, Error403Get.default(vars)); - app.get(Endpoints.ERROR_404_GET, Error404Get.default); -} - -export class RestApi { - static setup(app: Express.Application, vars: ServerVariables): void { - app.get(Endpoints.FIRST_FACTOR_GET, FirstFactorGet.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - SecondFactorGet.default(vars)); - - app.get(Endpoints.LOGOUT_GET, LogoutGet.default(vars)); - - app.get(Endpoints.VERIFY_GET, VerifyGet.default(vars)); - app.post(Endpoints.FIRST_FACTOR_POST, FirstFactorPost.default(vars)); - - setupTotp(app, vars); - setupU2f(app, vars); - setupResetPassword(app, vars); - setupErrors(app, vars); - - app.get(Endpoints.LOGGED_IN, - RequireValidatedFirstFactor.middleware(vars.logger), - LoggedIn.default(vars)); - } -} diff --git a/themes/black/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts b/themes/black/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts deleted file mode 100644 index ecfd75765..000000000 --- a/themes/black/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts +++ /dev/null @@ -1,27 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import ErrorReplies = require("../../ErrorReplies"); -import { IRequestLogger } from "../../logging/IRequestLogger"; -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Exceptions = require("../../Exceptions"); -import { Level } from "../../authentication/Level"; - -export class RequireValidatedFirstFactor { - static middleware(logger: IRequestLogger) { - return function (req: Express.Request, res: Express.Response, - next: Express.NextFunction): BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, logger); - if (!authSession.userid || authSession.authentication_level < Level.ONE_FACTOR) - return reject( - new Exceptions.FirstFactorValidationError( - "First factor has not been validated yet.")); - - next(); - resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, logger)); - }; - } -} \ No newline at end of file diff --git a/themes/black/server/src/lib/web_server/middlewares/WithHeadersLogged.ts b/themes/black/server/src/lib/web_server/middlewares/WithHeadersLogged.ts deleted file mode 100644 index 139db1141..000000000 --- a/themes/black/server/src/lib/web_server/middlewares/WithHeadersLogged.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Express = require("express"); -import { IRequestLogger } from "../../logging/IRequestLogger"; - -export class WithHeadersLogged { - static middleware(logger: IRequestLogger) { - return function (req: Express.Request, res: Express.Response, - next: Express.NextFunction): void { - logger.debug(req, "Headers = %s", JSON.stringify(req.headers)); - next(); - }; - } -} \ No newline at end of file diff --git a/themes/black/server/src/resources/email-template.ejs b/themes/black/server/src/resources/email-template.ejs old mode 100644 new mode 100755 diff --git a/themes/black/server/src/views/already-logged-in.pug b/themes/black/server/src/views/already-logged-in.pug old mode 100644 new mode 100755 diff --git a/themes/black/server/src/views/errors/.directory b/themes/black/server/src/views/errors/.directory old mode 100644 new mode 100755 diff --git a/themes/black/server/src/views/errors/401.pug b/themes/black/server/src/views/errors/401.pug old mode 100644 new mode 100755 diff --git a/themes/black/server/src/views/errors/403.pug b/themes/black/server/src/views/errors/403.pug old mode 100644 new mode 100755 diff --git a/themes/black/server/src/views/errors/404.pug b/themes/black/server/src/views/errors/404.pug old mode 100644 new mode 100755 diff --git a/themes/black/server/src/views/firstfactor.pug b/themes/black/server/src/views/firstfactor.pug old mode 100644 new mode 100755 diff --git a/themes/black/server/src/views/layout/layout.pug b/themes/black/server/src/views/layout/layout.pug old mode 100644 new mode 100755 diff --git a/themes/black/server/src/views/need-identity-validation.pug b/themes/black/server/src/views/need-identity-validation.pug old mode 100644 new mode 100755 diff --git a/themes/black/server/src/views/password-reset-form.pug b/themes/black/server/src/views/password-reset-form.pug old mode 100644 new mode 100755 diff --git a/themes/black/server/src/views/password-reset-request.pug b/themes/black/server/src/views/password-reset-request.pug old mode 100644 new mode 100755 diff --git a/themes/black/server/src/views/secondfactor.pug b/themes/black/server/src/views/secondfactor.pug old mode 100644 new mode 100755 diff --git a/themes/black/server/src/views/totp-register.pug b/themes/black/server/src/views/totp-register.pug old mode 100644 new mode 100755 diff --git a/themes/black/server/src/views/u2f-register.pug b/themes/black/server/src/views/u2f-register.pug old mode 100644 new mode 100755 diff --git a/themes/black/server/test/requests.ts b/themes/black/server/test/requests.ts deleted file mode 100644 index 93fa0de47..000000000 --- a/themes/black/server/test/requests.ts +++ /dev/null @@ -1,94 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import request = require("request"); -import assert = require("assert"); -import express = require("express"); -import nodemailer = require("nodemailer"); -import Endpoints = require("../../shared/api"); - -declare module "request" { - export interface RequestAPI { - getAsync(uri: string, options?: RequiredUriUrl): BluebirdPromise; - getAsync(uri: string): BluebirdPromise; - getAsync(options: RequiredUriUrl & CoreOptions): BluebirdPromise; - - postAsync(uri: string, options?: CoreOptions): BluebirdPromise; - postAsync(uri: string): BluebirdPromise; - postAsync(options: RequiredUriUrl & CoreOptions): BluebirdPromise; - } -} - -const requestAsync: typeof request = BluebirdPromise.promisifyAll(request) as typeof request; - -export = function (port: number) { - const PORT = port; - const BASE_URL = "http://localhost:" + PORT; - - function execute_totp(jar: request.CookieJar, token: string) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_TOTP_POST, - jar: jar, - form: { - token: token - } - }); - } - - function execute_u2f_authentication(jar: request.CookieJar) { - return requestAsync.getAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, - jar: jar - }) - .then(function (res: request.RequestResponse) { - assert.equal(res.statusCode, 200); - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - jar: jar, - form: { - } - }); - }); - } - - function execute_verification(jar: request.CookieJar) { - return requestAsync.getAsync({ url: BASE_URL + Endpoints.VERIFY_GET, jar: jar }); - } - - function execute_login(jar: request.CookieJar) { - return requestAsync.getAsync({ url: BASE_URL + Endpoints.FIRST_FACTOR_GET, jar: jar }); - } - - function execute_first_factor(jar: request.CookieJar) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.FIRST_FACTOR_POST, - jar: jar, - form: { - username: "test_ok", - password: "password" - } - }); - } - - function execute_failing_first_factor(jar: request.CookieJar) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.FIRST_FACTOR_POST, - jar: jar, - form: { - username: "test_nok", - password: "password" - } - }); - } - - return { - login: execute_login, - verify: execute_verification, - u2f_authentication: execute_u2f_authentication, - first_factor: execute_first_factor, - failing_first_factor: execute_failing_first_factor, - totp: execute_totp, - }; -}; - diff --git a/themes/black/server/tsconfig.json b/themes/black/server/tsconfig.json deleted file mode 100644 index ebe98c5ed..000000000 --- a/themes/black/server/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "moduleResolution": "node", - "noImplicitAny": true, - "sourceMap": true, - "removeComments": true, - "outDir": "../dist", - "baseUrl": ".", - "paths": { - "*": [ - "./types/*", - "../shared/types/*" - ] - } - }, - "exclude": [ - "src/**/*.spec.ts" - ] -} diff --git a/themes/black/server/tslint.json b/themes/black/server/tslint.json deleted file mode 100644 index c2c1b7501..000000000 --- a/themes/black/server/tslint.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "no-var-keyword": true, - "quotemark": [ - true, - "double", - "avoid-escape" - ], - "semicolon": [ - true, - "always", - "ignore-bound-class-methods" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-module", - "check-separator", - "check-type" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "onespace", - "index-signature": "onespace", - "parameter": "onespace", - "property-declaration": "onespace", - "variable-declaration": "onespace" - } - ], - "no-internal-module": true, - "no-trailing-whitespace": true, - "no-null-keyword": true, - "prefer-const": true, - "jsdoc-format": true - } -} diff --git a/themes/black/server/types/.directory b/themes/black/server/types/.directory deleted file mode 100644 index 1e65000ea..000000000 --- a/themes/black/server/types/.directory +++ /dev/null @@ -1,4 +0,0 @@ -[Dolphin] -Timestamp=2018,12,17,20,58,27 -Version=3 -ViewMode=1 diff --git a/themes/black/server/types/AuthenticationSession.ts b/themes/black/server/types/AuthenticationSession.ts deleted file mode 100644 index bbed0e715..000000000 --- a/themes/black/server/types/AuthenticationSession.ts +++ /dev/null @@ -1,18 +0,0 @@ -import U2f = require("u2f"); -import { Level } from "../src/lib/authentication/Level"; - -export interface AuthenticationSession { - userid: string; - authentication_level: Level; - keep_me_logged_in: boolean; - last_activity_datetime: number; - identity_check?: { - challenge: string; - userid: string; - }; - register_request?: U2f.Request; - sign_request?: U2f.Request; - email: string; - groups: string[]; - redirect?: string; -} \ No newline at end of file diff --git a/themes/black/server/types/Dependencies.ts b/themes/black/server/types/Dependencies.ts deleted file mode 100644 index f20404dbc..000000000 --- a/themes/black/server/types/Dependencies.ts +++ /dev/null @@ -1,29 +0,0 @@ -import winston = require("winston"); -import speakeasy = require("speakeasy"); -import nodemailer = require("nodemailer"); -import session = require("express-session"); -import nedb = require("nedb"); -import ldapjs = require("ldapjs"); -import u2f = require("u2f"); -import RedisSession = require("connect-redis"); -import Redis = require("redis"); - -export type Speakeasy = typeof speakeasy; -export type Winston = typeof winston; -export type Session = typeof session; -export type Nedb = typeof nedb; -export type Ldapjs = typeof ldapjs; -export type U2f = typeof u2f; -export type ConnectRedis = typeof RedisSession; -export type Redis = typeof Redis; - -export interface GlobalDependencies { - u2f: U2f; - ldapjs: Ldapjs; - session: Session; - Redis: Redis; - ConnectRedis: ConnectRedis; - winston: Winston; - speakeasy: Speakeasy; - nedb: Nedb; -} \ No newline at end of file diff --git a/themes/black/server/types/Identity.ts b/themes/black/server/types/Identity.ts deleted file mode 100644 index e985984e2..000000000 --- a/themes/black/server/types/Identity.ts +++ /dev/null @@ -1,6 +0,0 @@ - - -export interface Identity { - userid: string; - email: string; -} \ No newline at end of file diff --git a/themes/black/server/types/TOTPSecret.ts b/themes/black/server/types/TOTPSecret.ts deleted file mode 100644 index d6775f2f0..000000000 --- a/themes/black/server/types/TOTPSecret.ts +++ /dev/null @@ -1,11 +0,0 @@ - -export interface TOTPSecret { - ascii: string; - hex: string; - base32: string; - qr_code_ascii: string; - qr_code_hex: string; - qr_code_base32: string; - google_auth_qr: string; - otpauth_url: string; - } \ No newline at end of file diff --git a/themes/black/server/types/U2FRegistration.ts b/themes/black/server/types/U2FRegistration.ts deleted file mode 100644 index b6080af07..000000000 --- a/themes/black/server/types/U2FRegistration.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface U2FRegistration { - keyHandle: string; - publicKey: string; -} \ No newline at end of file diff --git a/themes/black/server/types/dovehash.d.ts b/themes/black/server/types/dovehash.d.ts deleted file mode 100644 index c354609c0..000000000 --- a/themes/black/server/types/dovehash.d.ts +++ /dev/null @@ -1,4 +0,0 @@ - -declare module "dovehash" { - function encode(algo: string, text: string): string; -} \ No newline at end of file diff --git a/themes/black/server/types/speakeasy.d.ts b/themes/black/server/types/speakeasy.d.ts deleted file mode 100644 index 6ea06948b..000000000 --- a/themes/black/server/types/speakeasy.d.ts +++ /dev/null @@ -1,96 +0,0 @@ -declare module "speakeasy" { - export = speakeasy - - interface SharedOptions { - encoding?: string - algorithm?: string - } - - interface DigestOptions extends SharedOptions { - secret: string - counter: number - } - - interface HOTPOptions extends SharedOptions { - secret: string - counter: number - digest?: Buffer - digits?: number - } - - interface HOTPVerifyOptions extends SharedOptions { - secret: string - token: string - counter: number - digits?: number - window?: number - } - - interface TOTPOptions extends SharedOptions { - secret: string - time?: number - step?: number - epoch?: number - counter?: number - digits?: number - } - - interface TOTPVerifyOptions extends SharedOptions { - secret: string - token: string - time?: number - step?: number - epoch?: number - counter?: number - digits?: number - window?: number - } - - interface GenerateSecretOptions { - length?: number - symbols?: boolean - otpauth_url?: boolean - name?: string - issuer?: string - } - - interface GeneratedSecret { - ascii: string - hex: string - base32: string - qr_code_ascii: string - qr_code_hex: string - qr_code_base32: string - google_auth_qr: string - otpauth_url: string - } - - interface OTPAuthURLOptions extends SharedOptions { - secret: string - label: string - type?: string - counter?: number - issuer?: string - digits?: number - period?: number - } - - interface Speakeasy { - digest: (options: DigestOptions) => Buffer - hotp: { - (options: HOTPOptions): string, - verifyDelta: (options: HOTPVerifyOptions) => boolean, - verify: (options: HOTPVerifyOptions) => boolean, - } - totp: { - (options: TOTPOptions): string - verifyDelta: (options: TOTPVerifyOptions) => boolean, - verify: (options: TOTPVerifyOptions) => boolean, - } - generateSecret: (options?: GenerateSecretOptions) => GeneratedSecret - generateSecretASCII: (length?: number, symbols?: boolean) => string - otpauthURL: (options: OTPAuthURLOptions) => string - } - - const speakeasy: Speakeasy -} \ No newline at end of file diff --git a/themes/main/client/src/css/.directory b/themes/default/client/src/css/.directory old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/css/.directory rename to themes/default/client/src/css/.directory diff --git a/themes/main/client/src/css/00-bootstrap.min.css b/themes/default/client/src/css/00-bootstrap.min.css old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/css/00-bootstrap.min.css rename to themes/default/client/src/css/00-bootstrap.min.css diff --git a/themes/main/client/src/css/01-main.css b/themes/default/client/src/css/01-main.css old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/css/01-main.css rename to themes/default/client/src/css/01-main.css diff --git a/themes/main/client/src/css/02-login.css b/themes/default/client/src/css/02-login.css old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/css/02-login.css rename to themes/default/client/src/css/02-login.css diff --git a/themes/main/client/src/css/03-errors.css b/themes/default/client/src/css/03-errors.css old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/css/03-errors.css rename to themes/default/client/src/css/03-errors.css diff --git a/themes/main/client/src/css/03-password-reset-form.css b/themes/default/client/src/css/03-password-reset-form.css old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/css/03-password-reset-form.css rename to themes/default/client/src/css/03-password-reset-form.css diff --git a/themes/main/client/src/css/03-password-reset-request.css b/themes/default/client/src/css/03-password-reset-request.css old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/css/03-password-reset-request.css rename to themes/default/client/src/css/03-password-reset-request.css diff --git a/themes/main/client/src/css/03-totp-register.css b/themes/default/client/src/css/03-totp-register.css old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/css/03-totp-register.css rename to themes/default/client/src/css/03-totp-register.css diff --git a/themes/main/client/src/css/03-u2f-register.css b/themes/default/client/src/css/03-u2f-register.css old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/css/03-u2f-register.css rename to themes/default/client/src/css/03-u2f-register.css diff --git a/themes/main/client/src/img/background.svg b/themes/default/client/src/img/background.svg old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/background.svg rename to themes/default/client/src/img/background.svg diff --git a/themes/main/client/src/img/icon.png b/themes/default/client/src/img/icon.png old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/icon.png rename to themes/default/client/src/img/icon.png diff --git a/themes/main/client/src/img/mail.png b/themes/default/client/src/img/mail.png old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/mail.png rename to themes/default/client/src/img/mail.png diff --git a/themes/main/client/src/img/notifications/.directory b/themes/default/client/src/img/notifications/.directory old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/notifications/.directory rename to themes/default/client/src/img/notifications/.directory diff --git a/themes/main/client/src/img/notifications/error.png b/themes/default/client/src/img/notifications/error.png old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/notifications/error.png rename to themes/default/client/src/img/notifications/error.png diff --git a/themes/main/client/src/img/notifications/info.png b/themes/default/client/src/img/notifications/info.png old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/notifications/info.png rename to themes/default/client/src/img/notifications/info.png diff --git a/themes/main/client/src/img/notifications/success.png b/themes/default/client/src/img/notifications/success.png old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/notifications/success.png rename to themes/default/client/src/img/notifications/success.png diff --git a/themes/main/client/src/img/notifications/warning.png b/themes/default/client/src/img/notifications/warning.png old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/notifications/warning.png rename to themes/default/client/src/img/notifications/warning.png diff --git a/themes/main/client/src/img/padlock.png b/themes/default/client/src/img/padlock.png old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/padlock.png rename to themes/default/client/src/img/padlock.png diff --git a/themes/main/client/src/img/password.png b/themes/default/client/src/img/password.png old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/password.png rename to themes/default/client/src/img/password.png diff --git a/themes/main/client/src/img/pendrive.png b/themes/default/client/src/img/pendrive.png old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/pendrive.png rename to themes/default/client/src/img/pendrive.png diff --git a/themes/main/client/src/img/stores/.directory b/themes/default/client/src/img/stores/.directory old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/stores/.directory rename to themes/default/client/src/img/stores/.directory diff --git a/themes/main/client/src/img/stores/applestore-badge.svg b/themes/default/client/src/img/stores/applestore-badge.svg old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/stores/applestore-badge.svg rename to themes/default/client/src/img/stores/applestore-badge.svg diff --git a/themes/main/client/src/img/stores/googleplay-badge.svg b/themes/default/client/src/img/stores/googleplay-badge.svg old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/stores/googleplay-badge.svg rename to themes/default/client/src/img/stores/googleplay-badge.svg diff --git a/themes/main/client/src/img/success.png b/themes/default/client/src/img/success.png old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/success.png rename to themes/default/client/src/img/success.png diff --git a/themes/main/client/src/img/user.png b/themes/default/client/src/img/user.png old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/user.png rename to themes/default/client/src/img/user.png diff --git a/themes/main/client/src/img/warning.png b/themes/default/client/src/img/warning.png old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/img/warning.png rename to themes/default/client/src/img/warning.png diff --git a/themes/main/client/src/thirdparties/qrcode.min.js b/themes/default/client/src/thirdparties/qrcode.min.js old mode 100644 new mode 100755 similarity index 100% rename from themes/main/client/src/thirdparties/qrcode.min.js rename to themes/default/client/src/thirdparties/qrcode.min.js diff --git a/themes/main/server/.directory b/themes/default/server/.directory old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/.directory rename to themes/default/server/.directory diff --git a/themes/main/server/src/resources/email-template.ejs b/themes/default/server/src/resources/email-template.ejs old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/src/resources/email-template.ejs rename to themes/default/server/src/resources/email-template.ejs diff --git a/themes/main/server/src/views/already-logged-in.pug b/themes/default/server/src/views/already-logged-in.pug old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/src/views/already-logged-in.pug rename to themes/default/server/src/views/already-logged-in.pug diff --git a/themes/main/server/src/views/errors/.directory b/themes/default/server/src/views/errors/.directory old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/src/views/errors/.directory rename to themes/default/server/src/views/errors/.directory diff --git a/themes/main/server/src/views/errors/401.pug b/themes/default/server/src/views/errors/401.pug old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/src/views/errors/401.pug rename to themes/default/server/src/views/errors/401.pug diff --git a/themes/main/server/src/views/errors/403.pug b/themes/default/server/src/views/errors/403.pug old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/src/views/errors/403.pug rename to themes/default/server/src/views/errors/403.pug diff --git a/themes/main/server/src/views/errors/404.pug b/themes/default/server/src/views/errors/404.pug old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/src/views/errors/404.pug rename to themes/default/server/src/views/errors/404.pug diff --git a/themes/main/server/src/views/firstfactor.pug b/themes/default/server/src/views/firstfactor.pug old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/src/views/firstfactor.pug rename to themes/default/server/src/views/firstfactor.pug diff --git a/themes/main/server/src/views/layout/layout.pug b/themes/default/server/src/views/layout/layout.pug old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/src/views/layout/layout.pug rename to themes/default/server/src/views/layout/layout.pug diff --git a/themes/main/server/src/views/need-identity-validation.pug b/themes/default/server/src/views/need-identity-validation.pug old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/src/views/need-identity-validation.pug rename to themes/default/server/src/views/need-identity-validation.pug diff --git a/themes/main/server/src/views/password-reset-form.pug b/themes/default/server/src/views/password-reset-form.pug old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/src/views/password-reset-form.pug rename to themes/default/server/src/views/password-reset-form.pug diff --git a/themes/main/server/src/views/password-reset-request.pug b/themes/default/server/src/views/password-reset-request.pug old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/src/views/password-reset-request.pug rename to themes/default/server/src/views/password-reset-request.pug diff --git a/themes/main/server/src/views/secondfactor.pug b/themes/default/server/src/views/secondfactor.pug old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/src/views/secondfactor.pug rename to themes/default/server/src/views/secondfactor.pug diff --git a/themes/main/server/src/views/totp-register.pug b/themes/default/server/src/views/totp-register.pug old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/src/views/totp-register.pug rename to themes/default/server/src/views/totp-register.pug diff --git a/themes/main/server/src/views/u2f-register.pug b/themes/default/server/src/views/u2f-register.pug old mode 100644 new mode 100755 similarity index 100% rename from themes/main/server/src/views/u2f-register.pug rename to themes/default/server/src/views/u2f-register.pug diff --git a/themes/main/client/src/index.ts b/themes/main/client/src/index.ts deleted file mode 100644 index 6c22d17c7..000000000 --- a/themes/main/client/src/index.ts +++ /dev/null @@ -1,34 +0,0 @@ - -import FirstFactorValidator = require("./lib/firstfactor/FirstFactorValidator"); - -import FirstFactor from "./lib/firstfactor/index"; -import SecondFactor from "./lib/secondfactor/index"; -import TOTPRegister from "./lib/totp-register/totp-register"; -import U2fRegister from "./lib/u2f-register/u2f-register"; -import ResetPasswordRequest from "./lib/reset-password/reset-password-request"; -import ResetPasswordForm from "./lib/reset-password/reset-password-form"; -import jslogger = require("js-logger"); -import jQuery = require("jquery"); -import Endpoints = require("../../shared/api"); - -jslogger.useDefaults(); -jslogger.setLevel(jslogger.INFO); - -(function () { - (window).jQuery = jQuery; - require("bootstrap"); - - jQuery('[data-toggle="tooltip"]').tooltip(); - if (window.location.pathname == Endpoints.FIRST_FACTOR_GET) - FirstFactor(window, jQuery, FirstFactorValidator, jslogger); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_GET) - SecondFactor(window, jQuery); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET) - TOTPRegister(window, jQuery); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET) - U2fRegister(window, jQuery); - else if (window.location.pathname == Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET) - ResetPasswordForm(window, jQuery); - else if (window.location.pathname == Endpoints.RESET_PASSWORD_REQUEST_GET) - ResetPasswordRequest(window, jQuery); -})(); diff --git a/themes/main/client/src/lib/GetPromised.ts b/themes/main/client/src/lib/GetPromised.ts deleted file mode 100644 index 779139654..000000000 --- a/themes/main/client/src/lib/GetPromised.ts +++ /dev/null @@ -1,14 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -export default function ($: JQueryStatic, url: string, data: Object, fn: any, - dataType: string): BluebirdPromise { - return new BluebirdPromise((resolve, reject) => { - $.get(url, {}, undefined, dataType) - .done((data: any) => { - resolve(data); - }) - .fail((xhr: JQueryXHR, textStatus: string) => { - reject(textStatus); - }); - }); -} \ No newline at end of file diff --git a/themes/main/client/src/lib/INotifier.ts b/themes/main/client/src/lib/INotifier.ts deleted file mode 100644 index df947538f..000000000 --- a/themes/main/client/src/lib/INotifier.ts +++ /dev/null @@ -1,14 +0,0 @@ - -declare type Handler = () => void; - -export interface Handlers { - onFadedIn: Handler; - onFadedOut: Handler; -} - -export interface INotifier { - success(msg: string, handlers?: Handlers): void; - error(msg: string, handlers?: Handlers): void; - warning(msg: string, handlers?: Handlers): void; - info(msg: string, handlers?: Handlers): void; -} \ No newline at end of file diff --git a/themes/main/client/src/lib/Notifier.ts b/themes/main/client/src/lib/Notifier.ts deleted file mode 100644 index c0252b9b9..000000000 --- a/themes/main/client/src/lib/Notifier.ts +++ /dev/null @@ -1,83 +0,0 @@ - - -import util = require("util"); -import { INotifier, Handlers } from "./INotifier"; - -class NotificationEvent { - private element: JQuery; - private message: string; - private statusType: string; - private timeoutId: any; - - constructor(element: JQuery, msg: string, statusType: string) { - this.message = msg; - this.statusType = statusType; - this.element = element; - } - - private clearNotification() { - this.element.removeClass(this.statusType); - this.element.html(""); - } - - start(handlers?: Handlers) { - const that = this; - const FADE_TIME = 500; - const html = util.format('status %s\ - %s', this.statusType, this.statusType, this.message); - this.element.html(html); - this.element.addClass(this.statusType); - this.element.fadeIn(FADE_TIME, function () { - if (handlers) - handlers.onFadedIn(); - }); - - this.timeoutId = setTimeout(function () { - that.element.fadeOut(FADE_TIME, function () { - that.clearNotification(); - if (handlers) - handlers.onFadedOut(); - }); - }, 4000); - } - - interrupt() { - this.clearNotification(); - this.element.hide(); - clearTimeout(this.timeoutId); - } -} - -export class Notifier implements INotifier { - private element: JQuery; - private onGoingEvent: NotificationEvent; - - constructor(selector: string, $: JQueryStatic) { - this.element = $(selector); - this.onGoingEvent = undefined; - } - - private displayAndFadeout(msg: string, statusType: string, handlers?: Handlers): void { - if (this.onGoingEvent) - this.onGoingEvent.interrupt(); - - this.onGoingEvent = new NotificationEvent(this.element, msg, statusType); - this.onGoingEvent.start(handlers); - } - - success(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "success", handlers); - } - - error(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "error", handlers); - } - - warning(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "warning", handlers); - } - - info(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "info", handlers); - } -} \ No newline at end of file diff --git a/themes/main/client/src/lib/QueryParametersRetriever.ts b/themes/main/client/src/lib/QueryParametersRetriever.ts deleted file mode 100644 index a529adb6e..000000000 --- a/themes/main/client/src/lib/QueryParametersRetriever.ts +++ /dev/null @@ -1,12 +0,0 @@ - -export class QueryParametersRetriever { - static get(name: string, url?: string): string { - if (!url) url = window.location.href; - name = name.replace(/[\[\]]/g, "\\$&"); - const regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), - results = regex.exec(url); - if (!results) return undefined; - if (!results[2]) return ""; - return decodeURIComponent(results[2].replace(/\+/g, " ")); - } -} \ No newline at end of file diff --git a/themes/main/client/src/lib/SafeRedirect.ts b/themes/main/client/src/lib/SafeRedirect.ts deleted file mode 100644 index 7e7684b8a..000000000 --- a/themes/main/client/src/lib/SafeRedirect.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BelongToDomain } from "../../../shared/BelongToDomain"; - -export function SafeRedirect(url: string, cb: () => void): void { - const domain = window.location.hostname.split(".").slice(-2).join("."); - if (url.startsWith("/") || BelongToDomain(url, domain)) { - window.location.href = url; - return; - } - cb(); -} \ No newline at end of file diff --git a/themes/main/client/src/lib/firstfactor/FirstFactorValidator.ts b/themes/main/client/src/lib/firstfactor/FirstFactorValidator.ts deleted file mode 100644 index eaa496fdd..000000000 --- a/themes/main/client/src/lib/firstfactor/FirstFactorValidator.ts +++ /dev/null @@ -1,46 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import Constants = require("../../../../shared/constants"); -import Util = require("util"); -import UserMessages = require("../../../../shared/UserMessages"); - -export function validate(username: string, password: string, - keepMeLoggedIn: boolean, redirectUrl: string, $: JQueryStatic) - : BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - let url: string; - if (redirectUrl != undefined) { - const redirectParam = Util.format("%s=%s", Constants.REDIRECT_QUERY_PARAM, redirectUrl); - url = Util.format("%s?%s", Endpoints.FIRST_FACTOR_POST, redirectParam); - } - else { - url = Util.format("%s", Endpoints.FIRST_FACTOR_POST); - } - - const data: any = { - username: username, - password: password, - }; - - if (keepMeLoggedIn) { - data.keepMeLoggedIn = "true"; - } - - $.ajax({ - method: "POST", - url: url, - data: data - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(body.redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(UserMessages.AUTHENTICATION_FAILED)); - }); - }); -} diff --git a/themes/main/client/src/lib/firstfactor/UISelectors.ts b/themes/main/client/src/lib/firstfactor/UISelectors.ts deleted file mode 100644 index 0e971b3c3..000000000 --- a/themes/main/client/src/lib/firstfactor/UISelectors.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export const USERNAME_FIELD_ID = "#username"; -export const PASSWORD_FIELD_ID = "#password"; -export const SIGN_IN_BUTTON_ID = "#signin"; -export const KEEP_ME_LOGGED_IN_ID = "#keep_me_logged_in"; diff --git a/themes/main/client/src/lib/firstfactor/index.ts b/themes/main/client/src/lib/firstfactor/index.ts deleted file mode 100644 index 24affee2d..000000000 --- a/themes/main/client/src/lib/firstfactor/index.ts +++ /dev/null @@ -1,49 +0,0 @@ -import FirstFactorValidator = require("./FirstFactorValidator"); -import JSLogger = require("js-logger"); -import UISelectors = require("./UISelectors"); -import { Notifier } from "../Notifier"; -import { QueryParametersRetriever } from "../QueryParametersRetriever"; -import Constants = require("../../../../shared/constants"); -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic, - firstFactorValidator: typeof FirstFactorValidator, jslogger: typeof JSLogger) { - - const notifier = new Notifier(".notification", $); - - function onFormSubmitted() { - const username: string = $(UISelectors.USERNAME_FIELD_ID).val() as string; - const password: string = $(UISelectors.PASSWORD_FIELD_ID).val() as string; - const keepMeLoggedIn: boolean = $(UISelectors.KEEP_ME_LOGGED_IN_ID).is(":checked"); - - $("form").css("opacity", 0.5); - $("input,button").attr("disabled", "true"); - $(UISelectors.SIGN_IN_BUTTON_ID).text("Please wait..."); - - const redirectUrl = QueryParametersRetriever.get(Constants.REDIRECT_QUERY_PARAM); - firstFactorValidator.validate(username, password, keepMeLoggedIn, redirectUrl, $) - .then(onFirstFactorSuccess, onFirstFactorFailure); - return false; - } - - function onFirstFactorSuccess(redirectUrl: string) { - SafeRedirect(redirectUrl, () => { - notifier.error("Cannot redirect to an external domain."); - }); - } - - function onFirstFactorFailure(err: Error) { - $("input,button").removeAttr("disabled"); - $("form").css("opacity", 1); - notifier.error(UserMessages.AUTHENTICATION_FAILED); - $(UISelectors.PASSWORD_FIELD_ID).select(); - $(UISelectors.SIGN_IN_BUTTON_ID).text("Sign in"); - } - - $(window.document).ready(function () { - $("form").on("submit", onFormSubmitted); - }); -} - diff --git a/themes/main/client/src/lib/reset-password/constants.ts b/themes/main/client/src/lib/reset-password/constants.ts deleted file mode 100644 index d48d4e67d..000000000 --- a/themes/main/client/src/lib/reset-password/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const FORM_SELECTOR = ".form-signin"; \ No newline at end of file diff --git a/themes/main/client/src/lib/reset-password/reset-password-form.ts b/themes/main/client/src/lib/reset-password/reset-password-form.ts deleted file mode 100644 index b94279cde..000000000 --- a/themes/main/client/src/lib/reset-password/reset-password-form.ts +++ /dev/null @@ -1,57 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); - -import Constants = require("./constants"); -import { Notifier } from "../Notifier"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function modifyPassword(newPassword: string) { - return new BluebirdPromise(function (resolve, reject) { - $.post(Endpoints.RESET_PASSWORD_FORM_POST, { - password: newPassword, - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(body); - }) - .fail(function (xhr, status) { - reject(status); - }); - }); - } - - function onFormSubmitted() { - const password1 = $("#password1").val() as string; - const password2 = $("#password2").val() as string; - - if (!password1 || !password2) { - notifier.warning(UserMessages.MISSING_PASSWORD); - return false; - } - - if (password1 != password2) { - notifier.warning(UserMessages.DIFFERENT_PASSWORDS); - return false; - } - - modifyPassword(password1) - .then(function () { - window.location.href = Endpoints.FIRST_FACTOR_GET; - }) - .error(function () { - notifier.error(UserMessages.RESET_PASSWORD_FAILED); - }); - return false; - } - - $(document).ready(function () { - $(Constants.FORM_SELECTOR).on("submit", onFormSubmitted); - }); -} diff --git a/themes/main/client/src/lib/reset-password/reset-password-request.ts b/themes/main/client/src/lib/reset-password/reset-password-request.ts deleted file mode 100644 index 846226d75..000000000 --- a/themes/main/client/src/lib/reset-password/reset-password-request.ts +++ /dev/null @@ -1,56 +0,0 @@ - -import BluebirdPromise = require("bluebird"); - -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import Constants = require("./constants"); -import jslogger = require("js-logger"); -import { Notifier } from "../Notifier"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function requestPasswordReset(username: string) { - return new BluebirdPromise(function (resolve, reject) { - $.get(Endpoints.RESET_PASSWORD_IDENTITY_START_GET, { - userid: username, - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); - } - - function onFormSubmitted() { - const username = $("#username").val() as string; - - if (!username) { - notifier.warning(UserMessages.MISSING_USERNAME); - return; - } - - requestPasswordReset(username) - .then(function () { - notifier.success(UserMessages.MAIL_SENT); - setTimeout(function () { - window.location.replace(Endpoints.FIRST_FACTOR_GET); - }, 1000); - }) - .error(function () { - notifier.error(UserMessages.MAIL_NOT_SENT); - }); - return false; - } - - $(document).ready(function () { - $(Constants.FORM_SELECTOR).on("submit", onFormSubmitted); - }); -} - diff --git a/themes/main/client/src/lib/secondfactor/TOTPValidator.ts b/themes/main/client/src/lib/secondfactor/TOTPValidator.ts deleted file mode 100644 index 5394139a6..000000000 --- a/themes/main/client/src/lib/secondfactor/TOTPValidator.ts +++ /dev/null @@ -1,28 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; - -export function validate(token: string, $: JQueryStatic): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.ajax({ - url: Endpoints.SECOND_FACTOR_TOTP_POST, - data: { - token: token, - }, - method: "POST", - dataType: "json" - } as JQueryAjaxSettings) - .done(function (body: RedirectionMessage | ErrorMessage) { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); -} \ No newline at end of file diff --git a/themes/main/client/src/lib/secondfactor/U2FValidator.ts b/themes/main/client/src/lib/secondfactor/U2FValidator.ts deleted file mode 100644 index 5812922f7..000000000 --- a/themes/main/client/src/lib/secondfactor/U2FValidator.ts +++ /dev/null @@ -1,42 +0,0 @@ -import U2f = require("u2f"); -import U2fApi from "u2f-api"; -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { INotifier } from "../INotifier"; -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; -import GetPromised from "../GetPromised"; - -function finishU2fAuthentication(responseData: U2fApi.SignResponse, - $: JQueryStatic): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.ajax({ - url: Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - data: responseData, - method: "POST", - dataType: "json" - } as JQueryAjaxSettings) - .done(function (body: RedirectionMessage | ErrorMessage) { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); -} - -export function validate($: JQueryStatic): BluebirdPromise { - return GetPromised($, Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, {}, - undefined, "json") - .then(function (signRequest: U2f.Request) { - return U2fApi.sign(signRequest, 60); - }) - .then(function (signResponse: U2fApi.SignResponse) { - return finishU2fAuthentication(signResponse, $); - }); -} diff --git a/themes/main/client/src/lib/secondfactor/constants.ts b/themes/main/client/src/lib/secondfactor/constants.ts deleted file mode 100644 index 50bba7571..000000000 --- a/themes/main/client/src/lib/secondfactor/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ - -export const TOTP_FORM_SELECTOR = ".form-signin.totp"; -export const TOTP_TOKEN_SELECTOR = ".form-signin #token"; diff --git a/themes/main/client/src/lib/secondfactor/index.ts b/themes/main/client/src/lib/secondfactor/index.ts deleted file mode 100644 index 279723dce..000000000 --- a/themes/main/client/src/lib/secondfactor/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import TOTPValidator = require("./TOTPValidator"); -import U2FValidator = require("./U2FValidator"); -import ClientConstants = require("./constants"); -import { Notifier } from "../Notifier"; -import { QueryParametersRetriever } from "../QueryParametersRetriever"; -import UserMessages = require("../../../../shared/UserMessages"); -import SharedConstants = require("../../../../shared/constants"); -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function onAuthenticationSuccess(serverRedirectUrl: string) { - const queryRedirectUrl = QueryParametersRetriever.get(SharedConstants.REDIRECT_QUERY_PARAM); - if (queryRedirectUrl) { - SafeRedirect(queryRedirectUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - } else if (serverRedirectUrl) { - SafeRedirect(serverRedirectUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - } else { - notifier.success(UserMessages.AUTHENTICATION_SUCCEEDED); - } - } - - function onSecondFactorTotpSuccess(redirectUrl: string) { - onAuthenticationSuccess(redirectUrl); - } - - function onSecondFactorTotpFailure(err: Error) { - notifier.error(UserMessages.AUTHENTICATION_TOTP_FAILED); - } - - function onU2fAuthenticationSuccess(redirectUrl: string) { - onAuthenticationSuccess(redirectUrl); - } - - function onU2fAuthenticationFailure() { - // TODO(clems4ever): we should not display this error message until a device - // is registered. - // notifier.error(UserMessages.AUTHENTICATION_U2F_FAILED); - } - - function onTOTPFormSubmitted(): boolean { - const token = $(ClientConstants.TOTP_TOKEN_SELECTOR).val() as string; - TOTPValidator.validate(token, $) - .then(onSecondFactorTotpSuccess) - .catch(onSecondFactorTotpFailure); - return false; - } - - $(window.document).ready(function () { - $(ClientConstants.TOTP_FORM_SELECTOR).on("submit", onTOTPFormSubmitted); - U2FValidator.validate($) - .then(onU2fAuthenticationSuccess, onU2fAuthenticationFailure); - }); -} \ No newline at end of file diff --git a/themes/main/client/src/lib/totp-register/totp-register.ts b/themes/main/client/src/lib/totp-register/totp-register.ts deleted file mode 100644 index 6a9aa7ee0..000000000 --- a/themes/main/client/src/lib/totp-register/totp-register.ts +++ /dev/null @@ -1,11 +0,0 @@ - -import jslogger = require("js-logger"); -import UISelector = require("./ui-selector"); - -export default function(window: Window, $: JQueryStatic) { - jslogger.debug("Creating QRCode from OTPAuth url"); - const qrcode = $(UISelector.QRCODE_ID_SELECTOR); - const val = qrcode.text(); - qrcode.empty(); - new (window as any).QRCode(qrcode.get(0), val); -} diff --git a/themes/main/client/src/lib/totp-register/ui-selector.ts b/themes/main/client/src/lib/totp-register/ui-selector.ts deleted file mode 100644 index 9d43fabea..000000000 --- a/themes/main/client/src/lib/totp-register/ui-selector.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const QRCODE_ID_SELECTOR = "#qrcode"; \ No newline at end of file diff --git a/themes/main/client/src/lib/u2f-register/u2f-register.ts b/themes/main/client/src/lib/u2f-register/u2f-register.ts deleted file mode 100644 index abf40ee05..000000000 --- a/themes/main/client/src/lib/u2f-register/u2f-register.ts +++ /dev/null @@ -1,56 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import U2f = require("u2f"); -import * as U2fApi from "u2f-api"; -import { Notifier } from "../Notifier"; -import GetPromised from "../GetPromised"; -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function checkRegistration(regResponse: U2fApi.RegisterResponse): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, regResponse, undefined, "json") - .done((body: RedirectionMessage | ErrorMessage) => { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail((xhr, status) => { - reject(new Error("Failed to register device.")); - }); - }); - } - - function requestRegistration(): BluebirdPromise { - return GetPromised($, Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, {}, - undefined, "json") - .then((registrationRequest: U2f.Request) => { - return U2fApi.register(registrationRequest, [], 60); - }) - .then((res) => checkRegistration(res)); - } - - function onRegisterFailure(err: Error) { - notifier.error(UserMessages.REGISTRATION_U2F_FAILED); - } - - $(document).ready(function () { - requestRegistration() - .then((redirectionUrl: string) => { - SafeRedirect(redirectionUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - }) - .catch((err) => { - onRegisterFailure(err); - }); - }); -} diff --git a/themes/main/client/test/Notifier.test.ts b/themes/main/client/test/Notifier.test.ts deleted file mode 100644 index 70bfea146..000000000 --- a/themes/main/client/test/Notifier.test.ts +++ /dev/null @@ -1,71 +0,0 @@ - -import Assert = require("assert"); -import Sinon = require("sinon"); -import JQueryMock = require("./mocks/jquery"); - -import { Notifier } from "../src/lib/Notifier"; - -describe("test notifier", function() { - const SELECTOR = "dummy-selector"; - const MESSAGE = "This is a message"; - let jqueryMock: { jquery: JQueryMock.JQueryMock, element: JQueryMock.JQueryElementsMock }; - let clock: any; - - beforeEach(function() { - jqueryMock = JQueryMock.JQueryMock(); - clock = Sinon.useFakeTimers(); - }); - - afterEach(function() { - clock.restore(); - }); - - function should_fade_in_and_out_on_notification(notificationType: string): void { - const delayReturn = { - fadeOut: Sinon.stub() - }; - - jqueryMock.element.fadeIn.yields(); - - function onFadedInCallback() { - Assert(jqueryMock.element.fadeIn.calledOnce); - Assert(jqueryMock.element.addClass.calledWith(notificationType)); - Assert(!jqueryMock.element.removeClass.calledWith(notificationType)); - clock.tick(10 * 1000); - } - - function onFadedOutCallback() { - Assert(jqueryMock.element.removeClass.calledWith(notificationType)); - Assert(jqueryMock.element.fadeOut.calledOnce); - } - - const notifier = new Notifier(SELECTOR, jqueryMock.jquery as any); - - // Call the method by its name... Bad but allows code reuse. - (notifier as any)[notificationType](MESSAGE, { - onFadedIn: onFadedInCallback, - onFadedOut: onFadedOutCallback - }); - - clock.tick(510); - - Assert(jqueryMock.element.fadeIn.calledOnce); - } - - - it("should fade in and fade out an error message", function() { - should_fade_in_and_out_on_notification("error"); - }); - - it("should fade in and fade out an info message", function() { - should_fade_in_and_out_on_notification("info"); - }); - - it("should fade in and fade out an warning message", function() { - should_fade_in_and_out_on_notification("warning"); - }); - - it("should fade in and fade out an success message", function() { - should_fade_in_and_out_on_notification("success"); - }); -}); \ No newline at end of file diff --git a/themes/main/client/test/firstfactor/FirstFactorValidator.test.ts b/themes/main/client/test/firstfactor/FirstFactorValidator.test.ts deleted file mode 100644 index 027bc71d8..000000000 --- a/themes/main/client/test/firstfactor/FirstFactorValidator.test.ts +++ /dev/null @@ -1,46 +0,0 @@ - -import FirstFactorValidator = require("../../src/lib/firstfactor/FirstFactorValidator"); -import JQueryMock = require("../mocks/jquery"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -describe("test FirstFactorValidator", function () { - it("should validate first factor successfully", () => { - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.done.yields({ redirect: "http://redirect" }); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return FirstFactorValidator.validate("username", "password", false, - "http://redirect", jqueryMock.jquery as any); - }); - - function should_fail_first_factor_validation(errorMessage: string) { - const xhr = { - status: 401 - }; - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.fail.yields(xhr, errorMessage); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return FirstFactorValidator.validate("username", "password", false, - "http://redirect", jqueryMock.jquery as any) - .then(function () { - return BluebirdPromise.reject(new Error("First factor validation successfully finished while it should have not.")); - }, function (err: Error) { - Assert.equal(errorMessage, err.message); - return BluebirdPromise.resolve(); - }); - } - - describe("should fail first factor validation", () => { - it("should fail with error", () => { - return should_fail_first_factor_validation("Authentication failed. Please check your credentials."); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/client/test/mocks/NotifierStub.ts b/themes/main/client/test/mocks/NotifierStub.ts deleted file mode 100644 index 9c268d66d..000000000 --- a/themes/main/client/test/mocks/NotifierStub.ts +++ /dev/null @@ -1,33 +0,0 @@ - -import Sinon = require("sinon"); -import { INotifier } from "../../src/lib/INotifier"; - -export class NotifierStub implements INotifier { - successStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - warnStub: Sinon.SinonStub; - infoStub: Sinon.SinonStub; - - constructor() { - this.successStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - this.warnStub = Sinon.stub(); - this.infoStub = Sinon.stub(); - } - - success(msg: string) { - this.successStub(); - } - - error(msg: string) { - this.errorStub(); - } - - warning(msg: string) { - this.warnStub(); - } - - info(msg: string) { - this.infoStub(); - } -} \ No newline at end of file diff --git a/themes/main/client/test/mocks/jquery.ts b/themes/main/client/test/mocks/jquery.ts deleted file mode 100644 index 273f90861..000000000 --- a/themes/main/client/test/mocks/jquery.ts +++ /dev/null @@ -1,59 +0,0 @@ - -import sinon = require("sinon"); -import jquery = require("jquery"); - - -export interface JQueryMock extends sinon.SinonStub { - get: sinon.SinonStub; - post: sinon.SinonStub; - ajax: sinon.SinonStub; - notify: sinon.SinonStub; -} - -export interface JQueryElementsMock { - ready: sinon.SinonStub; - show: sinon.SinonStub; - hide: sinon.SinonStub; - html: sinon.SinonStub; - addClass: sinon.SinonStub; - removeClass: sinon.SinonStub; - fadeIn: sinon.SinonStub; - fadeOut: sinon.SinonStub; - on: sinon.SinonStub; -} - -export interface JQueryDeferredMock { - done: sinon.SinonStub; - fail: sinon.SinonStub; -} - -export function JQueryMock(): { jquery: JQueryMock, element: JQueryElementsMock } { - const jquery = sinon.stub() as any; - const jqueryInstance: JQueryElementsMock = { - ready: sinon.stub(), - show: sinon.stub(), - hide: sinon.stub(), - html: sinon.stub(), - addClass: sinon.stub(), - removeClass: sinon.stub(), - fadeIn: sinon.stub(), - fadeOut: sinon.stub(), - on: sinon.stub() - }; - jquery.ajax = sinon.stub(); - jquery.get = sinon.stub(); - jquery.post = sinon.stub(); - jquery.notify = sinon.stub(); - jquery.returns(jqueryInstance); - return { - jquery: jquery, - element: jqueryInstance - }; -} - -export function JQueryDeferredMock(): JQueryDeferredMock { - return { - done: sinon.stub(), - fail: sinon.stub() - }; -} diff --git a/themes/main/client/test/mocks/u2f-api.ts b/themes/main/client/test/mocks/u2f-api.ts deleted file mode 100644 index d123f6a95..000000000 --- a/themes/main/client/test/mocks/u2f-api.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import sinon = require("sinon"); - -export interface U2FApiMock { - sign: sinon.SinonStub; - register: sinon.SinonStub; -} - -export function U2FApiMock(): U2FApiMock { - return { - sign: sinon.stub(), - register: sinon.stub() - }; -} \ No newline at end of file diff --git a/themes/main/client/test/secondfactor/TOTPValidator.test.ts b/themes/main/client/test/secondfactor/TOTPValidator.test.ts deleted file mode 100644 index 5dd6f15c3..000000000 --- a/themes/main/client/test/secondfactor/TOTPValidator.test.ts +++ /dev/null @@ -1,37 +0,0 @@ - -import TOTPValidator = require("../../src/lib/secondfactor/TOTPValidator"); -import JQueryMock = require("../mocks/jquery"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -describe("test TOTPValidator", function () { - it("should initiate an identity check successfully", () => { - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.done.yields({ redirect: "https://home.test.url" }); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return TOTPValidator.validate("totp_token", jqueryMock.jquery as any); - }); - - it("should fail validating TOTP token", () => { - const errorMessage = "Error while validating TOTP token"; - - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.fail.yields(undefined, errorMessage); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return TOTPValidator.validate("totp_token", jqueryMock.jquery as any) - .then(function () { - return BluebirdPromise.reject(new Error("Registration successfully finished while it should have not.")); - }, function (err: Error) { - Assert.equal(errorMessage, err.message); - return BluebirdPromise.resolve(); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/client/test/totp-register/totp-register.test.ts b/themes/main/client/test/totp-register/totp-register.test.ts deleted file mode 100644 index 86fc455a1..000000000 --- a/themes/main/client/test/totp-register/totp-register.test.ts +++ /dev/null @@ -1,31 +0,0 @@ - -import sinon = require("sinon"); -import assert = require("assert"); - -import UISelector = require("../../src/lib/totp-register/ui-selector"); -import TOTPRegister = require("../../src/lib/totp-register/totp-register"); - -describe("test totp-register", function() { - let jqueryMock: any; - let windowMock: any; - before(function() { - jqueryMock = sinon.stub(); - windowMock = { - QRCode: sinon.spy() - }; - }); - - it("should create qrcode in page", function() { - const mock = { - text: sinon.stub(), - empty: sinon.stub(), - get: sinon.stub() - }; - jqueryMock.withArgs(UISelector.QRCODE_ID_SELECTOR).returns(mock); - - TOTPRegister.default(windowMock, jqueryMock); - - assert(mock.text.calledOnce); - assert(mock.empty.calledOnce); - }); -}); \ No newline at end of file diff --git a/themes/main/client/tsconfig.json b/themes/main/client/tsconfig.json deleted file mode 100644 index 0bb4d62ff..000000000 --- a/themes/main/client/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "moduleResolution": "node", - "noImplicitAny": true, - "sourceMap": true, - "removeComments": true, - "outDir": "../dist", - "baseUrl": ".", - "paths": { - "*": [ - "./types/*", - "../shared/types/*" - ] - } - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "test/**/*" - ] -} diff --git a/themes/main/client/tslint.json b/themes/main/client/tslint.json deleted file mode 100644 index c2c1b7501..000000000 --- a/themes/main/client/tslint.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "no-var-keyword": true, - "quotemark": [ - true, - "double", - "avoid-escape" - ], - "semicolon": [ - true, - "always", - "ignore-bound-class-methods" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-module", - "check-separator", - "check-type" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "onespace", - "index-signature": "onespace", - "parameter": "onespace", - "property-declaration": "onespace", - "variable-declaration": "onespace" - } - ], - "no-internal-module": true, - "no-trailing-whitespace": true, - "no-null-keyword": true, - "prefer-const": true, - "jsdoc-format": true - } -} diff --git a/themes/main/server/src/index.ts b/themes/main/server/src/index.ts deleted file mode 100755 index fcbf4d022..000000000 --- a/themes/main/server/src/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -#! /usr/bin/env node - -import Server from "./lib/Server"; -import { GlobalDependencies } from "../types/Dependencies"; -import YAML = require("yamljs"); - -const configurationFilepath = process.argv[2]; -if (!configurationFilepath) { - console.log("No config file has been provided."); - console.log("Usage: authelia "); - process.exit(0); -} - -const yamlContent = YAML.load(configurationFilepath); - -const deps: GlobalDependencies = { - u2f: require("u2f"), - ldapjs: require("ldapjs"), - session: require("express-session"), - winston: require("winston"), - speakeasy: require("speakeasy"), - nedb: require("nedb"), - ConnectRedis: require("connect-redis"), - Redis: require("redis") -}; - -const server = new Server(deps); -server.start(yamlContent, deps); diff --git a/themes/main/server/src/lib/.directory b/themes/main/server/src/lib/.directory deleted file mode 100644 index 006b379ad..000000000 --- a/themes/main/server/src/lib/.directory +++ /dev/null @@ -1,4 +0,0 @@ -[Dolphin] -Timestamp=2018,12,17,20,59,13 -Version=3 -ViewMode=1 diff --git a/themes/main/server/src/lib/AuthenticationSessionHandler.ts b/themes/main/server/src/lib/AuthenticationSessionHandler.ts deleted file mode 100644 index 57361bf8a..000000000 --- a/themes/main/server/src/lib/AuthenticationSessionHandler.ts +++ /dev/null @@ -1,45 +0,0 @@ - - -import express = require("express"); -import U2f = require("u2f"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { IRequestLogger } from "./logging/IRequestLogger"; -import { Level } from "./authentication/Level"; - -const INITIAL_AUTHENTICATION_SESSION: AuthenticationSession = { - keep_me_logged_in: false, - authentication_level: Level.NOT_AUTHENTICATED, - last_activity_datetime: undefined, - userid: undefined, - email: undefined, - groups: [], - register_request: undefined, - sign_request: undefined, - identity_check: undefined, - redirect: undefined -}; - -export class AuthenticationSessionHandler { - static reset(req: express.Request): void { - req.session.auth = Object.assign({}, INITIAL_AUTHENTICATION_SESSION, {}); - - // Initialize last activity with current time - req.session.auth.last_activity_datetime = new Date().getTime(); - } - - static get(req: express.Request, logger: IRequestLogger): AuthenticationSession { - if (!req.session) { - const errorMsg = "Something is wrong with session cookies. Please check Redis is running and Authelia can connect to it."; - logger.error(req, errorMsg); - throw new Error(errorMsg); - } - - if (!req.session.auth) { - logger.debug(req, "Authentication session %s was undefined. Resetting.", req.sessionID); - AuthenticationSessionHandler.reset(req); - } - - return req.session.auth; - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/ErrorReplies.ts b/themes/main/server/src/lib/ErrorReplies.ts deleted file mode 100644 index f1c5f4fd1..000000000 --- a/themes/main/server/src/lib/ErrorReplies.ts +++ /dev/null @@ -1,49 +0,0 @@ -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import { IRequestLogger } from "./logging/IRequestLogger"; - -function replyWithError(req: express.Request, res: express.Response, - code: number, logger: IRequestLogger, body?: Object): (err: Error) => void { - return function (err: Error): void { - if (req.originalUrl.startsWith("/api/") || code == 200) { - logger.error(req, "Reply with error %d: %s", code, err.message); - logger.debug(req, "%s", err.stack); - res.status(code); - res.send(body); - } - else { - logger.error(req, "Redirect to error %d: %s", code, err.message); - logger.debug(req, "%s", err.stack); - res.redirect("/error/" + code); - } - }; -} - -export function redirectTo(redirectUrl: string, req: express.Request, - res: express.Response, logger: IRequestLogger) { - return function(err: Error) { - logger.error(req, "Error: %s", err.message); - logger.debug(req, "Redirecting to %s", redirectUrl); - res.redirect(redirectUrl); - }; -} - -export function replyWithError400(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 400, logger); -} - -export function replyWithError401(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 401, logger); -} - -export function replyWithError403(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 403, logger); -} - -export function replyWithError200(req: express.Request, - res: express.Response, logger: IRequestLogger, message: string) { - return replyWithError(req, res, 200, logger, { error: message }); -} \ No newline at end of file diff --git a/themes/main/server/src/lib/Exceptions.ts b/themes/main/server/src/lib/Exceptions.ts deleted file mode 100644 index 83fa4eb6b..000000000 --- a/themes/main/server/src/lib/Exceptions.ts +++ /dev/null @@ -1,88 +0,0 @@ - -export class LdapSearchError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapSearchError"; - (Object).setPrototypeOf(this, LdapSearchError.prototype); - } -} - -export class LdapBindError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapBindError"; - (Object).setPrototypeOf(this, LdapBindError.prototype); - } -} - -export class LdapError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapError"; - (Object).setPrototypeOf(this, LdapError.prototype); - } -} - -export class IdentityError extends Error { - constructor(message?: string) { - super(message); - this.name = "IdentityError"; - (Object).setPrototypeOf(this, IdentityError.prototype); - } -} - -export class AccessDeniedError extends Error { - constructor(message?: string) { - super(message); - this.name = "AccessDeniedError"; - (Object).setPrototypeOf(this, AccessDeniedError.prototype); - } -} - -export class AuthenticationRegulationError extends Error { - constructor(message?: string) { - super(message); - this.name = "AuthenticationRegulationError"; - (Object).setPrototypeOf(this, AuthenticationRegulationError.prototype); - } -} - -export class InvalidTOTPError extends Error { - constructor(message?: string) { - super(message); - this.name = "InvalidTOTPError"; - (Object).setPrototypeOf(this, InvalidTOTPError.prototype); - } -} - -export class NotAuthenticatedError extends Error { - constructor(message?: string) { - super(message); - this.name = "NotAuthenticatedError"; - (Object).setPrototypeOf(this, NotAuthenticatedError.prototype); - } -} - -export class NotAuthorizedError extends Error { - constructor(message?: string) { - super(message); - this.name = "NotAuthanticatedError"; - (Object).setPrototypeOf(this, NotAuthorizedError.prototype); - } -} - -export class FirstFactorValidationError extends Error { - constructor(message?: string) { - super(message); - this.name = "FirstFactorValidationError"; - (Object).setPrototypeOf(this, FirstFactorValidationError.prototype); - } -} - -export class SecondFactorValidationError extends Error { - constructor(message?: string) { - super(message); - this.name = "SecondFactorValidationError"; - (Object).setPrototypeOf(this, FirstFactorValidationError.prototype); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/FirstFactorValidator.ts b/themes/main/server/src/lib/FirstFactorValidator.ts deleted file mode 100644 index 231060002..000000000 --- a/themes/main/server/src/lib/FirstFactorValidator.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import objectPath = require("object-path"); -import Exceptions = require("./Exceptions"); -import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler"; -import { IRequestLogger } from "./logging/IRequestLogger"; -import { Level } from "./authentication/Level"; - -export function validate(req: express.Request, logger: IRequestLogger): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, logger); - - if (!authSession.userid || authSession.authentication_level < Level.ONE_FACTOR) - return reject(new Exceptions.FirstFactorValidationError( - "First factor has not been validated yet.")); - - resolve(); - }); -} \ No newline at end of file diff --git a/themes/main/server/src/lib/IdentityCheckMiddleware.spec.ts b/themes/main/server/src/lib/IdentityCheckMiddleware.spec.ts deleted file mode 100644 index 842ed6bcb..000000000 --- a/themes/main/server/src/lib/IdentityCheckMiddleware.spec.ts +++ /dev/null @@ -1,176 +0,0 @@ - -import sinon = require("sinon"); -import IdentityValidator = require("./IdentityCheckMiddleware"); -import { AuthenticationSessionHandler } - from "./AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { UserDataStore } from "./storage/UserDataStore"; -import exceptions = require("./Exceptions"); -import { ServerVariables } from "./ServerVariables"; -import Assert = require("assert"); -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("./stubs/express.spec"); -import NotifierMock = require("./notifiers/NotifierStub.spec"); -import { IdentityValidableStub } from "./IdentityValidableStub.spec"; -import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "./ServerVariablesMockBuilder.spec"; -import { PRE_VALIDATION_TEMPLATE } - from "./IdentityCheckPreValidationTemplate"; - - -describe("IdentityCheckMiddleware", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let app: express.Application; - let app_get: sinon.SinonStub; - let app_post: sinon.SinonStub; - let identityValidable: IdentityValidableStub; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - - req.headers = {}; - req.originalUrl = "/non-api/xxx"; - req.session = {}; - - req.query = {}; - req.app = {}; - - identityValidable = new IdentityValidableStub(); - - mocks.notifier.notifyStub.returns(BluebirdPromise.resolve()); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve()); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({ userId: "user" })); - - app = express(); - app_get = sinon.stub(app, "get"); - app_post = sinon.stub(app, "post"); - }); - - afterEach(function () { - app_get.restore(); - app_post.restore(); - }); - - describe("test start GET", function () { - it("should redirect to error 401 if pre validation initialization \ -throws a first factor error", function () { - identityValidable.preValidationInitStub.returns(BluebirdPromise.reject( - new exceptions.FirstFactorValidationError( - "Error during prevalidation"))); - const callback = IdentityValidator.get_start_validation( - identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(() => { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - - // In that case we answer with 200 to avoid user enumeration. - it("should send 200 if email is missing in provided identity", function () { - const identity = { userid: "abc" }; - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(identityValidable.preValidationResponseStub.called); - }); - }); - - // In that case we answer with 200 to avoid user enumeration. - it("should send 200 if userid is missing in provided identity", - function () { - const endpoint = "/protected"; - const identity = { email: "abc@example.com" }; - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(identityValidable.preValidationResponseStub.called); - }); - }); - - it("should issue a token, send an email and return 204", function () { - const endpoint = "/protected"; - const identity = { userid: "user", email: "abc@example.com" }; - req.get = sinon.stub().withArgs("Host").returns("localhost"); - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/finish_endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(mocks.notifier.notifyStub.calledOnce); - Assert(mocks.userDataStore.produceIdentityValidationTokenStub - .calledOnce); - Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub - .getCall(0).args[0], "user"); - Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub - .getCall(0).args[3], 240000); - }); - }); - }); - - - - describe("test finish GET", function () { - it("should send 401 if no identity_token is provided", () => { - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - - it("should call postValidation if identity_token is provided and still \ -valid", function () { - req.query.identity_token = "token"; - - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - return callback(req as any, res as any, undefined); - }); - - it("should return 401 if identity_token is provided but invalid", - function () { - req.query.identity_token = "token"; - - identityValidable.postValidationInitStub - .returns(BluebirdPromise.resolve()); - mocks.userDataStore.consumeIdentityValidationTokenStub.reset(); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.reject(new Error("Invalid token"))); - - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - return callback(req as any, res as any, undefined) - .then(() => { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - }); -}); diff --git a/themes/main/server/src/lib/IdentityCheckMiddleware.ts b/themes/main/server/src/lib/IdentityCheckMiddleware.ts deleted file mode 100644 index e72ea4db1..000000000 --- a/themes/main/server/src/lib/IdentityCheckMiddleware.ts +++ /dev/null @@ -1,138 +0,0 @@ -import objectPath = require("object-path"); -import randomstring = require("randomstring"); -import BluebirdPromise = require("bluebird"); -import util = require("util"); -import Exceptions = require("./Exceptions"); -import fs = require("fs"); -import ejs = require("ejs"); -import { IUserDataStore } from "./storage/IUserDataStore"; -import Express = require("express"); -import ErrorReplies = require("./ErrorReplies"); -import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { ServerVariables } from "./ServerVariables"; -import { IdentityValidable } from "./IdentityValidable"; - -import Identity = require("../../types/Identity"); -import { IdentityValidationDocument } - from "./storage/IdentityValidationDocument"; - -const filePath = __dirname + "/../resources/email-template.ejs"; -const email_template = fs.readFileSync(filePath, "utf8"); - -function createAndSaveToken(userid: string, challenge: string, - userDataStore: IUserDataStore): BluebirdPromise { - - const five_minutes = 4 * 60 * 1000; - const token = randomstring.generate({ length: 64 }); - const that = this; - - return userDataStore.produceIdentityValidationToken(userid, token, challenge, - five_minutes) - .then(function () { - return BluebirdPromise.resolve(token); - }); -} - -function consumeToken(token: string, challenge: string, - userDataStore: IUserDataStore) - : BluebirdPromise { - return userDataStore.consumeIdentityValidationToken(token, challenge); -} - -export function register(app: Express.Application, - pre_validation_endpoint: string, - post_validation_endpoint: string, - handler: IdentityValidable, - vars: ServerVariables) { - - app.get(pre_validation_endpoint, - get_start_validation(handler, post_validation_endpoint, vars)); - app.get(post_validation_endpoint, - get_finish_validation(handler, vars)); -} - -function checkIdentityToken(req: Express.Request, identityToken: string) - : BluebirdPromise { - if (!identityToken) - return BluebirdPromise.reject( - new Exceptions.AccessDeniedError("No identity token provided")); - return BluebirdPromise.resolve(); -} - -export function get_finish_validation(handler: IdentityValidable, - vars: ServerVariables) - : Express.RequestHandler { - - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - - let authSession: AuthenticationSession; - const identityToken = objectPath.get( - req, "query.identity_token"); - vars.logger.debug(req, "Identity token provided is %s", identityToken); - - return checkIdentityToken(req, identityToken) - .then(() => { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - return handler.postValidationInit(req); - }) - .then(() => { - return consumeToken(identityToken, handler.challenge(), - vars.userDataStore); - }) - .then((doc: IdentityValidationDocument) => { - authSession.identity_check = { - challenge: handler.challenge(), - userid: doc.userId - }; - handler.postValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - }; -} - -export function get_start_validation(handler: IdentityValidable, - postValidationEndpoint: string, - vars: ServerVariables) - : Express.RequestHandler { - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - let identity: Identity.Identity; - - return handler.preValidationInit(req) - .then((id: Identity.Identity) => { - identity = id; - const email = identity.email; - const userid = identity.userid; - vars.logger.info(req, "Start identity validation of user \"%s\"", - userid); - - if (!(email && userid)) - return BluebirdPromise.reject(new Exceptions.IdentityError( - "Missing user id or email address")); - - return createAndSaveToken(userid, handler.challenge(), - vars.userDataStore); - }) - .then((token) => { - const host = req.get("Host"); - const link_url = util.format("https://%s%s?identity_token=%s", host, - postValidationEndpoint, token); - vars.logger.info(req, "Notification sent to user \"%s\"", - identity.userid); - return vars.notifier.notify(identity.email, handler.mailSubject(), - link_url); - }) - .then(() => { - handler.preValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(Exceptions.IdentityError, (err: Error) => { - handler.preValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - }; -} diff --git a/themes/main/server/src/lib/IdentityCheckPreValidationTemplate.ts b/themes/main/server/src/lib/IdentityCheckPreValidationTemplate.ts deleted file mode 100644 index 0161ce406..000000000 --- a/themes/main/server/src/lib/IdentityCheckPreValidationTemplate.ts +++ /dev/null @@ -1,3 +0,0 @@ - - -export const PRE_VALIDATION_TEMPLATE = "need-identity-validation"; \ No newline at end of file diff --git a/themes/main/server/src/lib/IdentityValidable.ts b/themes/main/server/src/lib/IdentityValidable.ts deleted file mode 100644 index 075580c9e..000000000 --- a/themes/main/server/src/lib/IdentityValidable.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Bluebird = require("bluebird"); -import Identity = require("../../types/Identity"); - -// IdentityValidator allows user to go through a identity validation process -// in two steps: -// - Request an operation to be performed (password reset, registration). -// - Confirm operation with email. - -export interface IdentityValidable { - challenge(): string; - preValidationInit(req: Express.Request): Bluebird; - postValidationInit(req: Express.Request): Bluebird; - - // Serves a page after identity check request - preValidationResponse(req: Express.Request, res: Express.Response): void; - // Serves the page if identity validated - postValidationResponse(req: Express.Request, res: Express.Response): void; - mailSubject(): string; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/IdentityValidableStub.spec.ts b/themes/main/server/src/lib/IdentityValidableStub.spec.ts deleted file mode 100644 index 20a977140..000000000 --- a/themes/main/server/src/lib/IdentityValidableStub.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ - -import Sinon = require("sinon"); -import { IdentityValidable } from "./IdentityValidable"; -import express = require("express"); -import Bluebird = require("bluebird"); -import { Identity } from "../../types/Identity"; - - -export class IdentityValidableStub implements IdentityValidable { - challengeStub: Sinon.SinonStub; - preValidationInitStub: Sinon.SinonStub; - postValidationInitStub: Sinon.SinonStub; - preValidationResponseStub: Sinon.SinonStub; - postValidationResponseStub: Sinon.SinonStub; - mailSubjectStub: Sinon.SinonStub; - - constructor() { - this.challengeStub = Sinon.stub(); - - this.preValidationInitStub = Sinon.stub(); - this.postValidationInitStub = Sinon.stub(); - - this.preValidationResponseStub = Sinon.stub(); - this.postValidationResponseStub = Sinon.stub(); - - this.mailSubjectStub = Sinon.stub(); - } - - challenge(): string { - return this.challengeStub(); - } - - preValidationInit(req: Express.Request): Bluebird { - return this.preValidationInitStub(req); - } - - postValidationInit(req: Express.Request): Bluebird { - return this.postValidationInitStub(req); - } - - preValidationResponse(req: Express.Request, res: Express.Response): void { - return this.preValidationResponseStub(req, res); - } - - postValidationResponse(req: Express.Request, res: Express.Response): void { - return this.postValidationResponseStub(req, res); - } - - mailSubject(): string { - return this.mailSubjectStub(); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/Server.spec.ts b/themes/main/server/src/lib/Server.spec.ts deleted file mode 100644 index 365163254..000000000 --- a/themes/main/server/src/lib/Server.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ - -import Assert = require("assert"); -import Sinon = require("sinon"); -import nedb = require("nedb"); -import express = require("express"); -import winston = require("winston"); -import speakeasy = require("speakeasy"); -import u2f = require("u2f"); -import session = require("express-session"); -import { Configuration } from "./configuration/schema/Configuration"; -import { GlobalDependencies } from "../../types/Dependencies"; -import Server from "./Server"; -import { LdapjsMock, LdapjsClientMock } from "./stubs/ldapjs.spec"; - - -describe("Server", function () { - let deps: GlobalDependencies; - let sessionMock: Sinon.SinonSpy; - let ldapjsMock: LdapjsMock; - - before(function () { - sessionMock = Sinon.spy(session); - ldapjsMock = new LdapjsMock(); - - deps = { - speakeasy: speakeasy, - u2f: u2f, - nedb: nedb, - winston: winston, - ldapjs: ldapjsMock as any, - session: sessionMock as any, - ConnectRedis: Sinon.spy(), - Redis: Sinon.spy() as any - }; - }); - - - it("should set cookie scope to domain set in the config", function () { - const config: Configuration = { - port: 8081, - session: { - domain: "example.com", - secret: "secret" - }, - authentication_backend: { - ldap: { - url: "http://ldap", - user: "user", - password: "password", - base_dn: "dc=example,dc=com" - }, - }, - notifier: { - email: { - username: "user@example.com", - password: "password", - sender: "test@authelia.com", - service: "gmail" - } - }, - regulation: { - max_retries: 3, - ban_time: 5 * 60, - find_time: 5 * 60 - }, - storage: { - local: { - in_memory: true - } - } - }; - - const server = new Server(deps); - server.start(config, deps) - .then(function () { - Assert(sessionMock.calledOnce); - Assert.equal(sessionMock.getCall(0).args[0].cookie.domain, "example.com"); - server.stop(); - }); - }); -}); diff --git a/themes/main/server/src/lib/Server.ts b/themes/main/server/src/lib/Server.ts deleted file mode 100644 index 4090f6294..000000000 --- a/themes/main/server/src/lib/Server.ts +++ /dev/null @@ -1,93 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import ObjectPath = require("object-path"); - -import { Configuration } from "./configuration/schema/Configuration"; -import { GlobalDependencies } from "../../types/Dependencies"; -import { UserDataStore } from "./storage/UserDataStore"; -import { ConfigurationParser } from "./configuration/ConfigurationParser"; -import { SessionConfigurationBuilder } from "./configuration/SessionConfigurationBuilder"; -import { GlobalLogger } from "./logging/GlobalLogger"; -import { RequestLogger } from "./logging/RequestLogger"; -import { ServerVariables } from "./ServerVariables"; -import { ServerVariablesInitializer } from "./ServerVariablesInitializer"; -import { Configurator } from "./web_server/Configurator"; - -import * as Express from "express"; -import * as Path from "path"; -import * as http from "http"; - -function clone(obj: any) { - return JSON.parse(JSON.stringify(obj)); -} - -export default class Server { - private httpServer: http.Server; - private globalLogger: GlobalLogger; - private requestLogger: RequestLogger; - - constructor(deps: GlobalDependencies) { - this.globalLogger = new GlobalLogger(deps.winston); - this.requestLogger = new RequestLogger(deps.winston); - } - - private displayConfigurations(configuration: Configuration) { - const displayableConfiguration: Configuration = clone(configuration); - const STARS = "*****"; - - if (displayableConfiguration.authentication_backend.ldap) { - displayableConfiguration.authentication_backend.ldap.password = STARS; - } - - displayableConfiguration.session.secret = STARS; - if (displayableConfiguration.notifier && displayableConfiguration.notifier.email) - displayableConfiguration.notifier.email.password = STARS; - if (displayableConfiguration.notifier && displayableConfiguration.notifier.smtp) - displayableConfiguration.notifier.smtp.password = STARS; - - this.globalLogger.debug("User configuration is %s", - JSON.stringify(displayableConfiguration, undefined, 2)); - } - - private setup(config: Configuration, app: Express.Application, deps: GlobalDependencies): BluebirdPromise { - const that = this; - return ServerVariablesInitializer.initialize( - config, this.globalLogger, this.requestLogger, deps) - .then(function (vars: ServerVariables) { - Configurator.configure(config, app, vars, deps); - return BluebirdPromise.resolve(); - }); - } - - private startServer(app: Express.Application, port: number) { - const that = this; - that.globalLogger.info("Starting Authelia..."); - return new BluebirdPromise((resolve, reject) => { - this.httpServer = app.listen(port, function (err: string) { - that.globalLogger.info("Listening on port %d...", port); - resolve(); - }); - }); - } - - start(configuration: Configuration, deps: GlobalDependencies) - : BluebirdPromise { - const that = this; - const app = Express(); - - const appConfiguration = ConfigurationParser.parse(configuration); - - // by default the level of logs is info - deps.winston.level = appConfiguration.logs_level; - this.displayConfigurations(appConfiguration); - - return this.setup(appConfiguration, app, deps) - .then(function () { - return that.startServer(app, appConfiguration.port); - }); - } - - stop() { - this.httpServer.close(); - } -} - diff --git a/themes/main/server/src/lib/ServerVariables.ts b/themes/main/server/src/lib/ServerVariables.ts deleted file mode 100644 index cd3dd6dc8..000000000 --- a/themes/main/server/src/lib/ServerVariables.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { IRequestLogger } from "./logging/IRequestLogger"; -import { ITotpHandler } from "./authentication/totp/ITotpHandler"; -import { IU2fHandler } from "./authentication/u2f/IU2fHandler"; -import { IUserDataStore } from "./storage/IUserDataStore"; -import { INotifier } from "./notifiers/INotifier"; -import { IRegulator } from "./regulation/IRegulator"; -import { Configuration } from "./configuration/schema/Configuration"; -import { IAuthorizer } from "./authorization/IAuthorizer"; -import { IUsersDatabase } from "./authentication/backends/IUsersDatabase"; - -export interface ServerVariables { - logger: IRequestLogger; - usersDatabase: IUsersDatabase; - totpHandler: ITotpHandler; - u2f: IU2fHandler; - userDataStore: IUserDataStore; - notifier: INotifier; - regulator: IRegulator; - config: Configuration; - authorizer: IAuthorizer; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/ServerVariablesInitializer.ts b/themes/main/server/src/lib/ServerVariablesInitializer.ts deleted file mode 100644 index df79238cc..000000000 --- a/themes/main/server/src/lib/ServerVariablesInitializer.ts +++ /dev/null @@ -1,116 +0,0 @@ - -import winston = require("winston"); -import BluebirdPromise = require("bluebird"); -import U2F = require("u2f"); -import Nodemailer = require("nodemailer"); - -import { IRequestLogger } from "./logging/IRequestLogger"; -import { RequestLogger } from "./logging/RequestLogger"; - -import { TotpHandler } from "./authentication/totp/TotpHandler"; -import { ITotpHandler } from "./authentication/totp/ITotpHandler"; -import { NotifierFactory } from "./notifiers/NotifierFactory"; -import { MailSenderBuilder } from "./notifiers/MailSenderBuilder"; -import { LdapUsersDatabase } from "./authentication/backends/ldap/LdapUsersDatabase"; -import { ConnectorFactory } from "./authentication/backends/ldap/connector/ConnectorFactory"; - -import { IUserDataStore } from "./storage/IUserDataStore"; -import { UserDataStore } from "./storage/UserDataStore"; -import { INotifier } from "./notifiers/INotifier"; -import { Regulator } from "./regulation/Regulator"; -import { IRegulator } from "./regulation/IRegulator"; -import Configuration = require("./configuration/schema/Configuration"); -import { CollectionFactoryFactory } from "./storage/CollectionFactoryFactory"; -import { ICollectionFactory } from "./storage/ICollectionFactory"; -import { MongoCollectionFactory } from "./storage/mongo/MongoCollectionFactory"; -import { IMongoClient } from "./connectors/mongo/IMongoClient"; - -import { GlobalDependencies } from "../../types/Dependencies"; -import { ServerVariables } from "./ServerVariables"; -import { MongoClient } from "./connectors/mongo/MongoClient"; -import { IGlobalLogger } from "./logging/IGlobalLogger"; -import { SessionFactory } from "./authentication/backends/ldap/SessionFactory"; -import { IUsersDatabase } from "./authentication/backends/IUsersDatabase"; -import { FileUsersDatabase } from "./authentication/backends/file/FileUsersDatabase"; -import { Authorizer } from "./authorization/Authorizer"; - -class UserDataStoreFactory { - static create(config: Configuration.Configuration, globalLogger: IGlobalLogger): BluebirdPromise { - if (config.storage.local) { - const nedbOptions: Nedb.DataStoreOptions = { - filename: config.storage.local.path, - inMemoryOnly: config.storage.local.in_memory - }; - const collectionFactory = CollectionFactoryFactory.createNedb(nedbOptions); - return BluebirdPromise.resolve(new UserDataStore(collectionFactory)); - } - else if (config.storage.mongo) { - const mongoClient = new MongoClient( - config.storage.mongo, - globalLogger); - const collectionFactory = CollectionFactoryFactory.createMongo(mongoClient); - return BluebirdPromise.resolve(new UserDataStore(collectionFactory)); - } - - return BluebirdPromise.reject(new Error("Storage backend incorrectly configured.")); - } -} - -export class ServerVariablesInitializer { - static createUsersDatabase( - config: Configuration.Configuration, - deps: GlobalDependencies) - : IUsersDatabase { - - if (config.authentication_backend.ldap) { - const ldapConfig = config.authentication_backend.ldap; - return new LdapUsersDatabase( - new SessionFactory( - ldapConfig, - new ConnectorFactory(ldapConfig, deps.ldapjs), - deps.winston - ), - ldapConfig - ); - } - else if (config.authentication_backend.file) { - return new FileUsersDatabase(config.authentication_backend.file); - } - } - - static initialize( - config: Configuration.Configuration, - globalLogger: IGlobalLogger, - requestLogger: IRequestLogger, - deps: GlobalDependencies) - : BluebirdPromise { - - const mailSenderBuilder = - new MailSenderBuilder(Nodemailer); - const notifier = NotifierFactory.build( - config.notifier, mailSenderBuilder); - const authorizer = new Authorizer(config.access_control, deps.winston); - const totpHandler = new TotpHandler(deps.speakeasy); - const usersDatabase = this.createUsersDatabase( - config, deps); - - return UserDataStoreFactory.create(config, globalLogger) - .then(function (userDataStore: UserDataStore) { - const regulator = new Regulator(userDataStore, config.regulation.max_retries, - config.regulation.find_time, config.regulation.ban_time); - - const variables: ServerVariables = { - authorizer: authorizer, - config: config, - usersDatabase: usersDatabase, - logger: requestLogger, - notifier: notifier, - regulator: regulator, - totpHandler: totpHandler, - u2f: deps.u2f, - userDataStore: userDataStore - }; - return BluebirdPromise.resolve(variables); - }); - } -} diff --git a/themes/main/server/src/lib/ServerVariablesMockBuilder.spec.ts b/themes/main/server/src/lib/ServerVariablesMockBuilder.spec.ts deleted file mode 100644 index 7874702a0..000000000 --- a/themes/main/server/src/lib/ServerVariablesMockBuilder.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { ServerVariables } from "./ServerVariables"; - -import { Configuration } from "./configuration/schema/Configuration"; -import { IUsersDatabaseStub } from "./authentication/backends/IUsersDatabaseStub.spec"; -import { AuthorizerStub } from "./authorization/AuthorizerStub.spec"; -import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec"; -import { NotifierStub } from "./notifiers/NotifierStub.spec"; -import { RegulatorStub } from "./regulation/RegulatorStub.spec"; -import { TotpHandlerStub } from "./authentication/totp/TotpHandlerStub.spec"; -import { UserDataStoreStub } from "./storage/UserDataStoreStub.spec"; -import { U2fHandlerStub } from "./authentication/u2f/U2fHandlerStub.spec"; - -export interface ServerVariablesMock { - authorizer: AuthorizerStub; - config: Configuration; - usersDatabase: IUsersDatabaseStub; - logger: RequestLoggerStub; - notifier: NotifierStub; - regulator: RegulatorStub; - totpHandler: TotpHandlerStub; - userDataStore: UserDataStoreStub; - u2f: U2fHandlerStub; -} - -export class ServerVariablesMockBuilder { - static build(enableLogging?: boolean): { variables: ServerVariables, mocks: ServerVariablesMock} { - const mocks: ServerVariablesMock = { - authorizer: new AuthorizerStub(), - config: { - access_control: {}, - totp: { - issuer: "authelia.com" - }, - authentication_backend: { - ldap: { - url: "ldap://ldap", - base_dn: "dc=example,dc=com", - user: "user", - password: "password", - mail_attribute: "mail", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn" - }, - }, - logs_level: "debug", - notifier: {}, - port: 8080, - regulation: { - ban_time: 50, - find_time: 50, - max_retries: 3 - }, - session: { - secret: "my_secret", - domain: "mydomain" - }, - storage: {} - }, - usersDatabase: new IUsersDatabaseStub(), - logger: new RequestLoggerStub(enableLogging), - notifier: new NotifierStub(), - regulator: new RegulatorStub(), - totpHandler: new TotpHandlerStub(), - userDataStore: new UserDataStoreStub(), - u2f: new U2fHandlerStub() - }; - const vars: ServerVariables = { - authorizer: mocks.authorizer, - config: mocks.config, - usersDatabase: mocks.usersDatabase, - logger: mocks.logger, - notifier: mocks.notifier, - regulator: mocks.regulator, - totpHandler: mocks.totpHandler, - userDataStore: mocks.userDataStore, - u2f: mocks.u2f - }; - - return { - variables: vars, - mocks: mocks - }; - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/Level.ts b/themes/main/server/src/lib/authentication/Level.ts deleted file mode 100644 index 57b6a2346..000000000 --- a/themes/main/server/src/lib/authentication/Level.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum Level { - NOT_AUTHENTICATED = 0, - ONE_FACTOR = 1, - TWO_FACTOR = 2 -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/GroupsAndEmails.ts b/themes/main/server/src/lib/authentication/backends/GroupsAndEmails.ts deleted file mode 100644 index 3434ba66f..000000000 --- a/themes/main/server/src/lib/authentication/backends/GroupsAndEmails.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface GroupsAndEmails { - groups: string[]; - emails: string[]; -} diff --git a/themes/main/server/src/lib/authentication/backends/IUsersDatabase.ts b/themes/main/server/src/lib/authentication/backends/IUsersDatabase.ts deleted file mode 100644 index d7fa13b7a..000000000 --- a/themes/main/server/src/lib/authentication/backends/IUsersDatabase.ts +++ /dev/null @@ -1,10 +0,0 @@ -import Bluebird = require("bluebird"); - -import { GroupsAndEmails } from "./GroupsAndEmails"; - -export interface IUsersDatabase { - checkUserPassword(username: string, password: string): Bluebird; - getEmails(username: string): Bluebird; - getGroups(username: string): Bluebird; - updatePassword(username: string, newPassword: string): Bluebird; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts b/themes/main/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts deleted file mode 100644 index 19341a5dd..000000000 --- a/themes/main/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); - -import { IUsersDatabase } from "./IUsersDatabase"; -import { GroupsAndEmails } from "./GroupsAndEmails"; - -export class IUsersDatabaseStub implements IUsersDatabase { - checkUserPasswordStub: Sinon.SinonStub; - getEmailsStub: Sinon.SinonStub; - getGroupsStub: Sinon.SinonStub; - updatePasswordStub: Sinon.SinonStub; - - constructor() { - this.checkUserPasswordStub = Sinon.stub(); - this.getEmailsStub = Sinon.stub(); - this.getGroupsStub = Sinon.stub(); - this.updatePasswordStub = Sinon.stub(); - } - - checkUserPassword(username: string, password: string): Bluebird { - return this.checkUserPasswordStub(username, password); - } - - getEmails(username: string): Bluebird { - return this.getEmailsStub(username); - } - - getGroups(username: string): Bluebird { - return this.getGroupsStub(username); - } - - updatePassword(username: string, newPassword: string): Bluebird { - return this.updatePasswordStub(username, newPassword); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts b/themes/main/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts deleted file mode 100644 index a258a78f7..000000000 --- a/themes/main/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts +++ /dev/null @@ -1,224 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); -import Fs = require("fs"); -import Sinon = require("sinon"); -import Tmp = require("tmp"); - -import { FileUsersDatabase } from "./FileUsersDatabase"; -import { FileUsersDatabaseConfiguration } from "../../../configuration/schema/FileUsersDatabaseConfiguration"; -import { HashGenerator } from "../../../utils/HashGenerator"; - -const GOOD_DATABASE = ` -users: - john: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev - - harry: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - emails: harry.potter@authelia.com - groups: [] -`; - -const BAD_HASH = ` -users: - john: - password: "{CRYPT}$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev -`; - -const NO_PASSWORD_DATABASE = ` -users: - john: - email: john.doe@authelia.com - groups: - - admins - - dev -`; - -const NO_EMAIL_DATABASE = ` -users: - john: - password: "{CRYPT}$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - groups: - - admins - - dev -`; - -const SINGLE_USER_DATABASE = ` -users: - john: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev -` - -function createTmpFileFrom(yaml: string) { - const tmpFileAsync = Bluebird.promisify(Tmp.file); - return tmpFileAsync() - .then((path: string) => { - Fs.writeFileSync(path, yaml, "utf-8"); - return Bluebird.resolve(path); - }); -} - -describe("authentication/backends/file/FileUsersDatabase", function() { - let configuration: FileUsersDatabaseConfiguration; - - describe("checkUserPassword", () => { - describe("good config", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then((groupsAndEmails) => { - Assert.deepEqual(groupsAndEmails.groups, ["admins", "dev"]); - Assert.deepEqual(groupsAndEmails.emails, ["john.doe@authelia.com"]); - }); - }); - - it("should fail when password is wrong", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "bad_password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("no_user", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("bad hash", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail when hash is wrong", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("no password", () => { - beforeEach(() => { - return createTmpFileFrom(NO_PASSWORD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - }); - - describe("getEmails", () => { - describe("good config", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("john") - .then((emails) => { - Assert.deepEqual(emails, ["john.doe@authelia.com"]); - }); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("no_user") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("no email provided", () => { - beforeEach(() => { - return createTmpFileFrom(NO_EMAIL_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("john") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - }); - - describe("updatePassword", () => { - beforeEach(() => { - return createTmpFileFrom(SINGLE_USER_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - const NEW_HASH = "{CRYPT}$6$rounds=500000$Qw6MhgADvLyYMEq9$ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const stub = Sinon.stub(HashGenerator, "ssha512").returns(Bluebird.resolve(NEW_HASH)); - return usersDatabase.updatePassword("john", "mypassword") - .then(() => { - const content = Fs.readFileSync(configuration.path, "utf-8"); - const matches = content.match(/password: '(.+)'/); - Assert.equal(matches[1], NEW_HASH); - }) - .finally(() => stub.restore()); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.updatePassword("bad_user", "mypassword") - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => Bluebird.resolve()); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/file/FileUsersDatabase.ts b/themes/main/server/src/lib/authentication/backends/file/FileUsersDatabase.ts deleted file mode 100644 index d34dde21e..000000000 --- a/themes/main/server/src/lib/authentication/backends/file/FileUsersDatabase.ts +++ /dev/null @@ -1,182 +0,0 @@ -import Bluebird = require("bluebird"); -import Fs = require("fs"); -import Yaml = require("yamljs"); - -import { FileUsersDatabaseConfiguration } - from "../../../configuration/schema/FileUsersDatabaseConfiguration"; -import { GroupsAndEmails } from "../GroupsAndEmails"; -import { IUsersDatabase } from "../IUsersDatabase"; -import { HashGenerator } from "../../../utils/HashGenerator"; -import { ReadWriteQueue } from "./ReadWriteQueue"; - -const loadAsync = Bluebird.promisify(Yaml.load); - -export class FileUsersDatabase implements IUsersDatabase { - private configuration: FileUsersDatabaseConfiguration; - private queue: ReadWriteQueue; - - constructor(configuration: FileUsersDatabaseConfiguration) { - this.configuration = configuration; - this.queue = new ReadWriteQueue(this.configuration.path); - } - - /** - * Read database from file. - * It enqueues the read task so that it is scheduled - * between other reads and writes. - */ - private readDatabase(): Bluebird { - return new Bluebird((resolve, reject) => { - this.queue.read((err: Error, data: string) => { - if (err) { - reject(err); - return; - } - resolve(data); - this.queue.next(); - }); - }) - .then((content) => { - const database = Yaml.parse(content); - if (!database) { - return Bluebird.reject(new Error("Unable to parse YAML file.")); - } - return Bluebird.resolve(database); - }); - } - - /** - * Checks the user exists in the database. - */ - private checkUserExists( - database: any, - username: string) - : Bluebird { - if (!(username in database.users)) { - return Bluebird.reject( - new Error(`User ${username} does not exist in database.`)); - } - return Bluebird.resolve(); - } - - /** - * Check the password of a given user. - */ - private checkPassword( - database: any, - username: string, - password: string) - : Bluebird { - const storedHash: string = database.users[username].password; - const matches = storedHash.match(/rounds=([0-9]+)\$([a-zA-z0-9]+)\$/); - if (!(matches && matches.length == 3)) { - return Bluebird.reject(new Error("Unable to detect the hash salt and rounds. " + - "Make sure the password is hashed with SSHA512.")); - } - - const rounds: number = parseInt(matches[1]); - const salt = matches[2]; - - return HashGenerator.ssha512(password, rounds, salt) - .then((hash: string) => { - if (hash !== storedHash) { - return Bluebird.reject(new Error("Wrong username/password.")); - } - return Bluebird.resolve(); - }); - } - - /** - * Retrieve email addresses of a given user. - */ - private retrieveEmails( - database: any, - username: string) - : Bluebird { - if (!("email" in database.users[username])) { - return Bluebird.reject( - new Error(`User ${username} has no email address.`)); - } - return Bluebird.resolve( - [database.users[username].email]); - } - - private retrieveGroups( - database: any, - username: string) - : Bluebird { - if (!("groups" in database.users[username])) { - return Bluebird.resolve([]); - } - return Bluebird.resolve( - database.users[username].groups); - } - - private replacePassword( - database: any, - username: string, - newPassword: string) - : Bluebird { - const that = this; - return HashGenerator.ssha512(newPassword) - .then((hash) => { - database.users[username].password = hash; - const str = Yaml.stringify(database, 4, 2); - return Bluebird.resolve(str); - }) - .then((content: string) => { - return new Bluebird((resolve, reject) => { - that.queue.write(content, (err) => { - if (err) { - return reject(err); - } - resolve(); - that.queue.next(); - }); - }); - }); - } - - checkUserPassword( - username: string, - password: string) - : Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.checkPassword(database, username, password)) - .then(() => { - return Bluebird.join( - this.retrieveEmails(database, username), - this.retrieveGroups(database, username) - ).spread((emails: string[], groups: string[]) => { - return { emails: emails, groups: groups }; - }); - }); - }); - } - - getEmails(username: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.retrieveEmails(database, username)); - }); - } - - getGroups(username: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.retrieveGroups(database, username)); - }); - } - - updatePassword(username: string, newPassword: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.replacePassword(database, username, newPassword)); - }); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/file/ReadWriteQueue.ts b/themes/main/server/src/lib/authentication/backends/file/ReadWriteQueue.ts deleted file mode 100644 index 957ddaec5..000000000 --- a/themes/main/server/src/lib/authentication/backends/file/ReadWriteQueue.ts +++ /dev/null @@ -1,60 +0,0 @@ -import Fs = require("fs"); - -type Callback = (err: Error, data?: string) => void; -type ContentAndCallback = [string, Callback] | [string, string, Callback]; - -/** - * WriteQueue is a queue synchronizing writes to a file. - * - * Example of use: - * - * queue.add(mycontent, (err) => { - * // do whatever you want here. - * queue.next(); - * }) - */ -export class ReadWriteQueue { - private filePath: string; - private queue: ContentAndCallback[]; - - constructor (filePath: string) { - this.queue = []; - this.filePath = filePath; - } - - next () { - if (this.queue.length === 0) - return; - - const task = this.queue[0]; - - if (task[0] == "write") { - Fs.writeFile(this.filePath, task[1], "utf-8", (err) => { - this.queue.shift(); - const cb = task[2] as Callback; - cb(err); - }); - } - else if (task[0] == "read") { - Fs.readFile(this.filePath, { encoding: "utf-8"} , (err, data) => { - this.queue.shift(); - const cb = task[1] as Callback; - cb(err, data); - }); - } - } - - write (content: string, cb: Callback) { - this.queue.push(["write", content, cb]); - if (this.queue.length === 1) { - this.next(); - } - } - - read (cb: Callback) { - this.queue.push(["read", cb]); - if (this.queue.length === 1) { - this.next(); - } - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/ISession.ts b/themes/main/server/src/lib/authentication/backends/ldap/ISession.ts deleted file mode 100644 index da2c74433..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/ISession.ts +++ /dev/null @@ -1,12 +0,0 @@ - -import BluebirdPromise = require("bluebird"); - -export interface ISession { - open(): BluebirdPromise; - close(): BluebirdPromise; - - searchUserDn(username: string): BluebirdPromise; - searchEmails(username: string): BluebirdPromise; - searchGroups(username: string): BluebirdPromise; - modifyPassword(username: string, newPassword: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/ISessionFactory.ts b/themes/main/server/src/lib/authentication/backends/ldap/ISessionFactory.ts deleted file mode 100644 index 014d1eea5..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/ISessionFactory.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import { ISession } from "./ISession"; - -export interface ISessionFactory { - create(userDN: string, password: string): ISession; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts b/themes/main/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts deleted file mode 100644 index f4a6e6306..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts +++ /dev/null @@ -1,386 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); - -import { LdapUsersDatabase } from "./LdapUsersDatabase"; - -import { SessionFactoryStub } from "./SessionFactoryStub.spec"; -import { SessionStub } from "./SessionStub.spec"; - -const ADMIN_USER_DN = "cn=admin,dc=example,dc=com"; -const ADMIN_PASSWORD = "password"; - -describe("ldap/connector/LdapUsersDatabase", function() { - let sessionFactory: SessionFactoryStub; - let usersDatabase: LdapUsersDatabase; - - const USERNAME = "user"; - const PASSWORD = "pass"; - const NEW_PASSWORD = "pass2"; - - const LDAP_CONFIG = { - url: "http://localhost:324", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={0}", - mail_attribute: "mail", - group_name_attribute: "cn", - user: ADMIN_USER_DN, - password: ADMIN_PASSWORD - }; - - beforeEach(function() { - sessionFactory = new SessionFactoryStub(); - usersDatabase = new LdapUsersDatabase(sessionFactory, LDAP_CONFIG); - }) - - describe("checkUserPassword", function() { - it("should return groups and emails when user/password matches", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME).returns(Bluebird.resolve(emails)); - adminSession.searchGroupsStub.withArgs(USERNAME).returns(Bluebird.resolve(groups)); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => { - Assert.deepEqual(groupsAndEmails.groups, groups); - Assert.deepEqual(groupsAndEmails.emails, emails); - }) - }); - - it("should fail when username/password is wrong", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - - userSession.openStub.returns(Bluebird.reject(new Error("Failed binding"))); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when admin binding fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.reject(new Error("Failed binding"))); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.notCalled); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when search for user dn fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.reject(new Error("Failed searching user dn"))); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.notCalled); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when groups retrieval fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME) - .returns(Bluebird.resolve(emails)); - adminSession.searchGroupsStub.withArgs(USERNAME) - .returns(Bluebird.reject(new Error("Failed retrieving groups"))); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - }) - }); - - it("should fail when emails retrieval fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME) - .returns(Bluebird.reject(new Error("Emails retrieval failed"))); - adminSession.searchGroupsStub.withArgs(USERNAME) - .returns(Bluebird.resolve(groups)); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - }) - }); - }); - - describe("getEmails", function() { - it("should succefully retrieves email", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.resolve(emails)); - - return usersDatabase.getEmails(USERNAME) - .then((foundEmails) => { - Assert(session.closeStub.called); - Assert.deepEqual(foundEmails, emails); - }) - }); - - it("should fail when binding fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbinding fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.resolve(emails)); - session.closeStub.returns(Bluebird.reject(new Error("Unbinding failed"))); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when search fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.reject(new Error("Search failed"))); - session.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - }); - - - describe("getGroups", function() { - it("should succefully retrieves groups", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.resolve(groups)); - - return usersDatabase.getGroups(USERNAME) - .then((foundGroups) => { - Assert(session.closeStub.called); - Assert.deepEqual(foundGroups, groups); - }) - }); - - it("should fail when binding fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbinding fails", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.resolve(groups)); - session.closeStub.returns(Bluebird.reject(new Error("Unbinding failed"))); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when search fails", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.reject(new Error("Search failed"))); - session.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - }); - - - describe("updatePassword", function() { - it("should successfully update password", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => { - Assert(session.modifyPasswordStub.calledWith(USERNAME, NEW_PASSWORD)); - Assert(session.closeStub.called); - }) - }); - - it("should fail when binding fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when update fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.reject(new Error("Update failed"))); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbind fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.reject(new Error("Unbind failed"))); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts b/themes/main/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts deleted file mode 100644 index edda62ec6..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts +++ /dev/null @@ -1,107 +0,0 @@ -import Bluebird = require("bluebird"); -import { IUsersDatabase } from "../IUsersDatabase"; -import { ISessionFactory } from "./ISessionFactory"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { ISession } from "./ISession"; -import { GroupsAndEmails } from "../GroupsAndEmails"; -import Exceptions = require("../../../Exceptions"); - -type SessionCallback = (session: ISession) => Bluebird; - -export class LdapUsersDatabase implements IUsersDatabase { - private sessionFactory: ISessionFactory; - private configuration: LdapConfiguration; - - constructor( - sessionFactory: ISessionFactory, - configuration: LdapConfiguration) { - this.sessionFactory = sessionFactory; - this.configuration = configuration; - } - - private withSession( - username: string, - password: string, - cb: SessionCallback): Bluebird { - const session = this.sessionFactory.create(username, password); - return session.open() - .then(() => cb(session)) - .finally(() => session.close()); - } - - checkUserPassword(username: string, password: string): Bluebird { - const that = this; - function verifyUserPassword(userDN: string) { - return that.withSession( - userDN, - password, - (session) => Bluebird.resolve() - ); - } - - function getInfo(session: ISession) { - return Bluebird.join( - session.searchGroups(username), - session.searchEmails(username) - ) - .spread((groups: string[], emails: string[]) => { - return { groups: groups, emails: emails }; - }); - } - - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchUserDn(username) - .then(verifyUserPassword) - .then(() => getInfo(session)); - }) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError(err.message))); - } - - getEmails(username: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchEmails(username); - } - ) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError("Failed during email retrieval: " + err.message)) - ); - } - - getGroups(username: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchGroups(username); - } - ) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError("Failed during email retrieval: " + err.message)) - ); - } - - updatePassword(username: string, newPassword: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.modifyPassword(username, newPassword); - } - ) - .catch(function (err: Error) { - return Bluebird.reject( - new Exceptions.LdapError( - "Error while updating password: " + err.message)); - }); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts b/themes/main/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts deleted file mode 100644 index 9dedfcb7c..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { SessionStub } from "./SessionStub.spec"; -import { SafeSession } from "./SafeSession"; - -describe("ldap/SanitizedClient", function () { - let client: SafeSession; - - beforeEach(function () { - const clientStub = new SessionStub(); - clientStub.searchUserDnStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.searchGroupsStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.searchEmailsStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.modifyPasswordStub.onCall(0).returns(BluebirdPromise.resolve()); - client = new SafeSession(clientStub); - }); - - describe("special chars are used", function () { - it("should fail when special chars are used in searchUserDn", function () { - // potential ldap injection"; - return client.searchUserDn("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in searchGroups", function () { - // potential ldap injection"; - return client.searchGroups("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in searchEmails", function () { - // potential ldap injection"; - return client.searchEmails("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in modifyPassword", function () { - // potential ldap injection"; - return client.modifyPassword("cn=dummy_user,ou=groupgs", "abc") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - }); - - describe("no special chars are used", function() { - it("should succeed when no special chars are used in searchUserDn", function () { - return client.searchUserDn("dummy_user"); - }); - - it("should succeed when no special chars are used in searchGroups", function () { - return client.searchGroups("dummy_user"); - }); - - it("should succeed when no special chars are used in searchEmails", function () { - return client.searchEmails("dummy_user"); - }); - - it("should succeed when no special chars are used in modifyPassword", function () { - return client.modifyPassword("dummy_user", "abc"); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/SafeSession.ts b/themes/main/server/src/lib/authentication/backends/ldap/SafeSession.ts deleted file mode 100644 index 572209066..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/SafeSession.ts +++ /dev/null @@ -1,62 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ISession } from "./ISession"; -import { Sanitizer } from "./Sanitizer"; - -const SPECIAL_CHAR_USED_MESSAGE = "Special character used in LDAP query."; - - -export class SafeSession implements ISession { - private sesion: ISession; - - constructor(sesion: ISession) { - this.sesion = sesion; - } - - open(): BluebirdPromise { - return this.sesion.open(); - } - - close(): BluebirdPromise { - return this.sesion.close(); - } - - searchGroups(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchGroups(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - searchUserDn(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchUserDn(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - searchEmails(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchEmails(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - modifyPassword(username: string, newPassword: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.modifyPassword(sanitizedUsername, newPassword); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } -} diff --git a/themes/main/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts b/themes/main/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts deleted file mode 100644 index 9dd33fed4..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import Assert = require("assert"); -import { Sanitizer } from "./Sanitizer"; - -describe("ldap/InputsSanitizer", function () { - it("should fail when special characters are used", function () { - Assert.throws(() => { Sanitizer.sanitize("ab,c"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a\\bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a'bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a#bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a+bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a { Sanitizer.sanitize("a>bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a;bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a\"bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a=bc"); }, Error); - }); - - it("should return original string", function () { - Assert.equal(Sanitizer.sanitize("abcdef"), "abcdef"); - }); - - it("should trim", function () { - Assert.throws(() => { Sanitizer.sanitize(" abc "); }, Error); - }); -}); diff --git a/themes/main/server/src/lib/authentication/backends/ldap/Sanitizer.ts b/themes/main/server/src/lib/authentication/backends/ldap/Sanitizer.ts deleted file mode 100644 index be74132ad..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/Sanitizer.ts +++ /dev/null @@ -1,25 +0,0 @@ - -// returns true for 1 or more matches, where 'a' is an array and 'b' is a search string or an array of multiple search strings -function contains(a: string, character: string) { - // string match - return a.indexOf(character) > -1; -} - -function containsOneOf(s: string, characters: string[]) { - return characters - .map((character: string) => { return contains(s, character); }) - .reduce((acc: boolean, current: boolean) => { return acc || current; }, false); -} - -export class Sanitizer { - static sanitize(input: string): string { - const forbiddenChars = [",", "\\", "'", "#", "+", "<", ">", ";", "\"", "="]; - if (containsOneOf(input, forbiddenChars)) - throw new Error("Input containing unsafe characters."); - - if (input != input.trim()) - throw new Error("Input has unexpected spaces."); - - return input; - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/Session.spec.ts b/themes/main/server/src/lib/authentication/backends/ldap/Session.spec.ts deleted file mode 100644 index d55f6a805..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/Session.spec.ts +++ /dev/null @@ -1,127 +0,0 @@ - -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Session } from "./Session"; -import { ConnectorFactoryStub } from "./connector/ConnectorFactoryStub.spec"; -import { ConnectorStub } from "./connector/ConnectorStub.spec"; - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import Winston = require("winston"); - -describe("ldap/Session", function () { - const USERNAME = "username"; - const ADMIN_USER_DN = "cn=admin,dc=example,dc=com"; - const ADMIN_PASSWORD = "password"; - - it("should replace {0} by username when searching for groups in LDAP", function () { - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member=cn={0},ou=users,dc=example,dc=com", - group_name_attribute: "cn", - mail_attribute: "mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const connectorStub = new ConnectorStub(); - connectorStub.searchAsyncStub.returns(BluebirdPromise.resolve([{ - cn: "group1" - }])); - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, connectorStub, Winston); - - return client.searchGroups("user1") - .then(function () { - Assert.equal(connectorStub.searchAsyncStub.getCall(0).args[1].filter, - "member=cn=user1,ou=users,dc=example,dc=com"); - }); - }); - - it("should replace {dn} by user DN when searching for groups in LDAP", function () { - const USER_DN = "cn=user1,ou=users,dc=example,dc=com"; - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn", - mail_attribute: "mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const ldapClient = new ConnectorStub(); - - // Retrieve user DN - ldapClient.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: "cn=user1" - }).returns(BluebirdPromise.resolve([{ - dn: USER_DN - }])); - - // Retrieve groups - ldapClient.searchAsyncStub.withArgs("ou=groups,dc=example,dc=com", { - scope: "sub", - attributes: ["cn"], - filter: "member=" + USER_DN - }).returns(BluebirdPromise.resolve([{ - cn: "group1" - }])); - - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, ldapClient, Winston); - - return client.searchGroups("user1") - .then(function (groups: string[]) { - Assert.deepEqual(groups, ["group1"]); - }); - }); - - it("should retrieve mail from custom attribute", function () { - const USER_DN = "cn=user1,ou=users,dc=example,dc=com"; - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn", - mail_attribute: "custom_mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const connector = new ConnectorStub(); - // Retrieve user DN - connector.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: "cn=user1" - }).returns(BluebirdPromise.resolve([{ - dn: USER_DN - }])); - - // Retrieve email - connector.searchAsyncStub.withArgs("cn=user1,ou=users,dc=example,dc=com", { - scope: "base", - sizeLimit: 1, - attributes: ["custom_mail"], - }).returns(BluebirdPromise.resolve([{ - custom_mail: "user1@example.com" - }])); - - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, connector, Winston); - - return client.searchEmails("user1") - .then(function (emails: string[]) { - Assert.deepEqual(emails, ["user1@example.com"]); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/Session.ts b/themes/main/server/src/lib/authentication/backends/ldap/Session.ts deleted file mode 100644 index e0284b3c4..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/Session.ts +++ /dev/null @@ -1,156 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import exceptions = require("../../../Exceptions"); -import { EventEmitter } from "events"; -import { ISession } from "./ISession"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Winston } from "../../../../../types/Dependencies"; -import Util = require("util"); -import { HashGenerator } from "../../../utils/HashGenerator"; -import { IConnector } from "./connector/IConnector"; - -export class Session implements ISession { - private userDN: string; - private password: string; - private connector: IConnector; - private logger: Winston; - private options: LdapConfiguration; - - private groupsSearchBase: string; - private usersSearchBase: string; - - constructor(userDN: string, password: string, options: LdapConfiguration, - connector: IConnector, logger: Winston) { - this.options = options; - this.logger = logger; - this.userDN = userDN; - this.password = password; - this.connector = connector; - - this.groupsSearchBase = (this.options.additional_groups_dn) - ? Util.format("%s,%s", this.options.additional_groups_dn, this.options.base_dn) - : this.options.base_dn; - - this.usersSearchBase = (this.options.additional_users_dn) - ? Util.format("%s,%s", this.options.additional_users_dn, this.options.base_dn) - : this.options.base_dn; - } - - open(): BluebirdPromise { - this.logger.debug("LDAP: Bind user '%s'", this.userDN); - return this.connector.bindAsync(this.userDN, this.password) - .error(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapBindError(err.message)); - }); - } - - close(): BluebirdPromise { - this.logger.debug("LDAP: Unbind user '%s'", this.userDN); - return this.connector.unbindAsync() - .error(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapBindError(err.message)); - }); - } - - private createGroupsFilter(userGroupsFilter: string, username: string): BluebirdPromise { - if (userGroupsFilter.indexOf("{0}") > 0) { - return BluebirdPromise.resolve(userGroupsFilter.replace("{0}", username)); - } - else if (userGroupsFilter.indexOf("{dn}") > 0) { - return this.searchUserDn(username) - .then(function (userDN: string) { - return BluebirdPromise.resolve(userGroupsFilter.replace("{dn}", userDN)); - }); - } - return BluebirdPromise.resolve(userGroupsFilter); - } - - searchGroups(username: string): BluebirdPromise { - const that = this; - return this.createGroupsFilter(this.options.groups_filter, username) - .then(function (groupsFilter: string) { - that.logger.debug("Computed groups filter is %s", groupsFilter); - const query = { - scope: "sub", - attributes: [that.options.group_name_attribute], - filter: groupsFilter - }; - return that.connector.searchAsync(that.groupsSearchBase, query); - }) - .then(function (docs: { cn: string }[]) { - const groups = docs.map((doc: any) => { return doc.cn; }); - that.logger.debug("LDAP: groups of user %s are [%s]", username, groups.join(",")); - return BluebirdPromise.resolve(groups); - }); - } - - searchUserDn(username: string): BluebirdPromise { - const that = this; - const filter = this.options.users_filter.replace("{0}", username); - this.logger.debug("Computed users filter is %s", filter); - const query = { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: filter - }; - - that.logger.debug("LDAP: searching for user dn of %s", username); - return that.connector.searchAsync(this.usersSearchBase, query) - .then(function (users: { dn: string }[]) { - if (users.length > 0) { - that.logger.debug("LDAP: retrieved user dn is %s", users[0].dn); - return BluebirdPromise.resolve(users[0].dn); - } - return BluebirdPromise.reject(new Error( - Util.format("No user DN found for user '%s'", username))); - }); - } - - searchEmails(username: string): BluebirdPromise { - const that = this; - const query = { - scope: "base", - sizeLimit: 1, - attributes: [this.options.mail_attribute] - }; - - return this.searchUserDn(username) - .then(function (userDN) { - return that.connector.searchAsync(userDN, query); - }) - .then(function (docs: { [mail_attribute: string]: string }[]) { - const emails: string[] = docs - .filter((d) => { return typeof d[that.options.mail_attribute] === "string"; }) - .map((d) => { return d[that.options.mail_attribute]; }); - that.logger.debug("LDAP: emails of user '%s' are %s", username, emails); - return BluebirdPromise.resolve(emails); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapError("Error while searching emails. " + err.stack)); - }); - } - - modifyPassword(username: string, newPassword: string): BluebirdPromise { - const that = this; - this.logger.debug("LDAP: update password of user '%s'", username); - return this.searchUserDn(username) - .then(function (userDN: string) { - return BluebirdPromise.join( - HashGenerator.ssha512(newPassword), - BluebirdPromise.resolve(userDN)); - }) - .then(function (res: string[]) { - const change = { - operation: "replace", - modification: { - userPassword: res[0] - } - }; - that.logger.debug("Password new='%s'", change.modification.userPassword); - return that.connector.modifyAsync(res[1], change); - }) - .then(function () { - return that.connector.unbindAsync(); - }); - } -} diff --git a/themes/main/server/src/lib/authentication/backends/ldap/SessionFactory.ts b/themes/main/server/src/lib/authentication/backends/ldap/SessionFactory.ts deleted file mode 100644 index 0b6c4bff3..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/SessionFactory.ts +++ /dev/null @@ -1,37 +0,0 @@ -import Ldapjs = require("ldapjs"); -import Winston = require("winston"); - -import { IConnectorFactory } from "./connector/IConnectorFactory"; -import { ISessionFactory } from "./ISessionFactory"; -import { ISession } from "./ISession"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Session } from "./Session"; -import { SafeSession } from "./SafeSession"; - - -export class SessionFactory implements ISessionFactory { - private config: LdapConfiguration; - private connectorFactory: IConnectorFactory; - private logger: typeof Winston; - - constructor(ldapConfiguration: LdapConfiguration, - connectorFactory: IConnectorFactory, - logger: typeof Winston) { - this.config = ldapConfiguration; - this.connectorFactory = connectorFactory; - this.logger = logger; - } - - create(userDN: string, password: string): ISession { - const connector = this.connectorFactory.create(); - return new SafeSession( - new Session( - userDN, - password, - this.config, - connector, - this.logger - ) - ); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts b/themes/main/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts deleted file mode 100644 index face3930d..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); - -import { ISession } from "./ISession"; -import { ISessionFactory } from "./ISessionFactory"; - -export class SessionFactoryStub implements ISessionFactory { - createStub: Sinon.SinonStub; - - constructor() { - this.createStub = Sinon.stub(); - } - - create(userDN: string, password: string): ISession { - return this.createStub(userDN, password); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts b/themes/main/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts deleted file mode 100644 index 5faf2ba18..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); - -import { ISession } from "./ISession"; - -export class SessionStub implements ISession { - openStub: Sinon.SinonStub; - closeStub: Sinon.SinonStub; - searchUserDnStub: Sinon.SinonStub; - searchEmailsStub: Sinon.SinonStub; - searchGroupsStub: Sinon.SinonStub; - modifyPasswordStub: Sinon.SinonStub; - - constructor() { - this.openStub = Sinon.stub(); - this.closeStub = Sinon.stub(); - this.searchUserDnStub = Sinon.stub(); - this.searchEmailsStub = Sinon.stub(); - this.searchGroupsStub = Sinon.stub(); - this.modifyPasswordStub = Sinon.stub(); - } - - open(): Bluebird { - return this.openStub(); - } - - close(): Bluebird { - return this.closeStub(); - } - - searchUserDn(username: string): Bluebird { - return this.searchUserDnStub(username); - } - - searchEmails(username: string): Bluebird { - return this.searchEmailsStub(username); - } - - searchGroups(username: string): Bluebird { - return this.searchGroupsStub(username); - } - - modifyPassword(username: string, newPassword: string): Bluebird { - return this.modifyPasswordStub(username, newPassword); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/connector/Connector.ts b/themes/main/server/src/lib/authentication/backends/ldap/connector/Connector.ts deleted file mode 100644 index 2542ea7f9..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/connector/Connector.ts +++ /dev/null @@ -1,69 +0,0 @@ -import LdapJs = require("ldapjs"); -import EventEmitter = require("events"); -import Bluebird = require("bluebird"); -import { IConnector } from "./IConnector"; -import Exceptions = require("../../../../Exceptions"); - -interface SearchEntry { - object: any; -} - -export interface ClientAsync { - on(event: string, callback: (data?: any) => void): void; - bindAsync(username: string, password: string): Bluebird; - unbindAsync(): Bluebird; - searchAsync(base: string, query: LdapJs.SearchOptions): Bluebird; - modifyAsync(userdn: string, change: LdapJs.Change): Bluebird; -} - -export class Connector implements IConnector { - private client: ClientAsync; - - constructor(url: string, ldapjs: typeof LdapJs) { - const ldapClient = ldapjs.createClient({ - url: url, - reconnect: true - }); - - /*const clientLogger = (ldapClient as any).log; - if (clientLogger) { - clientLogger.level("trace"); - }*/ - - this.client = Bluebird.promisifyAll(ldapClient) as any; - } - - bindAsync(username: string, password: string): Bluebird { - return this.client.bindAsync(username, password); - } - - unbindAsync(): Bluebird { - return this.client.unbindAsync(); - } - - searchAsync(base: string, query: any): Bluebird { - const that = this; - return this.client.searchAsync(base, query) - .then(function (res: EventEmitter) { - const doc: SearchEntry[] = []; - return new Bluebird((resolve, reject) => { - res.on("searchEntry", function (entry: SearchEntry) { - doc.push(entry.object); - }); - res.on("error", function (err: Error) { - reject(new Exceptions.LdapSearchError(err.message)); - }); - res.on("end", function () { - resolve(doc); - }); - }); - }) - .catch(function (err: Error) { - return Bluebird.reject(new Exceptions.LdapSearchError(err.message)); - }); - } - - modifyAsync(dn: string, changeRequest: any): Bluebird { - return this.client.modifyAsync(dn, changeRequest); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts b/themes/main/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts deleted file mode 100644 index 61fef07a4..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IConnector } from "./IConnector"; -import { Connector } from "./Connector"; -import { LdapConfiguration } from "../../../../configuration/schema/LdapConfiguration"; -import { Ldapjs } from "Dependencies"; - -export class ConnectorFactory { - private configuration: LdapConfiguration; - private ldapjs: Ldapjs; - - constructor(configuration: LdapConfiguration, ldapjs: Ldapjs) { - this.configuration = configuration; - this.ldapjs = ldapjs; - } - - create(): IConnector { - return new Connector(this.configuration.url, this.ldapjs); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts b/themes/main/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts deleted file mode 100644 index d11fa6386..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); - -import { IConnectorFactory } from "./IConnectorFactory"; -import { IConnector } from "./IConnector"; - -export class ConnectorFactoryStub implements IConnectorFactory { - createStub: Sinon.SinonStub; - - constructor() { - this.createStub = Sinon.stub(); - } - - create(): IConnector { - return this.createStub(); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts b/themes/main/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts deleted file mode 100644 index 0b78225bf..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); - -import { IConnector } from "./IConnector"; - -export class ConnectorStub implements IConnector { - bindAsyncStub: Sinon.SinonStub; - unbindAsyncStub: Sinon.SinonStub; - searchAsyncStub: Sinon.SinonStub; - modifyAsyncStub: Sinon.SinonStub; - - constructor() { - this.bindAsyncStub = Sinon.stub(); - this.unbindAsyncStub = Sinon.stub(); - this.searchAsyncStub = Sinon.stub(); - this.modifyAsyncStub = Sinon.stub(); - } - - bindAsync(username: string, password: string): BluebirdPromise { - return this.bindAsyncStub(username, password); - } - - unbindAsync(): BluebirdPromise { - return this.unbindAsyncStub(); - } - - searchAsync(base: string, query: any): BluebirdPromise { - return this.searchAsyncStub(base, query); - } - - modifyAsync(dn: string, changeRequest: any): BluebirdPromise { - return this.modifyAsyncStub(dn, changeRequest); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/connector/IConnector.ts b/themes/main/server/src/lib/authentication/backends/ldap/connector/IConnector.ts deleted file mode 100644 index 1e63ab193..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/connector/IConnector.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Bluebird = require("bluebird"); -import EventEmitter = require("events"); - -export interface IConnector { - bindAsync(username: string, password: string): Bluebird; - unbindAsync(): Bluebird; - searchAsync(base: string, query: any): Bluebird; - modifyAsync(dn: string, changeRequest: any): Bluebird; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts b/themes/main/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts deleted file mode 100644 index f9ed65eff..000000000 --- a/themes/main/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { IConnector } from "./IConnector"; - -export interface IConnectorFactory { - create(): IConnector; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/totp/ITotpHandler.ts b/themes/main/server/src/lib/authentication/totp/ITotpHandler.ts deleted file mode 100644 index d600d31e4..000000000 --- a/themes/main/server/src/lib/authentication/totp/ITotpHandler.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { TOTPSecret } from "../../../../types/TOTPSecret"; - -export interface ITotpHandler { - generate(label: string, issuer: string): TOTPSecret; - validate(token: string, secret: string): boolean; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/totp/TotpHandler.spec.ts b/themes/main/server/src/lib/authentication/totp/TotpHandler.spec.ts deleted file mode 100644 index 67cffa638..000000000 --- a/themes/main/server/src/lib/authentication/totp/TotpHandler.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import { TotpHandler } from "./TotpHandler"; -import Sinon = require("sinon"); -import Speakeasy = require("speakeasy"); -import Assert = require("assert"); - -describe("authentication/totp/TotpHandler", function() { - let totpValidator: TotpHandler; - let validateStub: Sinon.SinonStub; - - beforeEach(() => { - validateStub = Sinon.stub(Speakeasy.totp, "verify"); - totpValidator = new TotpHandler(Speakeasy); - }); - - afterEach(function() { - validateStub.restore(); - }); - - it("should validate the TOTP token", function() { - const totp_secret = "NBD2ZV64R9UV1O7K"; - const token = "token"; - validateStub.withArgs({ - secret: totp_secret, - token: token, - encoding: "base32", - window: 1 - }).returns(true); - Assert(totpValidator.validate(token, totp_secret)); - }); - - it("should not validate a wrong TOTP token", function() { - const totp_secret = "NBD2ZV64R9UV1O7K"; - const token = "wrong token"; - validateStub.returns(false); - Assert(!totpValidator.validate(token, totp_secret)); - }); -}); - diff --git a/themes/main/server/src/lib/authentication/totp/TotpHandler.ts b/themes/main/server/src/lib/authentication/totp/TotpHandler.ts deleted file mode 100644 index dfab502a7..000000000 --- a/themes/main/server/src/lib/authentication/totp/TotpHandler.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ITotpHandler } from "./ITotpHandler"; -import { TOTPSecret } from "../../../../types/TOTPSecret"; -import Speakeasy = require("speakeasy"); - -const TOTP_ENCODING = "base32"; -const WINDOW: number = 1; - -export class TotpHandler implements ITotpHandler { - private speakeasy: typeof Speakeasy; - - constructor(speakeasy: typeof Speakeasy) { - this.speakeasy = speakeasy; - } - - generate(label: string, issuer: string): TOTPSecret { - const secret = this.speakeasy.generateSecret({ - otpauth_url: false - }) as TOTPSecret; - - secret.otpauth_url = this.speakeasy.otpauthURL({ - secret: secret.ascii, - label: label, - issuer: issuer - }); - return secret; - } - - validate(token: string, secret: string): boolean { - return this.speakeasy.totp.verify({ - secret: secret, - encoding: TOTP_ENCODING, - token: token, - window: WINDOW - }); - } -} diff --git a/themes/main/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts b/themes/main/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts deleted file mode 100644 index ea93330d9..000000000 --- a/themes/main/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import { ITotpHandler } from "./ITotpHandler"; -import { TOTPSecret } from "../../../../types/TOTPSecret"; - -export class TotpHandlerStub implements ITotpHandler { - generateStub: Sinon.SinonStub; - validateStub: Sinon.SinonStub; - - constructor() { - this.generateStub = Sinon.stub(); - this.validateStub = Sinon.stub(); - } - - generate(label: string, issuer: string): TOTPSecret { - return this.generateStub(label, issuer); - } - - validate(token: string, secret: string): boolean { - return this.validateStub(token, secret); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/u2f/IU2fHandler.ts b/themes/main/server/src/lib/authentication/u2f/IU2fHandler.ts deleted file mode 100644 index b9b7d6f26..000000000 --- a/themes/main/server/src/lib/authentication/u2f/IU2fHandler.ts +++ /dev/null @@ -1,9 +0,0 @@ -import U2f = require("u2f"); - -export interface IU2fHandler { - request(appId: string, keyHandle?: string): U2f.Request; - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error; - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authentication/u2f/U2fHandler.ts b/themes/main/server/src/lib/authentication/u2f/U2fHandler.ts deleted file mode 100644 index bf3891e5b..000000000 --- a/themes/main/server/src/lib/authentication/u2f/U2fHandler.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { IU2fHandler } from "./IU2fHandler"; -import U2f = require("u2f"); - -export class U2fHandler implements IU2fHandler { - private u2f: typeof U2f; - - constructor(u2f: typeof U2f) { - this.u2f = u2f; - } - - request(appId: string, keyHandle?: string): U2f.Request { - return this.u2f.request(appId, keyHandle); - } - - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error { - return this.u2f.checkRegistration(registrationRequest, registrationResponse); - } - - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error { - return this.u2f.checkSignature(signatureRequest, signatureResponse, publicKey); - } -} diff --git a/themes/main/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts b/themes/main/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts deleted file mode 100644 index 135d7eb06..000000000 --- a/themes/main/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import U2f = require("u2f"); -import { IU2fHandler } from "./IU2fHandler"; - - -export class U2fHandlerStub implements IU2fHandler { - requestStub: Sinon.SinonStub; - checkRegistrationStub: Sinon.SinonStub; - checkSignatureStub: Sinon.SinonStub; - - constructor() { - this.requestStub = Sinon.stub(); - this.checkRegistrationStub = Sinon.stub(); - this.checkSignatureStub = Sinon.stub(); - } - - request(appId: string, keyHandle?: string): U2f.Request { - return this.requestStub(appId, keyHandle); - } - - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error { - return this.checkRegistrationStub(registrationRequest, registrationResponse); - } - - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error { - return this.checkSignatureStub(signatureRequest, signatureResponse, publicKey); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authorization/Authorizer.spec.ts b/themes/main/server/src/lib/authorization/Authorizer.spec.ts deleted file mode 100644 index 586814042..000000000 --- a/themes/main/server/src/lib/authorization/Authorizer.spec.ts +++ /dev/null @@ -1,372 +0,0 @@ - -import Assert = require("assert"); -import winston = require("winston"); -import { Authorizer } from "./Authorizer"; -import { ACLConfiguration, ACLRule } from "../configuration/schema/AclConfiguration"; -import { Level } from "./Level"; - -describe("authorization/Authorizer", function () { - let authorizer: Authorizer; - let configuration: ACLConfiguration; - - describe("configuration is null", function() { - it("should allow access to anything, anywhere for anybody", function() { - configuration = undefined; - authorizer = new Authorizer(configuration, winston); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc"}, {user: "user1", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user2", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "admin.example.com", resource: "/"}, {user: "user3", groups: ["group3"]}), Level.BYPASS); - }); - }); - - describe("configuration is not null", function () { - beforeEach(function () { - configuration = { - default_policy: "deny", - rules: [] - }; - authorizer = new Authorizer(configuration, winston); - }); - - describe("check access control with default policy to deny", function () { - beforeEach(function () { - configuration.default_policy = "deny"; - }); - - it("should deny access when no rule is provided", function () { - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should control access when multiple domain matcher is provided", function () { - configuration.rules = [{ - domain: "*.mail.example.com", - policy: "two_factor", - subject: "user:user1", - resources: [".*"] - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "mx1.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mx1.server.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should allow access to all resources when resources is not provided", function () { - configuration.rules = [{ - domain: "*.mail.example.com", - policy: "two_factor", - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "mx1.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mx1.server.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - describe("check user rules", function () { - it("should allow access when user has a matching allowing rule", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/another/resource"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should deny to other users", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user2", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/another/resource"}, {user: "user2", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, {user: "user2", groups: ["group1"]}), Level.DENY); - }); - - it("should allow user access only to specific resources", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["/private/.*", "^/begin", "/end$"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/class"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/middle/private/class"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/begin"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/not/begin"}, {user: "user1", groups: ["group1"]}), Level.DENY); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc/end"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc/end/x"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should allow access to multiple domains", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }, { - domain: "home1.example.com", - policy: "one_factor", - resources: [".*"], - subject: "user:user1" - }, { - domain: "home2.example.com", - policy: "deny", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home1.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.ONE_FACTOR); - Assert.equal(authorizer.authorization({domain: "home2.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home3.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should apply rules in order", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "one_factor", - resources: ["/my/private/resource"], - subject: "user:user1" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/my/private/.*"], - subject: "user:user1" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/my/.*"], - subject: "user:user1" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/poney"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/private/duck"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/private/resource"}, {user: "user1", groups: ["group1"]}), Level.ONE_FACTOR); - }); - }); - - describe("check group rules", function () { - it("should allow access when user is in group having a matching allowing rule", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/$"], - subject: "group:group1" - }, { - domain: "home.example.com", - policy: "one_factor", - resources: ["^/test$"], - subject: "group:group2" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/private$"], - subject: "group:group2" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.ONE_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - }); - }); - }); - - describe("check any rules", function () { - it("should control access when any rules are defined", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "bypass", - resources: ["^/public$"] - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/private$"] - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, - {user: "user4", groups: ["group5"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user4", groups: ["group5"]}), Level.DENY); - }); - }); - - describe("check access control with default policy to allow", function () { - beforeEach(function () { - configuration.default_policy = "bypass"; - }); - - it("should allow access to anything when no rule is provided", function () { - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - }); - - it("should deny access to one resource when defined", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["/test"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - }); - }); - - describe("check access control with complete use case", function () { - beforeEach(function () { - configuration.default_policy = "deny"; - }); - - it("should control access of multiple user (real use case)", function () { - // Let say we have three users: admin, john, harry. - // admin is in groups ["admins"] - // john is in groups ["dev", "admin-private"] - // harry is in groups ["dev"] - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/public$", "^/$"] - }, { - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "group:admins" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/?.*"], - subject: "group:admin-private" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/john$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/harry"], - subject: "user:harry" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - }); - - it("should allow when allowed at group level and denied at user level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"], - subject: "group:dev" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should allow access when allowed at 'any' level and denied at user level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should allow access when allowed at 'any' level and denied at group level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "group:dev" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should respect rules precedence", function () { - // the priority from least to most is 'default_policy', 'all', 'group', 'user' - // and the first rules in each category as a lower priority than the latest. - // You can think of it that way: they override themselves inside each category. - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "group:dev" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - }); - }); - }); -}); diff --git a/themes/main/server/src/lib/authorization/Authorizer.ts b/themes/main/server/src/lib/authorization/Authorizer.ts deleted file mode 100644 index 889b7ec20..000000000 --- a/themes/main/server/src/lib/authorization/Authorizer.ts +++ /dev/null @@ -1,85 +0,0 @@ - -import { ACLConfiguration, ACLPolicy, ACLRule } from "../configuration/schema/AclConfiguration"; -import { IAuthorizer } from "./IAuthorizer"; -import { Winston } from "../../../types/Dependencies"; -import { MultipleDomainMatcher } from "./MultipleDomainMatcher"; -import { Level } from "./Level"; -import { Object } from "./Object"; -import { Subject } from "./Subject"; - -function MatchDomain(actualDomain: string) { - return function (rule: ACLRule): boolean { - return MultipleDomainMatcher.match(actualDomain, rule.domain); - }; -} - -function MatchResource(actualResource: string) { - return function (rule: ACLRule): boolean { - // If resources key is not provided, the rule applies to all resources. - if (!rule.resources) return true; - - for (let i = 0; i < rule.resources.length; ++i) { - const regexp = new RegExp(rule.resources[i]); - if (regexp.test(actualResource)) return true; - } - return false; - }; -} - -function MatchSubject(subject: Subject) { - return (rule: ACLRule) => { - // If no subject, matches anybody - if (!rule.subject) return true; - - if (rule.subject.startsWith("user:")) { - const ruleUser = rule.subject.split(":")[1]; - if (subject.user == ruleUser) return true; - } - - if (rule.subject.startsWith("group:")) { - const ruleGroup = rule.subject.split(":")[1]; - if (subject.groups.indexOf(ruleGroup) > -1) return true; - } - return false; - }; -} - -export class Authorizer implements IAuthorizer { - private logger: Winston; - private readonly configuration: ACLConfiguration; - - constructor(configuration: ACLConfiguration, logger_: Winston) { - this.logger = logger_; - this.configuration = configuration; - } - - private getMatchingRules(object: Object, subject: Subject): ACLRule[] { - const rules = this.configuration.rules; - if (!rules) return []; - return rules - .filter(MatchDomain(object.domain)) - .filter(MatchResource(object.resource)) - .filter(MatchSubject(subject)); - } - - private ruleToLevel(policy: string): Level { - if (policy == "bypass") { - return Level.BYPASS; - } else if (policy == "one_factor") { - return Level.ONE_FACTOR; - } else if (policy == "two_factor") { - return Level.TWO_FACTOR; - } - return Level.DENY; - } - - authorization(object: Object, subject: Subject): Level { - if (!this.configuration) return Level.BYPASS; - - const rules = this.getMatchingRules(object, subject); - - return (rules.length > 0) - ? this.ruleToLevel(rules[0].policy) // extract the policy of the first matching rule - : this.ruleToLevel(this.configuration.default_policy); // otherwise use the default policy - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authorization/AuthorizerStub.spec.ts b/themes/main/server/src/lib/authorization/AuthorizerStub.spec.ts deleted file mode 100644 index 9bd6f4a85..000000000 --- a/themes/main/server/src/lib/authorization/AuthorizerStub.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import Sinon = require("sinon"); -import { IAuthorizer } from "./IAuthorizer"; -import { Level } from "./Level"; -import { Object } from "./Object"; -import { Subject } from "./Subject"; - -export class AuthorizerStub implements IAuthorizer { - authorizationMock: Sinon.SinonStub; - - constructor() { - this.authorizationMock = Sinon.stub(); - } - - authorization(object: Object, subject: Subject): Level { - return this.authorizationMock(object, subject); - } -} diff --git a/themes/main/server/src/lib/authorization/IAuthorizer.ts b/themes/main/server/src/lib/authorization/IAuthorizer.ts deleted file mode 100644 index fe7ba367a..000000000 --- a/themes/main/server/src/lib/authorization/IAuthorizer.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Level } from "./Level"; -import { Subject } from "./Subject"; -import { Object } from "./Object"; - -export interface IAuthorizer { - authorization(object: Object, subject: Subject): Level; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authorization/Level.ts b/themes/main/server/src/lib/authorization/Level.ts deleted file mode 100644 index d12802610..000000000 --- a/themes/main/server/src/lib/authorization/Level.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum Level { - BYPASS = 0, - ONE_FACTOR = 1, - TWO_FACTOR = 2, - DENY = 3 -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authorization/MultipleDomainMatcher.ts b/themes/main/server/src/lib/authorization/MultipleDomainMatcher.ts deleted file mode 100644 index 64c647a4a..000000000 --- a/themes/main/server/src/lib/authorization/MultipleDomainMatcher.ts +++ /dev/null @@ -1,12 +0,0 @@ - -export class MultipleDomainMatcher { - static match(domain: string, pattern: string): boolean { - if (pattern.startsWith("*") && - domain.endsWith(pattern.substr(1))) { - return true; - } - else if (domain == pattern) { - return true; - } - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authorization/Object.ts b/themes/main/server/src/lib/authorization/Object.ts deleted file mode 100644 index 5411b0d24..000000000 --- a/themes/main/server/src/lib/authorization/Object.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface Object { - domain: string; - resource: string; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/authorization/Subject.ts b/themes/main/server/src/lib/authorization/Subject.ts deleted file mode 100644 index 310d6b4c0..000000000 --- a/themes/main/server/src/lib/authorization/Subject.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface Subject { - user: string; - groups: string[]; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/ConfigurationParser.spec.ts b/themes/main/server/src/lib/configuration/ConfigurationParser.spec.ts deleted file mode 100644 index 60c0f6182..000000000 --- a/themes/main/server/src/lib/configuration/ConfigurationParser.spec.ts +++ /dev/null @@ -1,171 +0,0 @@ -import * as Assert from "assert"; -import { Configuration } from "./schema/Configuration"; -import { ACLConfiguration } from "./schema/AclConfiguration"; -import { ConfigurationParser } from "./ConfigurationParser"; - -describe("configuration/ConfigurationParser", function () { - function buildYamlConfig(): Configuration { - const yaml_config: Configuration = { - port: 8080, - authentication_backend: { - ldap: { - url: "http://ldap", - base_dn: "dc=example,dc=com", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - user: "user", - password: "pass" - }, - }, - session: { - domain: "example.com", - secret: "secret", - expiration: 40000 - }, - storage: { - local: { - path: "/mydirectory" - } - }, - regulation: { - max_retries: 3, - find_time: 5 * 60, - ban_time: 5 * 60 - }, - logs_level: "debug", - notifier: { - email: { - username: "user", - password: "password", - sender: "admin@example.com", - service: "gmail" - } - } - }; - return yaml_config; - } - - describe("port", function () { - it("should read the port from the yaml file", function () { - const yaml_config = buildYamlConfig(); - yaml_config.port = 7070; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.port, 7070); - }); - - it("should default the port to 8080 if not provided", function () { - const yaml_config = buildYamlConfig(); - delete yaml_config.port; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.port, 8080); - }); - }); - - describe("test session configuration", function() { - it("should get the session attributes", function () { - const yaml_config = buildYamlConfig(); - yaml_config.session = { - domain: "example.com", - secret: "secret", - expiration: 3600, - inactivity: 4000 - }; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.session.domain, "example.com"); - Assert.equal(config.session.secret, "secret"); - Assert.equal(config.session.expiration, 3600); - Assert.equal(config.session.inactivity, 4000); - }); - - it("should be ok not specifying inactivity", function () { - const yaml_config = buildYamlConfig(); - yaml_config.session = { - domain: "example.com", - secret: "secret", - expiration: 3600 - }; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.session.domain, "example.com"); - Assert.equal(config.session.secret, "secret"); - Assert.equal(config.session.expiration, 3600); - Assert.equal(config.session.inactivity, undefined); - }); - }); - - it("should get the log level", function () { - const yaml_config = buildYamlConfig(); - yaml_config.logs_level = "debug"; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.logs_level, "debug"); - }); - - it("should get the notifier config", function () { - const userConfig = buildYamlConfig(); - userConfig.notifier = { - email: { - username: "user", - password: "pass", - sender: "admin@example.com", - service: "gmail" - } - }; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.notifier, { - email: { - username: "user", - password: "pass", - sender: "admin@example.com", - service: "gmail" - } - }); - }); - - describe("access_control", function() { - it("should adapt access_control when it is already ok", function () { - const userConfig = buildYamlConfig(); - userConfig.access_control = { - default_policy: "deny", - rules: [{ - domain: "www.example.com", - policy: "two_factor", - subject: "user:user" - }, { - domain: "public.example.com", - policy: "two_factor" - }] - }; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.access_control, { - default_policy: "deny", - rules: [{ - domain: "www.example.com", - policy: "two_factor", - subject: "user:user" - }, { - domain: "public.example.com", - policy: "two_factor" - }] - } as ACLConfiguration); - }); - - - it("should adapt access_control when it is empty", function () { - const userConfig = buildYamlConfig(); - userConfig.access_control = {} as any; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.access_control, { - default_policy: "bypass", - rules: [] - }); - }); - }); - - describe("default_redirection_url", function() { - it("should parse default_redirection_url", function() { - const userConfig = buildYamlConfig(); - userConfig.default_redirection_url = "dummy_url"; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.default_redirection_url, "dummy_url"); - }); - }); -}); diff --git a/themes/main/server/src/lib/configuration/ConfigurationParser.ts b/themes/main/server/src/lib/configuration/ConfigurationParser.ts deleted file mode 100644 index d92d163c4..000000000 --- a/themes/main/server/src/lib/configuration/ConfigurationParser.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import * as ObjectPath from "object-path"; -import { Configuration, complete } from "./schema/Configuration"; -import Ajv = require("ajv"); -import Path = require("path"); -import Util = require("util"); - -export class ConfigurationParser { - private static parseTypes(configuration: Configuration): string[] { - const schema = require(Path.resolve(__dirname, "./Configuration.schema.json")); - const ajv = new Ajv({ - allErrors: true, - missingRefs: "fail" - }); - ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-06.json")); - const valid = ajv.validate(schema, configuration); - if (!valid) - return ajv.errors.map( - (e: Ajv.ErrorObject) => { return ajv.errorsText([e]); }); - return []; - } - - static parse(configuration: Configuration): Configuration { - const validationErrors = this.parseTypes(configuration); - if (validationErrors.length > 0) { - validationErrors.forEach((e: string) => { console.log(e); }); - throw new Error("Malformed configuration (schema). Please double-check your configuration file."); - } - - const [newConfiguration, completionErrors] = complete(configuration); - - if (completionErrors.length > 0) { - completionErrors.forEach((e: string) => { console.log(e); }); - throw new Error("Malformed configuration (validator). Please double-check your configuration file."); - } - return newConfiguration; - } -} - diff --git a/themes/main/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts b/themes/main/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts deleted file mode 100644 index d4a3093ee..000000000 --- a/themes/main/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { SessionConfigurationBuilder } from "./SessionConfigurationBuilder"; -import { Configuration } from "./schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; - -import ExpressSession = require("express-session"); -import ConnectRedis = require("connect-redis"); -import Sinon = require("sinon"); -import Assert = require("assert"); - -describe("configuration/SessionConfigurationBuilder", function () { - const configuration: Configuration = { - access_control: { - default_policy: "deny", - rules: [] - }, - totp: { - issuer: "authelia.com" - }, - authentication_backend: { - ldap: { - url: "ldap://ldap", - user: "user", - base_dn: "dc=example,dc=com", - password: "password", - additional_groups_dn: "ou=groups", - additional_users_dn: "ou=users", - group_name_attribute: "", - groups_filter: "", - mail_attribute: "", - users_filter: "" - }, - }, - logs_level: "debug", - notifier: { - filesystem: { - filename: "/test" - } - }, - port: 8080, - session: { - name: "authelia_session", - domain: "example.com", - expiration: 3600, - secret: "secret" - }, - regulation: { - max_retries: 3, - ban_time: 5 * 60, - find_time: 5 * 60 - }, - storage: { - local: { - in_memory: true - } - } - }; - - const deps: GlobalDependencies = { - ConnectRedis: Sinon.spy() as any, - ldapjs: Sinon.spy() as any, - nedb: Sinon.spy() as any, - session: Sinon.spy() as any, - speakeasy: Sinon.spy() as any, - u2f: Sinon.spy() as any, - winston: Sinon.spy() as any, - Redis: Sinon.spy() as any - }; - - it("should return session options without redis options", function () { - const options = SessionConfigurationBuilder.build(configuration, deps); - const expectedOptions = { - name: "authelia_session", - secret: "secret", - resave: false, - saveUninitialized: true, - cookie: { - secure: true, - httpOnly: true, - maxAge: 3600, - domain: "example.com" - } - }; - - Assert.deepEqual(expectedOptions, options); - }); - - it("should return session options with redis options", function () { - configuration.session["redis"] = { - host: "redis.example.com", - port: 6379 - }; - const RedisStoreMock = Sinon.spy(); - const redisClient = Sinon.mock().returns({ on: Sinon.spy() }); - - deps.ConnectRedis = Sinon.stub().returns(RedisStoreMock) as any; - deps.Redis = { - createClient: Sinon.mock().returns(redisClient) - } as any; - - const options = SessionConfigurationBuilder.build(configuration, deps); - - const expectedOptions: ExpressSession.SessionOptions = { - secret: "secret", - resave: false, - saveUninitialized: true, - name: "authelia_session", - cookie: { - secure: true, - httpOnly: true, - maxAge: 3600, - domain: "example.com" - }, - store: Sinon.match.object as any - }; - - Assert((deps.ConnectRedis as Sinon.SinonStub).calledWith(deps.session)); - Assert.equal(options.secret, expectedOptions.secret); - Assert.equal(options.resave, expectedOptions.resave); - Assert.equal(options.saveUninitialized, expectedOptions.saveUninitialized); - Assert.deepEqual(options.cookie, expectedOptions.cookie); - Assert(options.store != undefined); - }); - - it("should return session options with redis password", function () { - configuration.session["redis"] = { - host: "redis.example.com", - port: 6379, - password: "authelia_pass" - }; - const RedisStoreMock = Sinon.spy(); - const redisClient = Sinon.mock().returns({ on: Sinon.spy() }); - const createClientStub = Sinon.stub(); - - deps.ConnectRedis = Sinon.stub().returns(RedisStoreMock) as any; - deps.Redis = { - createClient: createClientStub - } as any; - - createClientStub.returns(redisClient); - - const options = SessionConfigurationBuilder.build(configuration, deps); - - Assert(createClientStub.calledWith({ - host: "redis.example.com", - port: 6379, - password: "authelia_pass" - })); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/SessionConfigurationBuilder.ts b/themes/main/server/src/lib/configuration/SessionConfigurationBuilder.ts deleted file mode 100644 index 6ce643d9d..000000000 --- a/themes/main/server/src/lib/configuration/SessionConfigurationBuilder.ts +++ /dev/null @@ -1,52 +0,0 @@ -import ExpressSession = require("express-session"); -import Redis = require("redis"); - -import { Configuration } from "./schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; -import { RedisStoreOptions } from "connect-redis"; - -export class SessionConfigurationBuilder { - - static build(configuration: Configuration, deps: GlobalDependencies): ExpressSession.SessionOptions { - const sessionOptions: ExpressSession.SessionOptions = { - name: configuration.session.name, - secret: configuration.session.secret, - resave: false, - saveUninitialized: true, - cookie: { - secure: true, - httpOnly: true, - maxAge: configuration.session.expiration, - domain: configuration.session.domain - }, - }; - - if (configuration.session.redis) { - let redisOptions; - const options: Redis.ClientOpts = { - host: configuration.session.redis.host, - port: configuration.session.redis.port - }; - - if (configuration.session.redis.password) { - options["password"] = configuration.session.redis.password; - } - const client = deps.Redis.createClient(options); - - client.on("error", function (err: Error) { - console.error("Redis error:", err); - }); - - redisOptions = { - client: client, - logErrors: true - }; - - if (redisOptions) { - const RedisStore = deps.ConnectRedis(deps.session); - sessionOptions.store = new RedisStore(redisOptions); - } - } - return sessionOptions; - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/AclConfiguration.spec.ts b/themes/main/server/src/lib/configuration/schema/AclConfiguration.spec.ts deleted file mode 100644 index d1e2a03a2..000000000 --- a/themes/main/server/src/lib/configuration/schema/AclConfiguration.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ACLConfiguration, complete } from "./AclConfiguration"; -import Assert = require("assert"); - -describe("configuration/schema/AclConfiguration", function() { - it("should complete ACLConfiguration", function() { - const configuration: ACLConfiguration = {}; - const [newConfiguration, errors] = complete(configuration); - - Assert.deepEqual(newConfiguration.default_policy, "bypass"); - Assert.deepEqual(newConfiguration.rules, []); - }); - - it("should return errors when subject is not good", function() { - const configuration: ACLConfiguration = { - default_policy: "deny", - rules: [{ - domain: "dev.example.com", - subject: "user:abc", - policy: "bypass" - }, { - domain: "dev.example.com", - subject: "user:def", - policy: "bypass" - }, { - domain: "dev.example.com", - subject: "badkey:abc", - policy: "bypass" - }] - }; - const [newConfiguration, errors] = complete(configuration); - - Assert.deepEqual(errors, ["Rule 2 has wrong subject. It should be starting with user: or group:."]); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/AclConfiguration.ts b/themes/main/server/src/lib/configuration/schema/AclConfiguration.ts deleted file mode 100644 index 40401dd64..000000000 --- a/themes/main/server/src/lib/configuration/schema/AclConfiguration.ts +++ /dev/null @@ -1,41 +0,0 @@ - -export type ACLPolicy = "deny" | "bypass" | "one_factor" | "two_factor"; - -export type ACLRule = { - domain: string; - resources?: string[]; - subject?: string; - policy: ACLPolicy; -}; - -export interface ACLConfiguration { - default_policy?: ACLPolicy; - rules?: ACLRule[]; -} - -export function complete(configuration: ACLConfiguration): [ACLConfiguration, string[]] { - const newConfiguration: ACLConfiguration = (configuration) - ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.default_policy) { - newConfiguration.default_policy = "bypass"; - } - - if (!newConfiguration.rules) { - newConfiguration.rules = []; - } - - if (newConfiguration.rules.length > 0) { - const errors: string[] = []; - newConfiguration.rules.forEach((r, idx) => { - if (r.subject && !r.subject.match(/^(user|group):[a-zA-Z0-9]+$/)) { - errors.push(`Rule ${idx} has wrong subject. It should be starting with user: or group:.`); - } - }); - if (errors.length > 0) { - return [newConfiguration, errors]; - } - } - - return [newConfiguration, []]; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts b/themes/main/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts deleted file mode 100644 index 3ca86381c..000000000 --- a/themes/main/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { AuthenticationBackendConfiguration, complete } from "./AuthenticationBackendConfiguration"; -import Assert = require("assert"); - -describe("configuration/schema/AuthenticationBackendConfiguration", function() { - it("should ensure there is at least one key", function() { - const configuration: AuthenticationBackendConfiguration = {} as any; - const [newConfiguration, error] = complete(configuration); - - Assert.equal(error, "Authentication backend must have one of the following keys:`ldap` or `file`"); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts b/themes/main/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts deleted file mode 100644 index 7f77f894f..000000000 --- a/themes/main/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { LdapConfiguration } from "./LdapConfiguration"; -import { FileUsersDatabaseConfiguration } from "./FileUsersDatabaseConfiguration"; - -export interface AuthenticationBackendConfiguration { - ldap?: LdapConfiguration; - file?: FileUsersDatabaseConfiguration; -} - -export function complete( - configuration: AuthenticationBackendConfiguration) - : [AuthenticationBackendConfiguration, string] { - - const newConfiguration: AuthenticationBackendConfiguration = (configuration) - ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (Object.keys(newConfiguration).length != 1) { - return [ - newConfiguration, - "Authentication backend must have one of the following keys:" + - "`ldap` or `file`" - ]; - } - - return [newConfiguration, undefined]; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/Configuration.ts b/themes/main/server/src/lib/configuration/schema/Configuration.ts deleted file mode 100644 index 8d16a5fb2..000000000 --- a/themes/main/server/src/lib/configuration/schema/Configuration.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { ACLConfiguration, complete as AclConfigurationComplete } from "./AclConfiguration"; -import { AuthenticationBackendConfiguration, complete as AuthenticationBackendComplete } from "./AuthenticationBackendConfiguration"; -import { NotifierConfiguration, complete as NotifierConfigurationComplete } from "./NotifierConfiguration"; -import { RegulationConfiguration, complete as RegulationConfigurationComplete } from "./RegulationConfiguration"; -import { SessionConfiguration, complete as SessionConfigurationComplete } from "./SessionConfiguration"; -import { StorageConfiguration, complete as StorageConfigurationComplete } from "./StorageConfiguration"; -import { TotpConfiguration, complete as TotpConfigurationComplete } from "./TotpConfiguration"; - -export interface Configuration { - access_control?: ACLConfiguration; - authentication_backend: AuthenticationBackendConfiguration; - default_redirection_url?: string; - logs_level?: string; - notifier?: NotifierConfiguration; - port?: number; - regulation?: RegulationConfiguration; - session?: SessionConfiguration; - storage?: StorageConfiguration; - totp?: TotpConfiguration; -} - -export function complete( - configuration: Configuration): - [Configuration, string[]] { - - const newConfiguration: Configuration = JSON.parse( - JSON.stringify(configuration)); - const errors: string[] = []; - - const [acls, aclsErrors] = AclConfigurationComplete( - newConfiguration.access_control); - - newConfiguration.access_control = acls; - if (aclsErrors.length > 0) { - errors.concat(aclsErrors); - } - - const [backend, error] = - AuthenticationBackendComplete( - newConfiguration.authentication_backend); - - if (error) errors.push(error); - newConfiguration.authentication_backend = backend; - - if (!newConfiguration.logs_level) { - newConfiguration.logs_level = "info"; - } - - const [notifier, notifierError] = NotifierConfigurationComplete( - newConfiguration.notifier); - newConfiguration.notifier = notifier; - if (notifierError) errors.push(notifierError); - - if (!newConfiguration.port) { - newConfiguration.port = 8080; - } - - newConfiguration.regulation = RegulationConfigurationComplete( - newConfiguration.regulation); - newConfiguration.session = SessionConfigurationComplete( - newConfiguration.session); - newConfiguration.storage = StorageConfigurationComplete( - newConfiguration.storage); - newConfiguration.totp = TotpConfigurationComplete( - newConfiguration.totp); - - return [newConfiguration, errors]; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts b/themes/main/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts deleted file mode 100644 index d19002ba9..000000000 --- a/themes/main/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export interface FileUsersDatabaseConfiguration { - path: string; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/LdapConfiguration.spec.ts b/themes/main/server/src/lib/configuration/schema/LdapConfiguration.spec.ts deleted file mode 100644 index cc73d1085..000000000 --- a/themes/main/server/src/lib/configuration/schema/LdapConfiguration.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import Assert = require("assert"); -import { LdapConfiguration, complete } from "./LdapConfiguration"; - -describe("configuration/schema/AuthenticationMethodsConfiguration", function() { - it("should ensure at least one key is provided", function() { - const configuration: LdapConfiguration = { - url: "ldap.example.com", - base_dn: "dc=example,dc=com", - user: "admin", - password: "password" - }; - const newConfiguration = complete(configuration); - - Assert.deepEqual(newConfiguration, { - url: "ldap.example.com", - base_dn: "dc=example,dc=com", - user: "admin", - password: "password", - users_filter: "cn={0}", - group_name_attribute: "cn", - groups_filter: "member={dn}", - mail_attribute: "mail" - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/LdapConfiguration.ts b/themes/main/server/src/lib/configuration/schema/LdapConfiguration.ts deleted file mode 100644 index 5dacb9390..000000000 --- a/themes/main/server/src/lib/configuration/schema/LdapConfiguration.ts +++ /dev/null @@ -1,40 +0,0 @@ -import Util = require("util"); - -export interface LdapConfiguration { - url: string; - base_dn: string; - - additional_users_dn?: string; - users_filter?: string; - - additional_groups_dn?: string; - groups_filter?: string; - - group_name_attribute?: string; - mail_attribute?: string; - - user: string; // admin username - password: string; // admin password -} - -export function complete(configuration: LdapConfiguration): LdapConfiguration { - const newConfiguration: LdapConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.users_filter) { - newConfiguration.users_filter = "cn={0}"; - } - - if (!newConfiguration.groups_filter) { - newConfiguration.groups_filter = "member={dn}"; - } - - if (!newConfiguration.group_name_attribute) { - newConfiguration.group_name_attribute = "cn"; - } - - if (!newConfiguration.mail_attribute) { - newConfiguration.mail_attribute = "mail"; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts b/themes/main/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts deleted file mode 100644 index 6c576e8e0..000000000 --- a/themes/main/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import Assert = require("assert"); -import { NotifierConfiguration, complete } from "./NotifierConfiguration"; - -describe("configuration/schema/NotifierConfiguration", function() { - it("should use a default notifier when none is provided", function() { - const configuration: NotifierConfiguration = {}; - const [newConfiguration, error] = complete(configuration); - - Assert.deepEqual(newConfiguration.filesystem, {filename: "/tmp/authelia/notification.txt"}); - }); - - it("should ensure correct key is provided", function() { - const configuration = { - abc: "badvalue" - }; - const [newConfiguration, error] = complete(configuration as any); - - Assert.equal(error, "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"); - }); - - it("should ensure there is no more than one key", function() { - const configuration: NotifierConfiguration = { - smtp: { - host: "smtp.example.com", - port: 25, - secure: false, - sender: "test@example.com" - }, - email: { - username: "test", - password: "test", - sender: "test@example.com", - service: "gmail" - } - }; - const [newConfiguration, error] = complete(configuration); - - Assert.equal(error, "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/NotifierConfiguration.ts b/themes/main/server/src/lib/configuration/schema/NotifierConfiguration.ts deleted file mode 100644 index 7bcce15c2..000000000 --- a/themes/main/server/src/lib/configuration/schema/NotifierConfiguration.ts +++ /dev/null @@ -1,45 +0,0 @@ - -export interface EmailNotifierConfiguration { - username: string; - password: string; - sender: string; - service: string; -} - -export interface SmtpNotifierConfiguration { - username?: string; - password?: string; - host: string; - port: number; - secure: boolean; - sender: string; -} - -export interface FileSystemNotifierConfiguration { - filename: string; -} - -export interface NotifierConfiguration { - email?: EmailNotifierConfiguration; - smtp?: SmtpNotifierConfiguration; - filesystem?: FileSystemNotifierConfiguration; -} - -export function complete(configuration: NotifierConfiguration): [NotifierConfiguration, string] { - const newConfiguration: NotifierConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (Object.keys(newConfiguration).length == 0) - newConfiguration.filesystem = { filename: "/tmp/authelia/notification.txt" }; - - const ERROR = "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"; - - if (Object.keys(newConfiguration).length != 1) - return [newConfiguration, ERROR]; - - const key = Object.keys(newConfiguration)[0]; - - if (key != "filesystem" && key != "smtp" && key != "email") - return [newConfiguration, ERROR]; - - return [newConfiguration, undefined]; -} diff --git a/themes/main/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts b/themes/main/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts deleted file mode 100644 index dce2caf4e..000000000 --- a/themes/main/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Assert = require("assert"); -import { RegulationConfiguration, complete } from "./RegulationConfiguration"; - -describe("configuration/schema/RegulationConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: RegulationConfiguration = {}; - const newConfiguration = complete(configuration); - - Assert.equal(newConfiguration.ban_time, 300); - Assert.equal(newConfiguration.find_time, 120); - Assert.equal(newConfiguration.max_retries, 3); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/RegulationConfiguration.ts b/themes/main/server/src/lib/configuration/schema/RegulationConfiguration.ts deleted file mode 100644 index 117463f43..000000000 --- a/themes/main/server/src/lib/configuration/schema/RegulationConfiguration.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface RegulationConfiguration { - max_retries?: number; - find_time?: number; - ban_time?: number; -} - -export function complete(configuration: RegulationConfiguration): RegulationConfiguration { - const newConfiguration: RegulationConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.max_retries) { - newConfiguration.max_retries = 3; - } - - if (!newConfiguration.find_time) { - newConfiguration.find_time = 120; // seconds - } - - if (!newConfiguration.ban_time) { - newConfiguration.ban_time = 300; // seconds - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/SessionConfiguration.spec.ts b/themes/main/server/src/lib/configuration/schema/SessionConfiguration.spec.ts deleted file mode 100644 index e54010837..000000000 --- a/themes/main/server/src/lib/configuration/schema/SessionConfiguration.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Assert = require("assert"); -import { SessionConfiguration, complete } from "./SessionConfiguration"; - -describe("configuration/schema/SessionConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: SessionConfiguration = { - domain: "example.com", - secret: "unsecure_secret" - }; - const newConfiguration = complete(configuration); - - Assert.equal(newConfiguration.name, 'authelia_session'); - Assert.equal(newConfiguration.expiration, 3600000); - Assert.equal(newConfiguration.inactivity, undefined); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/SessionConfiguration.ts b/themes/main/server/src/lib/configuration/schema/SessionConfiguration.ts deleted file mode 100644 index 2c88bb215..000000000 --- a/themes/main/server/src/lib/configuration/schema/SessionConfiguration.ts +++ /dev/null @@ -1,32 +0,0 @@ -export interface SessionRedisOptions { - host: string; - port: number; - password?: string; -} - -export interface SessionConfiguration { - name?: string; - domain: string; - secret: string; - expiration?: number; - inactivity?: number; - redis?: SessionRedisOptions; -} - -export function complete(configuration: SessionConfiguration): SessionConfiguration { - const newConfiguration: SessionConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.name) { - newConfiguration.name = "authelia_session"; - } - - if (!newConfiguration.expiration) { - newConfiguration.expiration = 3600000; // 1 hour - } - - if (!newConfiguration.inactivity) { - newConfiguration.inactivity = undefined; // disabled - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/StorageConfiguration.spec.ts b/themes/main/server/src/lib/configuration/schema/StorageConfiguration.spec.ts deleted file mode 100644 index 9d02a11b6..000000000 --- a/themes/main/server/src/lib/configuration/schema/StorageConfiguration.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import Assert = require("assert"); -import { StorageConfiguration, complete } from "./StorageConfiguration"; - -describe("configuration/schema/StorageConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: StorageConfiguration = {}; - const newConfiguration = complete(configuration); - - Assert.deepEqual(newConfiguration, { - local: { - in_memory: true - } - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/StorageConfiguration.ts b/themes/main/server/src/lib/configuration/schema/StorageConfiguration.ts deleted file mode 100644 index 47e356ef4..000000000 --- a/themes/main/server/src/lib/configuration/schema/StorageConfiguration.ts +++ /dev/null @@ -1,30 +0,0 @@ -export interface MongoStorageConfiguration { - url: string; - database: string; - auth?: { - username: string; - password: string; - }; -} - -export interface LocalStorageConfiguration { - path?: string; - in_memory?: boolean; -} - -export interface StorageConfiguration { - local?: LocalStorageConfiguration; - mongo?: MongoStorageConfiguration; -} - -export function complete(configuration: StorageConfiguration): StorageConfiguration { - const newConfiguration: StorageConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.local && !newConfiguration.mongo) { - newConfiguration.local = { - in_memory: true - }; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/TotpConfiguration.ts b/themes/main/server/src/lib/configuration/schema/TotpConfiguration.ts deleted file mode 100644 index 683135639..000000000 --- a/themes/main/server/src/lib/configuration/schema/TotpConfiguration.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface TotpConfiguration { - issuer: string; -} - -export function complete(configuration: TotpConfiguration): TotpConfiguration { - const newConfiguration: TotpConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.issuer) { - newConfiguration.issuer = "authelia.com"; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts b/themes/main/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts deleted file mode 100644 index 8008b4833..000000000 --- a/themes/main/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts +++ /dev/null @@ -1,9 +0,0 @@ - -export interface UserInfo { - username: string; - password_hash: string; - email: string; - groups?: string[]; -} - -export type UserDatabaseConfiguration = UserInfo[]; \ No newline at end of file diff --git a/themes/main/server/src/lib/connectors/mongo/IMongoClient.d.ts b/themes/main/server/src/lib/connectors/mongo/IMongoClient.d.ts deleted file mode 100644 index 36cb4b8bf..000000000 --- a/themes/main/server/src/lib/connectors/mongo/IMongoClient.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import MongoDB = require("mongodb"); -import Bluebird = require("bluebird"); - -export interface IMongoClient { - collection(name: string): Bluebird -} \ No newline at end of file diff --git a/themes/main/server/src/lib/connectors/mongo/MongoClient.spec.ts b/themes/main/server/src/lib/connectors/mongo/MongoClient.spec.ts deleted file mode 100644 index ca0c68593..000000000 --- a/themes/main/server/src/lib/connectors/mongo/MongoClient.spec.ts +++ /dev/null @@ -1,119 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); -import MongoDB = require("mongodb"); -import Sinon = require("sinon"); - -import { MongoClient } from "./MongoClient"; -import { GlobalLoggerStub } from "../../logging/GlobalLoggerStub.spec"; -import { MongoStorageConfiguration } from "../../configuration/schema/StorageConfiguration"; - -describe("connectors/mongo/MongoClient", function () { - let MongoClientStub: any; - let mongoClientStub: any; - let mongoDatabaseStub: any; - let logger: GlobalLoggerStub = new GlobalLoggerStub(); - - const configuration: MongoStorageConfiguration = { - url: "mongo://url", - database: "databasename" - }; - - describe("connection", () => { - before(() => { - mongoClientStub = { - db: Sinon.stub() - }; - mongoDatabaseStub = { - on: Sinon.stub(), - collection: Sinon.stub() - } - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - undefined, mongoClientStub); - mongoClientStub.db.returns( - mongoDatabaseStub); - }); - - after(() => { - MongoClientStub.restore(); - }); - - it("should use credentials from configuration", () => { - configuration.auth = { - username: "authelia", - password: "authelia_pass" - }; - - const client = new MongoClient(configuration, logger); - return client.collection("test") - .then(() => { - Assert(MongoClientStub.calledWith("mongo://url", { - auth: { - user: "authelia", - password: "authelia_pass" - } - })) - }); - }); - }); - - describe("collection", () => { - before(function() { - mongoClientStub = { - db: Sinon.stub() - }; - mongoDatabaseStub = { - on: Sinon.stub(), - collection: Sinon.stub() - } - }); - - describe("Connection to mongo is ok", function() { - before(function () { - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - undefined, mongoClientStub); - mongoClientStub.db.returns( - mongoDatabaseStub); - }); - - after(function () { - MongoClientStub.restore(); - }); - - it("should create a collection", function () { - const COLLECTION_NAME = "mycollection"; - const client = new MongoClient(configuration, logger); - - mongoDatabaseStub.collection.returns("COL"); - return client.collection(COLLECTION_NAME) - .then((collection) => mongoDatabaseStub.collection.calledWith(COLLECTION_NAME)); - }); - }); - - describe("Connection to mongo is broken", function() { - before(function () { - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - new Error("Failed connection"), undefined); - }); - - after(function () { - MongoClientStub.restore(); - }); - - it("should fail creating the collection", function() { - const COLLECTION_NAME = "mycollection"; - const client = new MongoClient(configuration, logger); - - mongoDatabaseStub.collection.returns("COL"); - return client.collection(COLLECTION_NAME) - .then((collection) => Bluebird.reject(new Error("should not be here."))) - .catch((err) => Bluebird.resolve()); - }); - }) - }); -}); diff --git a/themes/main/server/src/lib/connectors/mongo/MongoClient.ts b/themes/main/server/src/lib/connectors/mongo/MongoClient.ts deleted file mode 100644 index d15731e97..000000000 --- a/themes/main/server/src/lib/connectors/mongo/MongoClient.ts +++ /dev/null @@ -1,76 +0,0 @@ - -import MongoDB = require("mongodb"); -import { IMongoClient } from "./IMongoClient"; -import Bluebird = require("bluebird"); -import { AUTHENTICATION_FAILED } from "../../../../../shared/UserMessages"; -import { IGlobalLogger } from "../../logging/IGlobalLogger"; -import { MongoStorageConfiguration } from "../../configuration/schema/StorageConfiguration"; - -export class MongoClient implements IMongoClient { - private configuration: MongoStorageConfiguration; - - private database: MongoDB.Db; - private client: MongoDB.MongoClient; - private logger: IGlobalLogger; - - constructor( - configuration: MongoStorageConfiguration, - logger: IGlobalLogger) { - - this.configuration = configuration; - this.logger = logger; - } - - connect(): Bluebird { - const that = this; - const options: MongoDB.MongoClientOptions = {}; - if (that.configuration.auth) { - options["auth"] = { - user: that.configuration.auth.username, - password: that.configuration.auth.password - }; - } - - return new Bluebird((resolve, reject) => { - MongoDB.MongoClient.connect( - this.configuration.url, - options, - function(err, client) { - if (err) { - reject(err); - return; - } - resolve(client); - }); - }) - .then(function (client: MongoDB.MongoClient) { - that.database = client.db(that.configuration.database); - that.database.on("close", () => { - that.logger.info("[MongoClient] Lost connection."); - }); - that.database.on("reconnect", () => { - that.logger.info("[MongoClient] Reconnected."); - }); - that.client = client; - }); - } - - close(): Bluebird { - if (this.client) { - this.client.close(); - this.database = undefined; - this.client = undefined; - } - return Bluebird.resolve(); - } - - collection(name: string): Bluebird { - if (!this.client) { - const that = this; - return this.connect() - .then(() => Bluebird.resolve(that.database.collection(name))); - } - - return Bluebird.resolve(this.database.collection(name)); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/connectors/mongo/MongoClientStub.spec.ts b/themes/main/server/src/lib/connectors/mongo/MongoClientStub.spec.ts deleted file mode 100644 index 1cfd48e32..000000000 --- a/themes/main/server/src/lib/connectors/mongo/MongoClientStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import MongoDB = require("mongodb"); -import Bluebird = require("bluebird"); -import { IMongoClient } from "../../../../src/lib/connectors/mongo/IMongoClient"; - -export class MongoClientStub implements IMongoClient { - public collectionStub: Sinon.SinonStub; - - constructor() { - this.collectionStub = Sinon.stub(); - } - - collection(name: string): Bluebird { - return this.collectionStub(name); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/logging/GlobalLogger.ts b/themes/main/server/src/lib/logging/GlobalLogger.ts deleted file mode 100644 index 4da7acf49..000000000 --- a/themes/main/server/src/lib/logging/GlobalLogger.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { IGlobalLogger } from "./IGlobalLogger"; -import Util = require("util"); -import Express = require("express"); -import Winston = require("winston"); - -declare module "express" { - interface Request { - id: string; - } -} - -export class GlobalLogger implements IGlobalLogger { - private winston: typeof Winston; - constructor(winston: typeof Winston) { - this.winston = winston; - } - - private buildMessage(message: string, ...args: any[]): string { - return Util.format("date='%s' message='%s'", new Date(), - Util.format(message, ...args)); - } - - info(message: string, ...args: any[]): void { - this.winston.info(this.buildMessage(message, ...args)); - } - - debug(message: string, ...args: any[]): void { - this.winston.debug(this.buildMessage(message, ...args)); - } - - error(message: string, ...args: any[]): void { - this.winston.debug(this.buildMessage(message, ...args)); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/logging/GlobalLoggerStub.spec.ts b/themes/main/server/src/lib/logging/GlobalLoggerStub.spec.ts deleted file mode 100644 index d4bb13710..000000000 --- a/themes/main/server/src/lib/logging/GlobalLoggerStub.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import Sinon = require("sinon"); -import { GlobalLogger } from "./GlobalLogger"; -import Winston = require("winston"); -import Express = require("express"); -import { IGlobalLogger } from "./IGlobalLogger"; - -export class GlobalLoggerStub implements IGlobalLogger { - infoStub: Sinon.SinonStub; - debugStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - private globalLogger: IGlobalLogger; - - constructor(enableLogging?: boolean) { - this.infoStub = Sinon.stub(); - this.debugStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - if (enableLogging) - this.globalLogger = new GlobalLogger(Winston); - } - - info(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.infoStub(message, ...args); - } - - debug(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.debugStub(message, ...args); - } - - error(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.errorStub(message, ...args); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/logging/IGlobalLogger.ts b/themes/main/server/src/lib/logging/IGlobalLogger.ts deleted file mode 100644 index 548515ec7..000000000 --- a/themes/main/server/src/lib/logging/IGlobalLogger.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface IGlobalLogger { - info(message: string, ...args: any[]): void; - debug(message: string, ...args: any[]): void; - error(message: string, ...args: any[]): void; -} diff --git a/themes/main/server/src/lib/logging/IRequestLogger.ts b/themes/main/server/src/lib/logging/IRequestLogger.ts deleted file mode 100644 index 126a601fe..000000000 --- a/themes/main/server/src/lib/logging/IRequestLogger.ts +++ /dev/null @@ -1,7 +0,0 @@ -import Express = require("express"); - -export interface IRequestLogger { - info(req: Express.Request, message: string, ...args: any[]): void; - debug(req: Express.Request, message: string, ...args: any[]): void; - error(req: Express.Request, message: string, ...args: any[]): void; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/logging/RequestLogger.ts b/themes/main/server/src/lib/logging/RequestLogger.ts deleted file mode 100644 index c45c66018..000000000 --- a/themes/main/server/src/lib/logging/RequestLogger.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { IRequestLogger } from "./IRequestLogger"; -import Util = require("util"); -import Express = require("express"); -import Winston = require("winston"); - -declare module "express" { - interface Request { - id: string; - } -} - -export class RequestLogger implements IRequestLogger { - private winston: typeof Winston; - - constructor(winston: typeof Winston) { - this.winston = winston; - } - - private formatHeader(req: Express.Request) { - const clientIP = req.ip; // The IP of the original client going through the proxy chain. - return Util.format("date='%s' method='%s', path='%s' requestId='%s' sessionId='%s' ip='%s'", - new Date(), req.method, req.path, req.id, req.sessionID, clientIP); - } - - private formatBody(message: string) { - return Util.format("message='%s'", message); - } - - private formatMessage(req: Express.Request, message: string) { - return Util.format("%s %s", this.formatHeader(req), - this.formatBody(message)); - } - - info(req: Express.Request, message: string, ...args: any[]): void { - this.winston.info(this.formatMessage(req, message), ...args); - } - - debug(req: Express.Request, message: string, ...args: any[]): void { - this.winston.debug(this.formatMessage(req, message), ...args); - } - - error(req: Express.Request, message: string, ...args: any[]): void { - this.winston.error(this.formatMessage(req, message), ...args); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/logging/RequestLoggerStub.spec.ts b/themes/main/server/src/lib/logging/RequestLoggerStub.spec.ts deleted file mode 100644 index b0e375210..000000000 --- a/themes/main/server/src/lib/logging/RequestLoggerStub.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { IRequestLogger } from "./IRequestLogger"; -import Sinon = require("sinon"); -import { RequestLogger } from "./RequestLogger"; -import Winston = require("winston"); -import Express = require("express"); - -export class RequestLoggerStub implements IRequestLogger { - infoStub: Sinon.SinonStub; - debugStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - private requestLogger: RequestLogger; - - constructor(enableLogging?: boolean) { - this.infoStub = Sinon.stub(); - this.debugStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - if (enableLogging) - this.requestLogger = new RequestLogger(Winston); - } - - info(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.infoStub(req, message, ...args); - } - - debug(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.debugStub(req, message, ...args); - } - - error(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.errorStub(req, message, ...args); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/notifiers/AbstractEmailNotifier.ts b/themes/main/server/src/lib/notifiers/AbstractEmailNotifier.ts deleted file mode 100644 index 198e4e5db..000000000 --- a/themes/main/server/src/lib/notifiers/AbstractEmailNotifier.ts +++ /dev/null @@ -1,23 +0,0 @@ - -import { INotifier } from "../notifiers/INotifier"; -import { Identity } from "../../../types/Identity"; - -import Fs = require("fs"); -import Path = require("path"); -import Ejs = require("ejs"); -import BluebirdPromise = require("bluebird"); - -const email_template = Fs.readFileSync(Path.join(__dirname, "../../resources/email-template.ejs"), "UTF-8"); - -export abstract class AbstractEmailNotifier implements INotifier { - notify(to: string, subject: string, link: string): BluebirdPromise { - const d = { - url: link, - button_title: "Continue", - title: subject - }; - return this.sendEmail(to, subject, Ejs.render(email_template, d)); - } - - abstract sendEmail(to: string, subject: string, content: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/notifiers/EmailNotifier.spec.ts b/themes/main/server/src/lib/notifiers/EmailNotifier.spec.ts deleted file mode 100644 index 8211bbc02..000000000 --- a/themes/main/server/src/lib/notifiers/EmailNotifier.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as sinon from "sinon"; -import * as Assert from "assert"; -import BluebirdPromise = require("bluebird"); - -import { MailSenderStub } from "./MailSenderStub.spec"; -import EmailNotifier = require("./EmailNotifier"); - - -describe("notifiers/EmailNotifier", function () { - it("should send an email to given user", function () { - const mailSender = new MailSenderStub(); - const options = { - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }; - - mailSender.sendStub.returns(BluebirdPromise.resolve()); - const sender = new EmailNotifier.EmailNotifier(options, mailSender); - const subject = "subject"; - const url = "http://test.com"; - - return sender.notify("user@example.com", subject, url) - .then(function () { - Assert.equal(mailSender.sendStub.getCall(0).args[0].to, "user@example.com"); - Assert.equal(mailSender.sendStub.getCall(0).args[0].subject, "subject"); - return BluebirdPromise.resolve(); - }); - }); - - it("should fail while sending an email", function () { - const mailSender = new MailSenderStub(); - const options = { - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }; - - mailSender.sendStub.returns(BluebirdPromise.reject(new Error("Failed to send mail"))); - const sender = new EmailNotifier.EmailNotifier(options, mailSender); - const subject = "subject"; - const url = "http://test.com"; - - return sender.notify("user@example.com", subject, url) - .then(function () { - return BluebirdPromise.reject(new Error()); - }, function() { - Assert.equal(mailSender.sendStub.getCall(0).args[0].from, "admin@example.com"); - return BluebirdPromise.resolve(); - }); - }); -}); diff --git a/themes/main/server/src/lib/notifiers/EmailNotifier.ts b/themes/main/server/src/lib/notifiers/EmailNotifier.ts deleted file mode 100644 index 4df7c861e..000000000 --- a/themes/main/server/src/lib/notifiers/EmailNotifier.ts +++ /dev/null @@ -1,27 +0,0 @@ - -import * as BluebirdPromise from "bluebird"; - -import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier"; -import { EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; -import { IMailSender } from "./IMailSender"; - -export class EmailNotifier extends AbstractEmailNotifier { - private mailSender: IMailSender; - private sender: string; - - constructor(options: EmailNotifierConfiguration, mailSender: IMailSender) { - super(); - this.mailSender = mailSender; - this.sender = options.sender; - } - - sendEmail(to: string, subject: string, content: string) { - const mailOptions = { - from: this.sender, - to: to, - subject: subject, - html: content - }; - return this.mailSender.send(mailOptions); - } -} diff --git a/themes/main/server/src/lib/notifiers/FileSystemNotifier.ts b/themes/main/server/src/lib/notifiers/FileSystemNotifier.ts deleted file mode 100644 index 23f6242c4..000000000 --- a/themes/main/server/src/lib/notifiers/FileSystemNotifier.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as BluebirdPromise from "bluebird"; -import * as util from "util"; -import * as Fs from "fs"; -import { INotifier } from "./INotifier"; -import { Identity } from "../../../types/Identity"; - -import { FileSystemNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class FileSystemNotifier implements INotifier { - private filename: string; - - constructor(options: FileSystemNotifierConfiguration) { - this.filename = options.filename; - } - - notify(to: string, subject: string, link: string): BluebirdPromise { - const content = util.format("Date: %s\nEmail: %s\nSubject: %s\nLink: %s", - new Date().toString(), to, subject, link); - const writeFilePromised: any = BluebirdPromise.promisify(Fs.writeFile); - return writeFilePromised(this.filename, content); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/notifiers/IMailSender.ts b/themes/main/server/src/lib/notifiers/IMailSender.ts deleted file mode 100644 index 34ac464a8..000000000 --- a/themes/main/server/src/lib/notifiers/IMailSender.ts +++ /dev/null @@ -1,6 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); - -export interface IMailSender { - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/notifiers/IMailSenderBuilder.ts b/themes/main/server/src/lib/notifiers/IMailSenderBuilder.ts deleted file mode 100644 index 36d4dcdf7..000000000 --- a/themes/main/server/src/lib/notifiers/IMailSenderBuilder.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export interface IMailSenderBuilder { - buildEmail(options: EmailNotifierConfiguration): IMailSender; - buildSmtp(options: SmtpNotifierConfiguration): IMailSender; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/notifiers/INotifier.ts b/themes/main/server/src/lib/notifiers/INotifier.ts deleted file mode 100644 index b9a6b138c..000000000 --- a/themes/main/server/src/lib/notifiers/INotifier.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as BluebirdPromise from "bluebird"; - -export interface INotifier { - notify(to: string, subject: string, link: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/notifiers/MailSender.ts b/themes/main/server/src/lib/notifiers/MailSender.ts deleted file mode 100644 index 536a88e63..000000000 --- a/themes/main/server/src/lib/notifiers/MailSender.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import Nodemailer = require("nodemailer"); -import NodemailerDirectTransport = require("nodemailer-direct-transport"); -import NodemailerSmtpTransport = require("nodemailer-smtp-transport"); -import BluebirdPromise = require("bluebird"); - -export class MailSender implements IMailSender { - private transporter: Nodemailer.Transporter; - - constructor(options: NodemailerDirectTransport.DirectOptions | - NodemailerSmtpTransport.SmtpOptions, nodemailer: typeof Nodemailer) { - this.transporter = nodemailer.createTransport(options); - } - - verify(): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - that.transporter.verify(function (error: Error, success: any) { - if (error) { - reject(new Error("Unable to connect to SMTP server. \ - Please check the service is running and your credentials are correct.")); - return; - } - resolve(); - }); - }); - } - - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - that.transporter.sendMail(mailOptions, (error: Error, - data: Nodemailer.SentMessageInfo) => { - if (error) { - reject(new Error("Error while sending email: " + error.message)); - return; - } - resolve(); - }); - }); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/notifiers/MailSenderBuilder.spec.ts b/themes/main/server/src/lib/notifiers/MailSenderBuilder.spec.ts deleted file mode 100644 index 41e0db426..000000000 --- a/themes/main/server/src/lib/notifiers/MailSenderBuilder.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ - -import { MailSenderBuilder } from ".//MailSenderBuilder"; -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); -import Assert = require("assert"); - -describe("notifiers/MailSenderBuilder", function() { - let createTransportStub: Sinon.SinonStub; - beforeEach(function() { - createTransportStub = Sinon.stub(Nodemailer, "createTransport"); - }); - - afterEach(function() { - createTransportStub.restore(); - }); - - it("should create a email mail sender", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildEmail({ - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }); - Assert.equal(createTransportStub.getCall(0).args[0].auth.user, "user_gmail"); - Assert.equal(createTransportStub.getCall(0).args[0].auth.pass, "pass_gmail"); - Assert.equal(createTransportStub.getCall(0).args[0].service, "gmail"); - }); - - describe("build smtp mail sender", function() { - it("should create a smtp mail sender with authenticated user", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildSmtp({ - host: "mail.example.com", - password: "password", - port: 25, - secure: true, - username: "user", - sender: "admin@example.com" - }); - Assert.deepStrictEqual(createTransportStub.getCall(0).args[0], { - host: "mail.example.com", - auth: { - pass: "password", - user: "user" - }, - port: 25, - secure: true, - }); - }); - - it("should create a smtp mail sender with anonymous user", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildSmtp({ - host: "mail.example.com", - port: 25, - secure: true, - sender: "admin@example.com" - }); - Assert.deepStrictEqual(createTransportStub.getCall(0).args[0], { - host: "mail.example.com", - port: 25, - secure: true, - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/notifiers/MailSenderBuilder.ts b/themes/main/server/src/lib/notifiers/MailSenderBuilder.ts deleted file mode 100644 index 1d06be52f..000000000 --- a/themes/main/server/src/lib/notifiers/MailSenderBuilder.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import { IMailSenderBuilder } from "./IMailSenderBuilder"; -import { MailSender } from "./MailSender"; -import Nodemailer = require("nodemailer"); -import NodemailerSmtpTransport = require("nodemailer-smtp-transport"); -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class MailSenderBuilder implements IMailSenderBuilder { - private nodemailer: typeof Nodemailer; - - constructor(nodemailer: typeof Nodemailer) { - this.nodemailer = nodemailer; - } - - buildEmail(options: EmailNotifierConfiguration): IMailSender { - const emailOptions = { - service: options.service, - auth: { - user: options.username, - pass: options.password - } - }; - return new MailSender(emailOptions, this.nodemailer); - } - - buildSmtp(options: SmtpNotifierConfiguration): IMailSender { - const smtpOptions: NodemailerSmtpTransport.SmtpOptions = { - host: options.host, - port: options.port, - secure: options.secure, // upgrade later with STARTTLS - }; - - if (options.username && options.password) { - smtpOptions.auth = { - user: options.username, - pass: options.password - }; - } - - return new MailSender(smtpOptions, this.nodemailer); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts b/themes/main/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts deleted file mode 100644 index 5b76f6e56..000000000 --- a/themes/main/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { IMailSenderBuilder } from "../../../src/lib/notifiers/IMailSenderBuilder"; -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); -import { IMailSender } from "../../../src/lib/notifiers/IMailSender"; -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../../../src/lib/configuration/schema/NotifierConfiguration"; - -export class MailSenderBuilderStub implements IMailSenderBuilder { - buildEmailStub: Sinon.SinonStub; - buildSmtpStub: Sinon.SinonStub; - - constructor() { - this.buildEmailStub = Sinon.stub(); - this.buildSmtpStub = Sinon.stub(); - } - - buildEmail(options: EmailNotifierConfiguration): IMailSender { - return this.buildEmailStub(options); - } - - buildSmtp(options: SmtpNotifierConfiguration): IMailSender { - return this.buildSmtpStub(options); - } - -} \ No newline at end of file diff --git a/themes/main/server/src/lib/notifiers/MailSenderStub.spec.ts b/themes/main/server/src/lib/notifiers/MailSenderStub.spec.ts deleted file mode 100644 index d57c458fc..000000000 --- a/themes/main/server/src/lib/notifiers/MailSenderStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { IMailSender } from "../../../src/lib/notifiers/IMailSender"; -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); - -export class MailSenderStub implements IMailSender { - sendStub: Sinon.SinonStub; - - constructor() { - this.sendStub = Sinon.stub(); - } - - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise { - return this.sendStub(mailOptions); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/notifiers/NotifierFactory.spec.ts b/themes/main/server/src/lib/notifiers/NotifierFactory.spec.ts deleted file mode 100644 index f15e7667f..000000000 --- a/themes/main/server/src/lib/notifiers/NotifierFactory.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ - -import * as sinon from "sinon"; -import * as BluebirdPromise from "bluebird"; -import * as assert from "assert"; - -import { NotifierFactory } from "./NotifierFactory"; -import { EmailNotifier } from "./EmailNotifier"; -import { SmtpNotifier } from "./SmtpNotifier"; -import { MailSenderBuilderStub } from "./MailSenderBuilderStub.spec"; - - -describe("notifiers/NotifierFactory", function () { - let mailSenderBuilderStub: MailSenderBuilderStub; - it("should build a Email Notifier", function () { - const options = { - email: { - username: "abc", - password: "password", - sender: "admin@example.com", - service: "gmail" - } - }; - mailSenderBuilderStub = new MailSenderBuilderStub(); - assert(NotifierFactory.build(options, mailSenderBuilderStub) instanceof EmailNotifier); - }); - - it("should build a SMTP Notifier", function () { - const options = { - smtp: { - username: "user", - password: "pass", - secure: true, - host: "localhost", - port: 25, - sender: "admin@example.com" - } - }; - - mailSenderBuilderStub = new MailSenderBuilderStub(); - assert(NotifierFactory.build(options, mailSenderBuilderStub) instanceof SmtpNotifier); - }); -}); diff --git a/themes/main/server/src/lib/notifiers/NotifierFactory.ts b/themes/main/server/src/lib/notifiers/NotifierFactory.ts deleted file mode 100644 index a89155feb..000000000 --- a/themes/main/server/src/lib/notifiers/NotifierFactory.ts +++ /dev/null @@ -1,33 +0,0 @@ - -import { NotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; -import Nodemailer = require("nodemailer"); -import { INotifier } from "./INotifier"; - -import { FileSystemNotifier } from "./FileSystemNotifier"; -import { EmailNotifier } from "./EmailNotifier"; -import { SmtpNotifier } from "./SmtpNotifier"; -import { IMailSender } from "./IMailSender"; -import { IMailSenderBuilder } from "./IMailSenderBuilder"; - -export class NotifierFactory { - static build(options: NotifierConfiguration, mailSenderBuilder: IMailSenderBuilder): INotifier { - if ("email" in options) { - const mailSender = mailSenderBuilder.buildEmail(options.email); - return new EmailNotifier(options.email, mailSender); - } - else if ("smtp" in options) { - const mailSender = mailSenderBuilder.buildSmtp(options.smtp); - return new SmtpNotifier(options.smtp, mailSender); - } - else if ("filesystem" in options) { - return new FileSystemNotifier(options.filesystem); - } - else { - throw new Error("No available notifier option detected."); - } - } -} - - - - diff --git a/themes/main/server/src/lib/notifiers/NotifierStub.spec.ts b/themes/main/server/src/lib/notifiers/NotifierStub.spec.ts deleted file mode 100644 index f99231b50..000000000 --- a/themes/main/server/src/lib/notifiers/NotifierStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); - -import { INotifier } from "./INotifier"; - -export class NotifierStub implements INotifier { - notifyStub: Sinon.SinonStub; - - constructor() { - this.notifyStub = Sinon.stub(); - } - - notify(to: string, subject: string, link: string): BluebirdPromise { - return this.notifyStub(to, subject, link); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/notifiers/SmtpNotifier.ts b/themes/main/server/src/lib/notifiers/SmtpNotifier.ts deleted file mode 100644 index f93a6d4a0..000000000 --- a/themes/main/server/src/lib/notifiers/SmtpNotifier.ts +++ /dev/null @@ -1,30 +0,0 @@ - - -import * as BluebirdPromise from "bluebird"; - -import { IMailSender } from "./IMailSender"; -import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier"; -import { SmtpNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class SmtpNotifier extends AbstractEmailNotifier { - private mailSender: IMailSender; - private sender: string; - - constructor(options: SmtpNotifierConfiguration, - mailSender: IMailSender) { - super(); - this.mailSender = mailSender; - this.sender = options.sender; - } - - sendEmail(to: string, subject: string, content: string) { - const mailOptions = { - from: this.sender, - to: to, - subject: subject, - html: content - }; - const that = this; - return this.mailSender.send(mailOptions); - } -} diff --git a/themes/main/server/src/lib/regulation/IRegulator.ts b/themes/main/server/src/lib/regulation/IRegulator.ts deleted file mode 100644 index c49425b24..000000000 --- a/themes/main/server/src/lib/regulation/IRegulator.ts +++ /dev/null @@ -1,6 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -export interface IRegulator { - mark(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise; - regulate(userId: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/regulation/Regulator.spec.ts b/themes/main/server/src/lib/regulation/Regulator.spec.ts deleted file mode 100644 index f9c6e6086..000000000 --- a/themes/main/server/src/lib/regulation/Regulator.spec.ts +++ /dev/null @@ -1,186 +0,0 @@ - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -import { Regulator } from "./Regulator"; -import MockDate = require("mockdate"); -import exceptions = require("../Exceptions"); -import { UserDataStoreStub } from "../storage/UserDataStoreStub.spec"; - -describe("regulation/Regulator", function () { - const USER1 = "USER1"; - const USER2 = "USER2"; - let userDataStoreStub: UserDataStoreStub; - - beforeEach(function () { - userDataStoreStub = new UserDataStoreStub(); - const dataStore: { [userId: string]: { userId: string, date: Date, isAuthenticationSuccessful: boolean }[] } = { - [USER1]: [], - [USER2]: [] - }; - - userDataStoreStub.saveAuthenticationTraceStub.callsFake(function (userId, isAuthenticationSuccessful) { - dataStore[userId].unshift({ - userId: userId, - date: new Date(), - isAuthenticationSuccessful: isAuthenticationSuccessful, - }); - return BluebirdPromise.resolve(); - }); - - userDataStoreStub.retrieveLatestAuthenticationTracesStub.callsFake(function (userId, count) { - const ret = (dataStore[userId].length <= count) ? dataStore[userId] : dataStore[userId].slice(0, 3); - return BluebirdPromise.resolve(ret); - }); - }); - - afterEach(function () { - MockDate.reset(); - }); - - function markAuthenticationAt(regulator: Regulator, user: string, time: string, success: boolean) { - MockDate.set(time); - return regulator.mark(user, success); - } - - it("should mark 2 authentication and regulate (accept)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 10, 10); - - return regulator.mark(USER1, false) - .then(function () { - return regulator.mark(USER1, true); - }) - .then(function () { - return regulator.regulate(USER1); - }); - }); - - it("should mark 3 authentications and regulate (reject)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 10, 10); - - return regulator.mark(USER1, false) - .then(function () { - return regulator.mark(USER1, false); - }) - .then(function () { - return regulator.mark(USER1, false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { return BluebirdPromise.reject(new Error("should not be here!")); }) - .catch(exceptions.AuthenticationRegulationError, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should mark 1 failed, 1 successful and 1 failed authentications within minimum time and regulate (accept)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 60, 30); - - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:10", true); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:20", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:30", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:39", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here!")); - }, - function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should regulate user if number of failures is greater than 3 in allowed time lapse", function () { - function markAuthentications(regulator: Regulator, user: string) { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:45", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:01:05", false); - }) - .then(function () { - return regulator.regulate(user); - }); - } - - const regulator1 = new Regulator(userDataStoreStub, 3, 60, 60); - const regulator2 = new Regulator(userDataStoreStub, 3, 2 * 60, 60); - - const p1 = markAuthentications(regulator1, USER1); - const p2 = markAuthentications(regulator2, USER2); - - return BluebirdPromise.join(p1, p2) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here...")); - }, function () { - Assert(p1.isFulfilled()); - Assert(p2.isRejected()); - }); - }); - - it("should user wait after regulation to authenticate again", function () { - function markAuthentications(regulator: Regulator, user: string) { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:10", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:15", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:25", false); - }) - .then(function () { - MockDate.set("1/2/2000 00:00:54"); - return regulator.regulate(user); - }) - .then(function () { - return BluebirdPromise.reject(new Error("should fail at this time")); - }, function () { - MockDate.set("1/2/2000 00:00:56"); - return regulator.regulate(user); - }); - } - - const regulator = new Regulator(userDataStoreStub, 4, 30, 30); - return markAuthentications(regulator, USER1); - }); - - it("should disable regulation when max_retries is set to 0", function () { - const maxRetries = 0; - const regulator = new Regulator(userDataStoreStub, maxRetries, 60, 30); - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:10", false); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:15", false); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:25", false); - }) - .then(function () { - MockDate.set("1/2/2000 00:00:26"); - return regulator.regulate(USER1); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/regulation/Regulator.ts b/themes/main/server/src/lib/regulation/Regulator.ts deleted file mode 100644 index 1037a6a17..000000000 --- a/themes/main/server/src/lib/regulation/Regulator.ts +++ /dev/null @@ -1,55 +0,0 @@ - -import * as BluebirdPromise from "bluebird"; -import exceptions = require("../Exceptions"); -import { IUserDataStore } from "../storage/IUserDataStore"; -import { AuthenticationTraceDocument } from "../storage/AuthenticationTraceDocument"; -import { IRegulator } from "./IRegulator"; - -export class Regulator implements IRegulator { - private userDataStore: IUserDataStore; - private banTime: number; - private findTime: number; - private maxRetries: number; - - constructor(userDataStore: any, maxRetries: number, findTime: number, banTime: number) { - this.userDataStore = userDataStore; - this.banTime = banTime; - this.findTime = findTime; - this.maxRetries = maxRetries; - } - - // Mark authentication - mark(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - return this.userDataStore.saveAuthenticationTrace(userId, isAuthenticationSuccessful); - } - - regulate(userId: string): BluebirdPromise { - const that = this; - - if (that.maxRetries <= 0) return BluebirdPromise.resolve(); - - return this.userDataStore.retrieveLatestAuthenticationTraces(userId, that.maxRetries) - .then((docs: AuthenticationTraceDocument[]) => { - // less than the max authorized number of authentication in time range, thus authorizing access - if (docs.length < that.maxRetries) return BluebirdPromise.resolve(); - - const numberOfFailedAuth = docs - .map(function (d: AuthenticationTraceDocument) { return d.isAuthenticationSuccessful == false ? 1 : 0; }) - .reduce(function (acc, v) { return acc + v; }, 0); - - if (numberOfFailedAuth < this.maxRetries) return BluebirdPromise.resolve(); - - const newestDocument = docs[0]; - const oldestDocument = docs[that.maxRetries - 1]; - - const authenticationsTimeRangeInSeconds = (newestDocument.date.getTime() - oldestDocument.date.getTime()) / 1000; - const tooManyAuthInTimelapse = (authenticationsTimeRangeInSeconds < this.findTime); - const stillInBannedTimeRange = (new Date(new Date().getTime() - this.banTime * 1000) < newestDocument.date); - - if (tooManyAuthInTimelapse && stillInBannedTimeRange) - throw new exceptions.AuthenticationRegulationError("Max number of authentication. Please retry in few minutes."); - - return BluebirdPromise.resolve(); - }); - } -} diff --git a/themes/main/server/src/lib/regulation/RegulatorStub.spec.ts b/themes/main/server/src/lib/regulation/RegulatorStub.spec.ts deleted file mode 100644 index ca8a00fb1..000000000 --- a/themes/main/server/src/lib/regulation/RegulatorStub.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); -import { IRegulator } from "./IRegulator"; - - -export class RegulatorStub implements IRegulator { - markStub: Sinon.SinonStub; - regulateStub: Sinon.SinonStub; - - constructor() { - this.markStub = Sinon.stub(); - this.regulateStub = Sinon.stub(); - } - - mark(userId: string, isAuthenticationSuccessful: boolean): Bluebird { - return this.markStub(userId, isAuthenticationSuccessful); - } - - regulate(userId: string): Bluebird { - return this.regulateStub(userId); - } -} diff --git a/themes/main/server/src/lib/routes/error/401/get.spec.ts b/themes/main/server/src/lib/routes/error/401/get.spec.ts deleted file mode 100644 index 9fdac9c3c..000000000 --- a/themes/main/server/src/lib/routes/error/401/get.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get401 from "./get"; -import { ServerVariables } from "../../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../../ServerVariablesMockBuilder.spec"; - -describe("routes/error/401/get", function () { - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let req: any; - let res: any; - let renderSpy: Sinon.SinonSpy; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - - renderSpy = Sinon.spy(); - req = { - headers: {} - }; - res = { - render: renderSpy - }; - }); - - it("should set redirection url to the default redirection url", function () { - vars.config.default_redirection_url = "http://default-redirection"; - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: "http://default-redirection" - })); - }); - }); - - it("should set redirection url to the referer", function () { - req.headers["referer"] = "http://redirection"; - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: "http://redirection" - })); - }); - }); - - it("should render without redirecting the user", function () { - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: undefined - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/error/401/get.ts b/themes/main/server/src/lib/routes/error/401/get.ts deleted file mode 100644 index ca4a3963d..000000000 --- a/themes/main/server/src/lib/routes/error/401/get.ts +++ /dev/null @@ -1,15 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import redirector from "../redirector"; -import { ServerVariables } from "../../../ServerVariables"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - const redirectionUrl = redirector(req, vars); - res.render("errors/401", { - redirection_url: redirectionUrl - }); - return BluebirdPromise.resolve(); - }; -} diff --git a/themes/main/server/src/lib/routes/error/403/get.spec.ts b/themes/main/server/src/lib/routes/error/403/get.spec.ts deleted file mode 100644 index 22eb84853..000000000 --- a/themes/main/server/src/lib/routes/error/403/get.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get403 from "./get"; -import { ServerVariables } from "../../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../../ServerVariablesMockBuilder.spec"; - -describe("routes/error/403/get", function () { - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let req: any; - let res: any; - let renderSpy: Sinon.SinonSpy; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - - renderSpy = Sinon.spy(); - req = { - headers: {} - }; - res = { - render: renderSpy - }; - }); - - it("should set redirection url to the default redirection url", function () { - vars.config.default_redirection_url = "http://default-redirection"; - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: "http://default-redirection" - })); - }); - }); - - it("should set redirection url to the referer", function () { - req.headers["referer"] = "http://redirection"; - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: "http://redirection" - })); - }); - }); - - it("should render without redirecting the user", function () { - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: undefined - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/error/403/get.ts b/themes/main/server/src/lib/routes/error/403/get.ts deleted file mode 100644 index 3ab0319e5..000000000 --- a/themes/main/server/src/lib/routes/error/403/get.ts +++ /dev/null @@ -1,15 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import redirector from "../redirector"; -import { ServerVariables } from "../../../ServerVariables"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - const redirectionUrl = redirector(req, vars); - res.render("errors/403", { - redirection_url: redirectionUrl - }); - return BluebirdPromise.resolve(); - }; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/error/404/get.spec.ts b/themes/main/server/src/lib/routes/error/404/get.spec.ts deleted file mode 100644 index 73e4e6cef..000000000 --- a/themes/main/server/src/lib/routes/error/404/get.spec.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get404 from "./get"; - -describe("routes/error/404/get", function () { - it("should render the page", function () { - const req = {} as Express.Request; - const res = { - render: Sinon.stub() - }; - - return Get404(req, res as any) - .then(function () { - Assert(res.render.calledOnce); - Assert(res.render.calledWith("errors/404")); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/error/404/get.ts b/themes/main/server/src/lib/routes/error/404/get.ts deleted file mode 100644 index 6693b6fc8..000000000 --- a/themes/main/server/src/lib/routes/error/404/get.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); - -export default function (req: express.Request, res: express.Response): BluebirdPromise { - res.render("errors/404"); - return BluebirdPromise.resolve(); -} \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/error/redirector.ts b/themes/main/server/src/lib/routes/error/redirector.ts deleted file mode 100644 index b1a3ccc11..000000000 --- a/themes/main/server/src/lib/routes/error/redirector.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Express = require("express"); -import { ServerVariables } from "../../ServerVariables"; - -export default function (req: Express.Request, vars: ServerVariables): string { - let redirectionUrl: string; - - if (req.headers && req.headers["referer"]) - redirectionUrl = "" + req.headers["referer"]; - else if (vars.config.default_redirection_url) - redirectionUrl = vars.config.default_redirection_url; - - return redirectionUrl; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/firstfactor/get.ts b/themes/main/server/src/lib/routes/firstfactor/get.ts deleted file mode 100644 index d94f656c8..000000000 --- a/themes/main/server/src/lib/routes/firstfactor/get.ts +++ /dev/null @@ -1,72 +0,0 @@ - -import express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import Util = require("util"); -import { ServerVariables } from "../../ServerVariables"; -import { SafeRedirector } from "../../utils/SafeRedirection"; -import { Level } from "../../authentication/Level"; - -function getRedirectParam( - req: express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -function redirectToSecondFactorPage( - req: express.Request, - res: express.Response) { - - const redirectUrl = getRedirectParam(req); - if (!redirectUrl) - res.redirect(Endpoints.SECOND_FACTOR_GET); - else - res.redirect( - Util.format("%s?%s=%s", - Endpoints.SECOND_FACTOR_GET, - Constants.REDIRECT_QUERY_PARAM, - redirectUrl)); -} - -function redirectToService( - req: express.Request, - res: express.Response, - redirector: SafeRedirector) { - const redirectUrl = getRedirectParam(req); - if (!redirectUrl) { - res.redirect(Endpoints.LOGGED_IN); - } else { - redirector.redirectOrElse(res, redirectUrl, Endpoints.LOGGED_IN); - } -} - -function renderFirstFactor( - res: express.Response) { - - res.render("firstfactor", { - first_factor_post_endpoint: Endpoints.FIRST_FACTOR_POST, - reset_password_request_endpoint: Endpoints.RESET_PASSWORD_REQUEST_GET - }); -} - -export default function ( - vars: ServerVariables) { - - const redirector = new SafeRedirector(vars.config.session.domain); - return function (req: express.Request, res: express.Response): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (authSession.authentication_level == Level.ONE_FACTOR) { - redirectToSecondFactorPage(req, res); - } else if (authSession.authentication_level == Level.TWO_FACTOR) { - redirectToService(req, res, redirector); - } else { - renderFirstFactor(res); - } - resolve(); - }); - }; -} diff --git a/themes/main/server/src/lib/routes/firstfactor/post.spec.ts b/themes/main/server/src/lib/routes/firstfactor/post.spec.ts deleted file mode 100644 index e1d078cdd..000000000 --- a/themes/main/server/src/lib/routes/firstfactor/post.spec.ts +++ /dev/null @@ -1,136 +0,0 @@ - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import FirstFactorPost = require("./post"); -import exceptions = require("../../Exceptions"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import Endpoints = require("../../../../../shared/api"); -import AuthenticationRegulatorMock = require("../../regulation/RegulatorStub.spec"); -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; - -describe("routes/firstfactor/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let emails: string[]; - let groups: string[]; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let authSession: AuthenticationSession; - - beforeEach(function () { - emails = ["test_ok@example.com"]; - groups = ["group1", "group2" ]; - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - mocks.authorizer.authorizationMock.returns(true); - mocks.regulator.regulateStub.returns(BluebirdPromise.resolve()); - mocks.regulator.markStub.returns(BluebirdPromise.resolve()); - - req = { - originalUrl: "/api/firstfactor", - body: { - username: "username", - password: "password" - }, - query: { - redirect: "http://redirect.url" - }, - session: { - cookie: {} - }, - headers: { - host: "home.example.com" - } - }; - - res = ExpressMock.ResponseMock(); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - it("should reply with 204 if success", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal("username", authSession.userid); - Assert(res.send.calledOnce); - }); - }); - - describe("keep me logged in", () => { - beforeEach(() => { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - req.body.keepMeLoggedIn = "true"; - return FirstFactorPost.default(vars)(req as any, res as any); - }); - - it("should set keep_me_logged_in session variable to true", function () { - Assert.equal(authSession.keep_me_logged_in, true); - }); - - it("should set cookie maxAge to one year", function () { - Assert.equal(req.session.cookie.maxAge, 365 * 24 * 60 * 60 * 1000); - }); - }); - - it("should retrieve email from LDAP", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve([{ mail: ["test@example.com"] }])); - return FirstFactorPost.default(vars)(req as any, res as any); - }); - - it("should set first email address as user session variable", function () { - const emails = ["test_ok@example.com"]; - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal("test_ok@example.com", authSession.email); - }); - }); - - it("should return error message when LDAP authenticator throws", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.reject(new exceptions.LdapBindError("Bad credentials"))); - - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.equal(mocks.regulator.markStub.getCall(0).args[0], "username"); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); - - it("should return error message when regulator rejects authentication", function () { - const err = new exceptions.AuthenticationRegulationError("Authentication regulation..."); - mocks.regulator.regulateStub.returns(BluebirdPromise.reject(err)); - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); -}); - - diff --git a/themes/main/server/src/lib/routes/firstfactor/post.ts b/themes/main/server/src/lib/routes/firstfactor/post.ts deleted file mode 100644 index 565681d6a..000000000 --- a/themes/main/server/src/lib/routes/firstfactor/post.ts +++ /dev/null @@ -1,101 +0,0 @@ - -import Exceptions = require("../../Exceptions"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import Endpoint = require("../../../../../shared/api"); -import ErrorReplies = require("../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import UserMessages = require("../../../../../shared/UserMessages"); -import { ServerVariables } from "../../ServerVariables"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import { GroupsAndEmails } from "../../authentication/backends/GroupsAndEmails"; -import { Level as AuthenticationLevel } from "../../authentication/Level"; -import { Level as AuthorizationLevel } from "../../authorization/Level"; -import { URLDecomposer } from "../../utils/URLDecomposer"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response) - : BluebirdPromise { - const username: string = req.body.username; - const password: string = req.body.password; - const keepMeLoggedIn: boolean = req.body.keepMeLoggedIn && - req.body.keepMeLoggedIn === "true"; - let authSession: AuthenticationSession; - - if (keepMeLoggedIn) { - // Stay connected for 1 year. - vars.logger.debug(req, "User requested to stay logged in for one year."); - req.session.cookie.maxAge = 365 * 24 * 60 * 60 * 1000; - } - - return BluebirdPromise.resolve() - .then(function () { - if (!username || !password) { - return BluebirdPromise.reject(new Error("No username or password.")); - } - vars.logger.info(req, "Starting authentication of user \"%s\"", username); - authSession = AuthenticationSessionHandler.get(req, vars.logger); - return vars.regulator.regulate(username); - }) - .then(function () { - vars.logger.info(req, "No regulation applied."); - return vars.usersDatabase.checkUserPassword(username, password); - }) - .then(function (groupsAndEmails: GroupsAndEmails) { - vars.logger.info(req, - "LDAP binding successful. Retrieved information about user are %s", - JSON.stringify(groupsAndEmails)); - authSession.userid = username; - authSession.keep_me_logged_in = keepMeLoggedIn; - authSession.authentication_level = AuthenticationLevel.ONE_FACTOR; - const redirectUrl: string = req.query[Constants.REDIRECT_QUERY_PARAM] !== "undefined" - // Fuck, don't know why it is a string! - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : ""; - - const emails: string[] = groupsAndEmails.emails; - const groups: string[] = groupsAndEmails.groups; - const decomposition = URLDecomposer.fromUrl(redirectUrl); - const authorizationLevel = (decomposition) - ? vars.authorizer.authorization( - {domain: decomposition.domain, resource: decomposition.path}, - {user: username, groups: groups}) - : AuthorizationLevel.TWO_FACTOR; - - if (emails.length > 0) - authSession.email = emails[0]; - authSession.groups = groups; - - vars.logger.debug(req, "Mark successful authentication to regulator."); - vars.regulator.mark(username, true); - - if (authorizationLevel <= AuthorizationLevel.ONE_FACTOR) { - let newRedirectionUrl: string = redirectUrl; - if (!newRedirectionUrl) - newRedirectionUrl = Endpoint.LOGGED_IN; - res.send({ - redirect: newRedirectionUrl - }); - vars.logger.debug(req, "Redirect to '%s'", redirectUrl); - } - else { - let newRedirectUrl = Endpoint.SECOND_FACTOR_GET; - if (redirectUrl) { - newRedirectUrl += "?" + Constants.REDIRECT_QUERY_PARAM + "=" - + redirectUrl; - } - vars.logger.debug(req, "Redirect to '%s'", newRedirectUrl); - res.send({ - redirect: newRedirectUrl - }); - } - return BluebirdPromise.resolve(); - }) - .catch(Exceptions.LdapBindError, function (err: Error) { - vars.regulator.mark(username, false); - return ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.OPERATION_FAILED)(err); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.OPERATION_FAILED)); - }; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/loggedin/get.ts b/themes/main/server/src/lib/routes/loggedin/get.ts deleted file mode 100644 index 283a041b1..000000000 --- a/themes/main/server/src/lib/routes/loggedin/get.ts +++ /dev/null @@ -1,23 +0,0 @@ -import Express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; -import ErrorReplies = require("../../ErrorReplies"); - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - res.render("already-logged-in", { - logout_endpoint: Endpoints.LOGOUT_GET, - username: authSession.userid, - redirection_url: vars.config.default_redirection_url - }); - resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - } - - return handler; -} diff --git a/themes/main/server/src/lib/routes/logout/get.ts b/themes/main/server/src/lib/routes/logout/get.ts deleted file mode 100644 index 4d5112146..000000000 --- a/themes/main/server/src/lib/routes/logout/get.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import express = require("express"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import { ServerVariables } from "../../ServerVariables"; - -function getRedirectParam(req: express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -export default function (vars: ServerVariables) { - return function(req: express.Request, res: express.Response) { - const redirect_param = getRedirectParam(req); - const redirect_url = redirect_param || "/"; - AuthenticationSessionHandler.reset(req); - res.redirect(redirect_url); - }; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/password-reset/constants.ts b/themes/main/server/src/lib/routes/password-reset/constants.ts deleted file mode 100644 index 5c639e92a..000000000 --- a/themes/main/server/src/lib/routes/password-reset/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const CHALLENGE = "reset-password"; \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/password-reset/form/post.spec.ts b/themes/main/server/src/lib/routes/password-reset/form/post.spec.ts deleted file mode 100644 index ed029c906..000000000 --- a/themes/main/server/src/lib/routes/password-reset/form/post.spec.ts +++ /dev/null @@ -1,122 +0,0 @@ - -import PasswordResetFormPost = require("./post"); -import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../types/AuthenticationSession"; -import { UserDataStore } from "../../../storage/UserDataStore"; -import Sinon = require("sinon"); -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../ServerVariables"; -import { Level } from "../../../authentication/Level"; - -describe("routes/password-reset/form/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = { - originalUrl: "/api/password-reset", - body: { - userid: "user" - }, - session: {}, - headers: { - host: "localhost" - } - }; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - - mocks.config.authentication_backend.ldap = { - url: "ldap://ldapjs", - mail_attribute: "mail", - user: "user", - password: "password", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "user", - group_name_attribute: "cn", - groups_filter: "groups" - }; - - res = ExpressMock.ResponseMock(); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - authSession.userid = "user"; - authSession.email = "user@example.com"; - authSession.authentication_level = Level.ONE_FACTOR; - }); - - describe("test reset password post", () => { - it("should update the password and reset auth_session for reauthentication", function () { - req.body = {}; - req.body.password = "new-password"; - - mocks.usersDatabase.updatePasswordStub.returns(BluebirdPromise.resolve()); - - authSession.identity_check = { - userid: "user", - challenge: "reset-password" - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }).then(function (_authSession) { - Assert.equal(res.status.getCall(0).args[0], 204); - Assert.equal(_authSession.authentication_level, Level.NOT_AUTHENTICATED); - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if identity_challenge does not exist", function () { - authSession.identity_check = { - userid: "user", - challenge: undefined - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "An error occurred during password reset. Your password has not been changed." - }); - }); - }); - - it("should fail when ldap fails", function () { - req.body = {}; - req.body.password = "new-password"; - - mocks.usersDatabase.updatePasswordStub - .returns(BluebirdPromise.reject("Internal error with LDAP")); - - authSession.identity_check = { - challenge: "reset-password", - userid: "user" - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "An error occurred during password reset. Your password has not been changed." - }); - return BluebirdPromise.resolve(); - }); - }); - }); -}); diff --git a/themes/main/server/src/lib/routes/password-reset/form/post.ts b/themes/main/server/src/lib/routes/password-reset/form/post.ts deleted file mode 100644 index fccd7471b..000000000 --- a/themes/main/server/src/lib/routes/password-reset/form/post.ts +++ /dev/null @@ -1,50 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); -import exceptions = require("../../../Exceptions"); -import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../types/AuthenticationSession"; -import ErrorReplies = require("../../../ErrorReplies"); -import UserMessages = require("../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../ServerVariables"; - -import Constants = require("./../constants"); - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const newPassword = objectPath.get(req, "body.password"); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.identity_check) { - reject(new Error("No identity check initiated")); - return; - } - - vars.logger.info(req, "User %s wants to reset his/her password.", - authSession.identity_check.userid); - vars.logger.debug(req, "Challenge %s", authSession.identity_check.challenge); - - if (authSession.identity_check.challenge != Constants.CHALLENGE) { - reject(new Error("Bad challenge.")); - return; - } - resolve(); - }) - .then(function () { - return vars.usersDatabase.updatePassword(authSession.identity_check.userid, newPassword); - }) - .then(function () { - vars.logger.info(req, "Password reset for user '%s'", - authSession.identity_check.userid); - AuthenticationSessionHandler.reset(req); - res.status(204); - res.send(); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.RESET_PASSWORD_FAILED)); - }; -} diff --git a/themes/main/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts b/themes/main/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts deleted file mode 100644 index ac6a41754..000000000 --- a/themes/main/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts +++ /dev/null @@ -1,92 +0,0 @@ - -import PasswordResetHandler - from "./PasswordResetHandler"; -import { UserDataStore } from "../../../storage/UserDataStore"; -import Sinon = require("sinon"); -import winston = require("winston"); -import assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../ServerVariables"; - -describe("routes/password-reset/identity/PasswordResetHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = { - originalUrl: "/non-api/xxx", - query: { - userid: "user" - }, - session: { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false - } - }, - headers: { - host: "localhost" - } - }; - - const options = { - inMemoryOnly: true - }; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - mocks.userDataStore.saveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - res = ExpressMock.ResponseMock(); - }); - - describe("test reset password identity pre check", () => { - it("should fail when no userid is provided", function () { - req.query.userid = undefined; - const handler = new PasswordResetHandler(vars.logger, - vars.usersDatabase); - return handler.preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject("It should fail"); - }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if ldap fail", function () { - mocks.usersDatabase.getEmailsStub - .returns(BluebirdPromise.reject("Internal error")); - new PasswordResetHandler(vars.logger, vars.usersDatabase) - .preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should returns identity when ldap replies", function () { - mocks.usersDatabase.getEmailsStub - .returns(BluebirdPromise.resolve(["test@example.com"])); - return new PasswordResetHandler(vars.logger, vars.usersDatabase) - .preValidationInit(req as any); - }); - }); -}); diff --git a/themes/main/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts b/themes/main/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts deleted file mode 100644 index 42ae92cda..000000000 --- a/themes/main/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts +++ /dev/null @@ -1,69 +0,0 @@ -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); - -import exceptions = require("../../../Exceptions"); -import { Identity } from "../../../../../types/Identity"; -import { IdentityValidable } from "../../../IdentityValidable"; -import { PRE_VALIDATION_TEMPLATE } from "../../../IdentityCheckPreValidationTemplate"; -import Constants = require("../constants"); -import { IRequestLogger } from "../../../logging/IRequestLogger"; -import { IUsersDatabase } from "../../../authentication/backends/IUsersDatabase"; - -export const TEMPLATE_NAME = "password-reset-form"; - -export default class PasswordResetHandler implements IdentityValidable { - private logger: IRequestLogger; - private usersDatabase: IUsersDatabase; - - constructor(logger: IRequestLogger, usersDatabase: IUsersDatabase) { - this.logger = logger; - this.usersDatabase = usersDatabase; - } - - challenge(): string { - return Constants.CHALLENGE; - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - const userid: string = - objectPath.get(req, "query.userid"); - return BluebirdPromise.resolve() - .then(function () { - that.logger.debug(req, "User '%s' requested a password reset", userid); - if (!userid) - return BluebirdPromise.reject( - new exceptions.AccessDeniedError("No user id provided")); - - return that.usersDatabase.getEmails(userid); - }) - .then(function (emails: string[]) { - if (!emails && emails.length <= 0) throw new Error("No email found"); - const identity = { - email: emails[0], - userid: userid - }; - return BluebirdPromise.resolve(identity); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject(new exceptions.IdentityError(err.message)); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return BluebirdPromise.resolve(); - } - - postValidationResponse(req: express.Request, res: express.Response) { - res.render(TEMPLATE_NAME); - } - - mailSubject(): string { - return "Reset your password"; - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/password-reset/request/get.ts b/themes/main/server/src/lib/routes/password-reset/request/get.ts deleted file mode 100644 index 8f3ae2b4b..000000000 --- a/themes/main/server/src/lib/routes/password-reset/request/get.ts +++ /dev/null @@ -1,13 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); -import exceptions = require("../../../Exceptions"); - -import Constants = require("./../constants"); - -const TEMPLATE_NAME = "password-reset-request"; - -export default function (req: express.Request, res: express.Response) { - res.render(TEMPLATE_NAME); -} \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/secondfactor/get.spec.ts b/themes/main/server/src/lib/routes/secondfactor/get.spec.ts deleted file mode 100644 index 6c77e1f69..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/get.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import SecondFactorGet from "./get"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; -import Sinon = require("sinon"); -import ExpressMock = require("../../stubs/express.spec"); -import Assert = require("assert"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); - -describe("routes/secondfactor/get", function () { - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false - } - }; - }); - - describe("test rendering", function () { - it("should render second factor page", function () { - req.session.auth.second_factor = false; - return SecondFactorGet(vars)(req as any, res as any) - .then(function () { - Assert(res.render.calledWith("secondfactor")); - return BluebirdPromise.resolve(); - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/secondfactor/get.ts b/themes/main/server/src/lib/routes/secondfactor/get.ts deleted file mode 100644 index 9f6deb4c6..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/get.ts +++ /dev/null @@ -1,28 +0,0 @@ - -import Express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; - -const TEMPLATE_NAME = "secondfactor"; - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response) - : BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - - res.render(TEMPLATE_NAME, { - username: authSession.userid, - totp_identity_start_endpoint: - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - u2f_identity_start_endpoint: - Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET - }); - resolve(); - }); - } - return handler; -} diff --git a/themes/main/server/src/lib/routes/secondfactor/redirect.spec.ts b/themes/main/server/src/lib/routes/secondfactor/redirect.spec.ts deleted file mode 100644 index ea66e6dca..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/redirect.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import Redirect from "./redirect"; -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariablesMockBuilder, ServerVariablesMock } -from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; -import Assert = require("assert"); - -describe("routes/secondfactor/redirect", function() { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - }); - - it("should redirect to default_redirection_url", function() { - vars.config.default_redirection_url = "http://default_redirection_url"; - Redirect(vars)(req as any, res as any) - .then(function() { - Assert(res.json.calledWith({ - redirect: "http://default_redirection_url" - })); - }); - }); - - it("should redirect to /", function() { - Redirect(vars)(req as any, res as any) - .then(function() { - Assert(res.json.calledWith({ - redirect: "/" - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/secondfactor/redirect.ts b/themes/main/server/src/lib/routes/secondfactor/redirect.ts deleted file mode 100644 index 5d84d9ebf..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/redirect.ts +++ /dev/null @@ -1,30 +0,0 @@ - -import express = require("express"); -import objectPath = require("object-path"); -import Endpoints = require("../../../../../shared/api"); -import { ServerVariables } from "../../ServerVariables"; -import BluebirdPromise = require("bluebird"); -import ErrorReplies = require("../../ErrorReplies"); -import UserMessages = require("../../../../../shared/UserMessages"); -import { RedirectionMessage } from "../../../../../shared/RedirectionMessage"; -import Constants = require("../../../../../shared/constants"); - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response) - : BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - let redirectUrl: string = "/"; - if (vars.config.default_redirection_url) { - redirectUrl = vars.config.default_redirection_url; - } - vars.logger.debug(req, "Request redirection to \"%s\".", redirectUrl); - res.json({ - redirect: redirectUrl - } as RedirectionMessage); - return resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - }; -} diff --git a/themes/main/server/src/lib/routes/secondfactor/totp/constants.ts b/themes/main/server/src/lib/routes/secondfactor/totp/constants.ts deleted file mode 100644 index 7b5a1dcfe..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/totp/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export const CHALLENGE = "totp-register"; -export const TEMPLATE_NAME = "totp-register"; - diff --git a/themes/main/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts b/themes/main/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts deleted file mode 100644 index 78b8ea3ea..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts +++ /dev/null @@ -1,116 +0,0 @@ -import Sinon = require("sinon"); -import RegistrationHandler from "./RegistrationHandler"; -import { Identity } from "../../../../../../types/Identity"; -import { UserDataStore } from "../../../../storage/UserDataStore"; -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; -import Assert = require("assert"); - -describe("routes/secondfactor/totp/identity/RegistrationHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - req.session = { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false, - identity_check: { - userid: "user", - challenge: "totp-register" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.saveTOTPSecretStub - .returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - }); - - describe("test totp registration pre validation", function () { - it("should fail if first_factor has not been passed", function () { - req.session.auth.first_factor = false; - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("It should fail")); - }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if userid is missing", function (done) { - req.session.auth.first_factor = false; - req.session.auth.userid = undefined; - - new RegistrationHandler(vars.logger, vars.userDataStore, vars.totpHandler, - vars.config.totp) - .preValidationInit(req as any) - .catch(function (err: Error) { - done(); - }); - }); - - it("should fail if email is missing", function (done) { - req.session.auth.first_factor = false; - req.session.auth.email = undefined; - - new RegistrationHandler(vars.logger, vars.userDataStore, vars.totpHandler, - vars.config.totp) - .preValidationInit(req as any) - .catch(function (err: Error) { - done(); - }); - }); - - it("should succeed if first factor passed, userid and email are provided", - function () { - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .preValidationInit(req as any); - }); - }); - - describe("test totp registration post validation", function () { - it("should generate a secret using userId as label and issuer defined in config", function () { - vars.config.totp = { - issuer: "issuer" - }; - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .postValidationResponse(req as any, res as any) - .then(function() { - Assert(mocks.totpHandler.generateStub.calledWithExactly("user", "issuer")); - }); - }); - }); -}); diff --git a/themes/main/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts b/themes/main/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts deleted file mode 100644 index b39b6d045..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts +++ /dev/null @@ -1,112 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); - -import { Identity } from "../../../../../../types/Identity"; -import { IdentityValidable } from "../../../../IdentityValidable"; -import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate"; -import Constants = require("../constants"); -import Endpoints = require("../../../../../../../shared/api"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import FirstFactorValidator = require("../../../../FirstFactorValidator"); -import { IRequestLogger } from "../../../../logging/IRequestLogger"; -import { IUserDataStore } from "../../../../storage/IUserDataStore"; -import { ITotpHandler } from "../../../../authentication/totp/ITotpHandler"; -import { TOTPSecret } from "../../../../../../types/TOTPSecret"; -import { TotpConfiguration } from "../../../../configuration/schema/TotpConfiguration"; - - -export default class RegistrationHandler implements IdentityValidable { - private logger: IRequestLogger; - private userDataStore: IUserDataStore; - private totp: ITotpHandler; - private configuration: TotpConfiguration; - - constructor(logger: IRequestLogger, - userDataStore: IUserDataStore, - totp: ITotpHandler, configuration: TotpConfiguration) { - this.logger = logger; - this.userDataStore = userDataStore; - this.totp = totp; - this.configuration = configuration; - } - - challenge(): string { - return Constants.CHALLENGE; - } - - private retrieveIdentity(req: express.Request): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - const userid = authSession.userid; - const email = authSession.email; - - if (!(userid && email)) { - return reject(new Error("User ID or email is missing")); - } - - const identity = { - email: email, - userid: userid - }; - return resolve(identity); - }); - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - return FirstFactorValidator.validate(req, this.logger) - .then(function () { - return that.retrieveIdentity(req); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return FirstFactorValidator.validate(req, this.logger); - } - - postValidationResponse(req: express.Request, res: express.Response) - : BluebirdPromise { - const that = this; - let secret: TOTPSecret; - let userId: string; - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - userId = authSession.userid; - - if (authSession.identity_check.challenge != Constants.CHALLENGE - || !userId) - return reject(new Error("Bad challenge.")); - - resolve(); - }) - .then(function () { - secret = that.totp.generate(userId, - that.configuration.issuer); - that.logger.debug(req, "Save the TOTP secret in DB"); - return that.userDataStore.saveTOTPSecret(userId, secret); - }) - .then(function () { - AuthenticationSessionHandler.reset(req); - - res.render(Constants.TEMPLATE_NAME, { - base32_secret: secret.base32, - otpauth_url: secret.otpauth_url, - login_endpoint: Endpoints.FIRST_FACTOR_GET - }); - }) - .catch(ErrorReplies.replyWithError200(req, res, that.logger, UserMessages.OPERATION_FAILED)); - } - - mailSubject(): string { - return "Set up Authelia's one-time password"; - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts b/themes/main/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts deleted file mode 100644 index 70a20d39d..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import Assert = require("assert"); -import Exceptions = require("../../../../Exceptions"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import SignPost = require("./post"); -import { ServerVariables } from "../../../../ServerVariables"; - -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { Level } from "../../../../authentication/Level"; - -describe("routes/secondfactor/totp/sign/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let authSession: AuthenticationSession; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - const app_get = Sinon.stub(); - req = { - originalUrl: "/api/totp-register", - app: {}, - body: { - token: "abc" - }, - session: {}, - query: { - redirect: "http://redirect" - } - }; - res = ExpressMock.ResponseMock(); - - const doc = { - userid: "user", - secret: { - base32: "ABCDEF" - } - }; - mocks.userDataStore.retrieveTOTPSecretStub.returns(BluebirdPromise.resolve(doc)); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - authSession.userid = "user"; - authSession.authentication_level = Level.ONE_FACTOR; - }); - - - it("should send status code 200 when totp is valid", function () { - mocks.totpHandler.validateStub.returns(true); - return SignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(authSession.authentication_level, Level.TWO_FACTOR); - return BluebirdPromise.resolve(); - }); - }); - - it("should send error message when totp is not valid", function () { - mocks.totpHandler.validateStub.returns(false); - return SignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.notEqual(authSession.authentication_level, Level.TWO_FACTOR); - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); -}); - diff --git a/themes/main/server/src/lib/routes/secondfactor/totp/sign/post.ts b/themes/main/server/src/lib/routes/secondfactor/totp/sign/post.ts deleted file mode 100644 index 34a276d12..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/totp/sign/post.ts +++ /dev/null @@ -1,42 +0,0 @@ -import Bluebird = require("bluebird"); -import Express = require("express"); - -import { TOTPSecretDocument } from "../../../../storage/TOTPSecretDocument"; -import Endpoints = require("../../../../../../../shared/api"); -import Redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; -import { Level } from "../../../../authentication/Level"; - -const UNAUTHORIZED_MESSAGE = "Unauthorized access"; - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response): Bluebird { - let authSession: AuthenticationSession; - const token = req.body.token; - - return new Bluebird(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - vars.logger.info(req, "Initiate TOTP validation for user \"%s\".", authSession.userid); - resolve(); - }) - .then(function () { - return vars.userDataStore.retrieveTOTPSecret(authSession.userid); - }) - .then(function (doc: TOTPSecretDocument) { - if (!vars.totpHandler.validate(token, doc.secret.base32)) - return Bluebird.reject(new Error("Invalid TOTP token.")); - - vars.logger.debug(req, "TOTP validation succeeded."); - authSession.authentication_level = Level.TWO_FACTOR; - Redirect(vars)(req, res); - return Bluebird.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/main/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts b/themes/main/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts deleted file mode 100644 index 7f16c0ee8..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts +++ /dev/null @@ -1,11 +0,0 @@ - -import util = require("util"); -import express = require("express"); - -function extract_app_id(req: express.Request): string { - return util.format("https://%s", req.headers.host); -} - -export = { - extract_app_id: extract_app_id -}; \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts b/themes/main/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts deleted file mode 100644 index a54bfbfe6..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); - -import { Identity } from "../../../../../../types/Identity"; -import RegistrationHandler from "./RegistrationHandler"; -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -describe("routes/secondfactor/u2f/identity/RegistrationHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - req.app = {}; - req.session = { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = Sinon.spy(); - res.json = Sinon.spy(); - res.status = Sinon.spy(); - }); - - describe("test u2f registration check", test_registration_check); - - function test_registration_check() { - it("should fail if first_factor has not been passed", function () { - req.session.auth.first_factor = false; - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if userid is missing", function () { - req.session.auth.first_factor = false; - req.session.auth.userid = undefined; - - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if email is missing", function () { - req.session.auth.first_factor = false; - req.session.auth.email = undefined; - - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should succeed if first factor passed, userid and email are provided", function () { - req.session.auth.first_factor = true; - req.session.auth.email = "admin@example.com"; - req.session.auth.userid = "user"; - return new RegistrationHandler(vars.logger).preValidationInit(req as any); - }); - } -}); diff --git a/themes/main/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts b/themes/main/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts deleted file mode 100644 index bc4713c77..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts +++ /dev/null @@ -1,73 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import objectPath = require("object-path"); - -import { IdentityValidable } from "../../../../IdentityValidable"; -import { Identity } from "../../../../../../types/Identity"; -import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate"; -import FirstFactorValidator = require("../../../../FirstFactorValidator"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { IRequestLogger } from "../../../../logging/IRequestLogger"; - -const CHALLENGE = "u2f-register"; -const MAIL_SUBJECT = "Register your security key with Authelia"; - -const POST_VALIDATION_TEMPLATE_NAME = "u2f-register"; - - -export default class RegistrationHandler implements IdentityValidable { - private logger: IRequestLogger; - - constructor(logger: IRequestLogger) { - this.logger = logger; - } - - challenge(): string { - return CHALLENGE; - } - - private retrieveIdentity(req: express.Request): BluebirdPromise { - const that = this; - return new BluebirdPromise(function(resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - const userid = authSession.userid; - const email = authSession.email; - - if (!(userid && email)) { - return reject(new Error("User ID or email is missing")); - } - - const identity = { - email: email, - userid: userid - }; - return resolve(identity); - }); - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - return FirstFactorValidator.validate(req, this.logger) - .then(function () { - return that.retrieveIdentity(req); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return FirstFactorValidator.validate(req, this.logger); - } - - postValidationResponse(req: express.Request, res: express.Response) { - res.render(POST_VALIDATION_TEMPLATE_NAME); - } - - mailSubject(): string { - return MAIL_SUBJECT; - } -} - diff --git a/themes/main/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts b/themes/main/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts deleted file mode 100644 index de3347a21..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts +++ /dev/null @@ -1,146 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import assert = require("assert"); -import U2FRegisterPost = require("./post"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - - -describe("routes/secondfactor/u2f/register/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - describe("test registration", test_registration); - - - function test_registration() { - it("should save u2f meta and return status code 200", function () { - const expectedStatus = { - keyHandle: "keyHandle", - publicKey: "pbk", - certificate: "cert" - }; - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve(expectedStatus)); - - authSession.register_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { - assert.equal("user", mocks.userDataStore.saveU2FRegistrationStub.getCall(0).args[0]); - assert.equal(authSession.identity_check, undefined); - }); - }); - - it("should return error message on finishRegistration error", function () { - mocks.u2f.checkRegistrationStub.returns({ errorCode: 500 }); - - authSession.register_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when register_request is not provided", function () { - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve()); - authSession.register_request = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when no auth request has been initiated", function () { - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve()); - authSession.register_request = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when identity has not been verified", function () { - authSession.identity_check = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - } -}); - diff --git a/themes/main/server/src/lib/routes/secondfactor/u2f/register/post.ts b/themes/main/server/src/lib/routes/secondfactor/u2f/register/post.ts deleted file mode 100644 index 7296ccbe5..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/u2f/register/post.ts +++ /dev/null @@ -1,64 +0,0 @@ - -import { UserDataStore } from "../../../../storage/UserDataStore"; -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import U2f = require("u2f"); -import { U2FRegistration } from "../../../../../../types/U2FRegistration"; -import redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; - - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appid = u2f_common.extract_app_id(req); - const registrationResponse: U2f.RegistrationData = req.body; - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - const registrationRequest = authSession.register_request; - - if (!registrationRequest) { - return reject(new Error("No registration request")); - } - - if (!authSession.identity_check - || authSession.identity_check.challenge != "u2f-register") { - return reject(new Error("Bad challenge for registration request")); - } - - vars.logger.info(req, "Finishing registration"); - vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest)); - vars.logger.debug(req, "RegistrationResponse = %s", JSON.stringify(registrationResponse)); - - return resolve(vars.u2f.checkRegistration(registrationRequest, registrationResponse)); - }) - .then(function (u2fResult: U2f.RegistrationResult | U2f.Error): BluebirdPromise { - if (objectPath.has(u2fResult, "errorCode")) - return BluebirdPromise.reject(new Error("Error while registering.")); - - const registrationResult: U2f.RegistrationResult = u2fResult as U2f.RegistrationResult; - vars.logger.info(req, "Store registration and reply"); - vars.logger.debug(req, "RegistrationResult = %s", JSON.stringify(registrationResult)); - const registration: U2FRegistration = { - keyHandle: registrationResult.keyHandle, - publicKey: registrationResult.publicKey - }; - return vars.userDataStore.saveU2FRegistration(authSession.userid, appid, registration); - }) - .then(function () { - authSession.identity_check = undefined; - redirect(vars)(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/main/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts b/themes/main/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts deleted file mode 100644 index a207c9109..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts +++ /dev/null @@ -1,86 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import U2FRegisterRequestGet = require("./get"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -describe("routes/secondfactor/u2f/register_request/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - describe("test registration request", () => { - it("should send back the registration request and save it in the session", function () { - const expectedRequest = { - test: "abc" - }; - mocks.u2f.requestStub.returns(BluebirdPromise.resolve(expectedRequest)); - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.deepEqual(expectedRequest, res.json.getCall(0).args[0]); - }); - }); - - it("should return internal error on registration request", function () { - res.send = sinon.spy(); - const user_key_container = {}; - mocks.u2f.requestStub.returns(BluebirdPromise.reject("Internal error")); - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); - - it("should return forbidden if identity has not been verified", function () { - req.session.auth.identity_check = undefined; - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(403, res.status.getCall(0).args[0]); - }); - }); - }); -}); - diff --git a/themes/main/server/src/lib/routes/secondfactor/u2f/register_request/get.ts b/themes/main/server/src/lib/routes/secondfactor/u2f/register_request/get.ts deleted file mode 100644 index f611af933..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/u2f/register_request/get.ts +++ /dev/null @@ -1,43 +0,0 @@ - -import { UserDataStore } from "../../../../storage/UserDataStore"; - -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import U2f = require("u2f"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appid: string = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.identity_check - || authSession.identity_check.challenge != "u2f-register") { - res.status(403); - res.send(); - return reject(new Error("Bad challenge.")); - } - - vars.logger.info(req, "Starting registration for appId '%s'", appid); - return resolve(vars.u2f.request(appid)); - }) - .then(function (registrationRequest: U2f.Request) { - vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest)); - authSession.register_request = registrationRequest; - res.json(registrationRequest); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - - return handler; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts b/themes/main/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts deleted file mode 100644 index 9b137e66d..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts +++ /dev/null @@ -1,101 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import U2FSignPost = require("./post"); -import { ServerVariables } from "../../../../ServerVariables"; -import winston = require("winston"); - -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import ExpressMock = require("../../../../stubs/express.spec"); -import U2FMock = require("../../../../stubs/u2f.spec"); -import U2f = require("u2f"); -import { Level } from "../../../../authentication/Level"; - -describe("routes/secondfactor/u2f/sign/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.app = {}; - req.originalUrl = "/api/xxxx"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req.session = { - auth: { - userid: "user", - authentication_level: Level.ONE_FACTOR, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - it("should return status code 204", function () { - const expectedStatus = { - keyHandle: "keyHandle", - publicKey: "pbk", - certificate: "cert" - }; - mocks.u2f.checkSignatureStub.returns(expectedStatus); - - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({ - registration: { - publicKey: "PUBKEY" - } - })); - - req.session.auth.sign_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FSignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(req.session.auth.authentication_level, Level.TWO_FACTOR); - }); - }); - - it("should return unauthorized error on registration request internal error", function () { - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({ - registration: { - publicKey: "PUBKEY" - } - })); - mocks.u2f.checkSignatureStub.returns({ errorCode: 500 }); - - req.session.auth.sign_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FSignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], - { error: "Operation failed." }); - }); - }); -}); - diff --git a/themes/main/server/src/lib/routes/secondfactor/u2f/sign/post.ts b/themes/main/server/src/lib/routes/secondfactor/u2f/sign/post.ts deleted file mode 100644 index 7ee711c2c..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/u2f/sign/post.ts +++ /dev/null @@ -1,57 +0,0 @@ - -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import { UserDataStore } from "../../../../storage/UserDataStore"; -import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocument"; -import { Winston } from "../../../../../../types/Dependencies"; -import U2f = require("u2f"); -import exceptions = require("../../../../Exceptions"); -import redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import { Level } from "../../../../authentication/Level"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appId = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.sign_request) { - const err = new Error("No sign request"); - ErrorReplies.replyWithError401(req, res, vars.logger)(err); - return reject(err); - } - resolve(); - }) - .then(function () { - const userid = authSession.userid; - return vars.userDataStore.retrieveU2FRegistration(userid, appId); - }) - .then(function (doc: U2FRegistrationDocument): BluebirdPromise { - const signRequest = authSession.sign_request; - const signData: U2f.SignatureData = req.body; - vars.logger.info(req, "Finish authentication"); - return BluebirdPromise.resolve(vars.u2f.checkSignature(signRequest, signData, doc.registration.publicKey)); - }) - .then(function (result: U2f.SignatureResult | U2f.Error): BluebirdPromise { - if (objectPath.has(result, "errorCode")) - return BluebirdPromise.reject(new Error("Error while signing")); - vars.logger.info(req, "Successful authentication"); - authSession.authentication_level = Level.TWO_FACTOR; - redirect(vars)(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - - return handler; -} - diff --git a/themes/main/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts b/themes/main/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts deleted file mode 100644 index dd52b27e0..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import assert = require("assert"); -import U2FSignRequestGet = require("./get"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { Request } from "u2f"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -import { SignMessage } from "../../../../../../../shared/SignMessage"; - -describe("routes/secondfactor/u2f/sign_request/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - it("should send back the sign request and save it in the session", function () { - const expectedRequest: Request = { - version: "U2F_V2", - appId: 'app', - challenge: 'challenge!' - }; - mocks.u2f.requestStub.returns(expectedRequest); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({ - registration: { - keyHandle: "KeyHandle" - } - })); - - return U2FSignRequestGet.default(vars)(req as any, res as any) - .then(() => { - assert.deepEqual(expectedRequest, req.session.auth.sign_request); - assert.deepEqual(expectedRequest, res.json.getCall(0).args[0]); - }); - }); -}); - diff --git a/themes/main/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts b/themes/main/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts deleted file mode 100644 index 9e93dde06..000000000 --- a/themes/main/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts +++ /dev/null @@ -1,42 +0,0 @@ - -import u2f_common = require("../../../secondfactor/u2f/U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocument"; -import exceptions = require("../../../../Exceptions"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appId = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - resolve(); - }) - .then(function () { - return vars.userDataStore.retrieveU2FRegistration(authSession.userid, appId); - }) - .then(function (doc: U2FRegistrationDocument): BluebirdPromise { - if (!doc) - return BluebirdPromise.reject(new exceptions.AccessDeniedError("No U2F registration document found.")); - - const appId: string = u2f_common.extract_app_id(req); - vars.logger.info(req, "Start authentication of app '%s'", appId); - vars.logger.debug(req, "AppId = %s, keyHandle = %s", appId, JSON.stringify(doc.registration.keyHandle)); - - const request = vars.u2f.request(appId, doc.registration.keyHandle); - authSession.sign_request = request; - res.json(request); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/main/server/src/lib/routes/verify/access_control.ts b/themes/main/server/src/lib/routes/verify/access_control.ts deleted file mode 100644 index 136239aeb..000000000 --- a/themes/main/server/src/lib/routes/verify/access_control.ts +++ /dev/null @@ -1,51 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import Util = require("util"); - -import Exceptions = require("../../Exceptions"); - -import { Level as AuthorizationLevel } from "../../authorization/Level"; -import { Level as AuthenticationLevel } from "../../authentication/Level"; -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; - -function isAuthorized( - authorization: AuthorizationLevel, - authentication: AuthenticationLevel): boolean { - - if (authorization == AuthorizationLevel.BYPASS) { - return true; - } else if (authorization == AuthorizationLevel.ONE_FACTOR && - authentication >= AuthenticationLevel.ONE_FACTOR) { - return true; - } else if (authorization == AuthorizationLevel.TWO_FACTOR && - authentication >= AuthenticationLevel.TWO_FACTOR) { - return true; - } - return false; -} - -export default function ( - req: Express.Request, - vars: ServerVariables, - domain: string, resource: string, - user: string, groups: string[], - authenticationLevel: AuthenticationLevel) { - - return new BluebirdPromise(function (resolve, reject) { - const authorizationLevel = vars.authorizer - .authorization({domain, resource}, {user, groups}); - - if (!isAuthorized(authorizationLevel, authenticationLevel)) { - if (authorizationLevel == AuthorizationLevel.DENY) { - reject(new Exceptions.NotAuthorizedError( - Util.format("User %s is not authorized to access %s%s", user, domain, resource))); - return; - } - reject(new Exceptions.NotAuthenticatedError(Util.format( - "User '%s' is not sufficiently authorized to access %s%s.", user, domain, resource))); - return; - } - resolve(); - }); -} \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/verify/get.spec.ts b/themes/main/server/src/lib/routes/verify/get.spec.ts deleted file mode 100644 index 67cf19fb6..000000000 --- a/themes/main/server/src/lib/routes/verify/get.spec.ts +++ /dev/null @@ -1,320 +0,0 @@ - -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import Express = require("express"); -import Sinon = require("sinon"); -import winston = require("winston"); - -import VerifyGet = require("./get"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariables } from "../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../ServerVariablesMockBuilder.spec"; -import { Level } from "../../authentication/Level"; -import { Level as AuthorizationLevel } from "../../authorization/Level"; - -describe("routes/verify/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - req.originalUrl = "/api/xxxx"; - req.query = { - redirect: "undefined" - }; - AuthenticationSessionHandler.reset(req as any); - req.headers["x-original-url"] = "https://secret.example.com/"; - const s = ServerVariablesMockBuilder.build(false); - mocks = s.mocks; - vars = s.variables; - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - describe("with session cookie", function () { - it("should be already authenticated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser"); - Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup"); - Assert.equal(204, res.status.getCall(0).args[0]); - }); - }); - - function test_session(_authSession: AuthenticationSession, status_code: number) { - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert.equal(status_code, res.status.getCall(0).args[0]); - }); - } - - function test_non_authenticated_401(authSession: AuthenticationSession) { - return test_session(authSession, 401); - } - - function test_unauthorized_403(authSession: AuthenticationSession) { - return test_session(authSession, 403); - } - - function test_authorized(authSession: AuthenticationSession) { - return test_session(authSession, 204); - } - - describe("given user tries to access a 2-factor endpoint", function () { - before(function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - }); - - describe("given different cases of session", function () { - it("should not be authenticated when second factor is missing", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: "user", - authentication_level: Level.ONE_FACTOR, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when userid is missing", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: undefined, - authentication_level: Level.TWO_FACTOR, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when level is insufficient", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: "user", - authentication_level: Level.NOT_AUTHENTICATED, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when session has not be initiated", function () { - return test_non_authenticated_401(undefined); - }); - - it("should not be authenticated when domain is not allowed for user", function () { - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - req.headers["x-original-url"] = "https://test.example.com/"; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.DENY); - - return test_unauthorized_403({ - keep_me_logged_in: false, - authentication_level: Level.TWO_FACTOR, - userid: "user", - groups: ["group1", "group2"], - email: undefined, - last_activity_datetime: new Date().getTime() - }); - }); - }); - }); - - describe("given user tries to access a single factor endpoint", function () { - beforeEach(function () { - req.headers["x-original-url"] = "https://redirect.url/"; - }); - - it("should be authenticated when first factor is validated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - authSession.authentication_level = Level.ONE_FACTOR; - authSession.userid = "user1"; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWith(204)); - Assert(res.send.calledOnce); - }); - }); - - it("should be rejected with 401 when not authenticated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - authSession.authentication_level = Level.NOT_AUTHENTICATED; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWith(401)); - }); - }); - }); - - describe("inactivity period", function () { - it("should update last inactivity period on requests on /api/verify", function () { - mocks.config.session.inactivity = 200000; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - const currentTime = new Date().getTime() - 1000; - AuthenticationSessionHandler.reset(req as any); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - authSession.last_activity_datetime = currentTime; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }) - .then(function (authSession) { - Assert(authSession.last_activity_datetime > currentTime); - }); - }); - - it("should reset session when max inactivity period has been reached", function () { - mocks.config.session.inactivity = 1; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - const currentTime = new Date().getTime() - 1000; - AuthenticationSessionHandler.reset(req as any); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - authSession.last_activity_datetime = currentTime; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }) - .then(function (authSession) { - Assert.equal(authSession.authentication_level, Level.NOT_AUTHENTICATED); - Assert.equal(authSession.userid, undefined); - }); - }); - }); - }); - - describe("response type 401 | 302", function() { - it("should return error code 401", function() { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should redirect to provided redirection url", function() { - const REDIRECT_URL = "http://redirection_url.com"; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - req.query["rd"] = REDIRECT_URL; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.redirect.calledWithExactly(REDIRECT_URL)); - }); - }); - }); - - describe("with basic auth", function () { - it("should authenticate correctly", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.returns({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "john"); - Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup"); - Assert.equal(204, res.status.getCall(0).args[0]); - }); - }); - - it("should fail when endpoint is protected by two factors", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.config.access_control.rules = [{ - domain: "secret.example.com", - policy: "two_factor" - }]; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when base64 token is not valid", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic i_m*not_a_base64*token"; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when base64 token has not format user:psswd", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzOmJhZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when bad user password is provided", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when resource is restricted", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - }); -}); - diff --git a/themes/main/server/src/lib/routes/verify/get.ts b/themes/main/server/src/lib/routes/verify/get.ts deleted file mode 100644 index f73861696..000000000 --- a/themes/main/server/src/lib/routes/verify/get.ts +++ /dev/null @@ -1,91 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Express = require("express"); -import Exceptions = require("../../Exceptions"); -import ErrorReplies = require("../../ErrorReplies"); -import { ServerVariables } from "../../ServerVariables"; -import GetWithSessionCookieMethod from "./get_session_cookie"; -import GetWithBasicAuthMethod from "./get_basic_auth"; -import Constants = require("../../../../../shared/constants"); -import ObjectPath = require("object-path"); - -import { AuthenticationSessionHandler } - from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; - -const REMOTE_USER = "Remote-User"; -const REMOTE_GROUPS = "Remote-Groups"; - - -function verifyWithSelectedMethod(req: Express.Request, res: Express.Response, - vars: ServerVariables, authSession: AuthenticationSession) - : () => BluebirdPromise<{ username: string, groups: string[] }> { - return function () { - const authorization: string = "" + req.headers["proxy-authorization"]; - if (authorization && authorization.startsWith("Basic ")) - return GetWithBasicAuthMethod(req, res, vars, authorization); - - return GetWithSessionCookieMethod(req, res, vars, authSession); - }; -} - -function setRedirectHeader(req: Express.Request, res: Express.Response) { - return function () { - const originalUrl = ObjectPath.get( - req, "headers.x-original-url"); - res.set("Redirect", originalUrl); - return BluebirdPromise.resolve(); - }; -} - -function setUserAndGroupsHeaders(res: Express.Response) { - return function (u: { username: string, groups: string[] }) { - res.setHeader(REMOTE_USER, u.username); - res.setHeader(REMOTE_GROUPS, u.groups.join(",")); - return BluebirdPromise.resolve(); - }; -} - -function replyWith200(res: Express.Response) { - return function () { - res.status(204); - res.send(); - }; -} - -function getRedirectParam(req: Express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -export default function (vars: ServerVariables) { - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - let authSession: AuthenticationSession; - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - resolve(); - }) - .then(setRedirectHeader(req, res)) - .then(verifyWithSelectedMethod(req, res, vars, authSession)) - .then(setUserAndGroupsHeaders(res)) - .then(replyWith200(res)) - // The user is authenticated but has restricted access -> 403 - .catch(Exceptions.NotAuthorizedError, - ErrorReplies.replyWithError403(req, res, vars.logger)) - .catch(Exceptions.NotAuthenticatedError, - ErrorReplies.replyWithError401(req, res, vars.logger)) - // The user is not yet authenticated -> 401 - .catch((err) => { - const redirectUrl = getRedirectParam(req); - if (redirectUrl) { - ErrorReplies.redirectTo(redirectUrl, req, res, vars.logger)(err); - } - else { - ErrorReplies.replyWithError401(req, res, vars.logger)(err); - } - }); - }; -} - diff --git a/themes/main/server/src/lib/routes/verify/get_basic_auth.ts b/themes/main/server/src/lib/routes/verify/get_basic_auth.ts deleted file mode 100644 index af23c76c9..000000000 --- a/themes/main/server/src/lib/routes/verify/get_basic_auth.ts +++ /dev/null @@ -1,55 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import ObjectPath = require("object-path"); -import { ServerVariables } from "../../ServerVariables"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; -import AccessControl from "./access_control"; -import { URLDecomposer } from "../../utils/URLDecomposer"; -import { Level } from "../../authentication/Level"; - -export default function (req: Express.Request, res: Express.Response, - vars: ServerVariables, authorizationHeader: string) - : BluebirdPromise<{ username: string, groups: string[] }> { - let username: string; - const uri = ObjectPath.get(req, "headers.x-original-url"); - const urlDecomposition = URLDecomposer.fromUrl(uri); - - return BluebirdPromise.resolve() - .then(() => { - const base64Re = new RegExp("^Basic ((?:[A-Za-z0-9+/]{4})*" + - "(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)$"); - const isTokenValidBase64 = base64Re.test(authorizationHeader); - - if (!isTokenValidBase64) { - return BluebirdPromise.reject(new Error("No valid base64 token found in the header")); - } - - const tokenMatches = authorizationHeader.match(base64Re); - const base64Token = tokenMatches[1]; - const decodedToken = Buffer.from(base64Token, "base64").toString(); - const splittedToken = decodedToken.split(":"); - - if (splittedToken.length != 2) { - return BluebirdPromise.reject(new Error( - "The authorization token is invalid. Expecting 'userid:password'")); - } - - username = splittedToken[0]; - const password = splittedToken[1]; - return vars.usersDatabase.checkUserPassword(username, password); - }) - .then(function (groupsAndEmails) { - return AccessControl(req, vars, urlDecomposition.domain, urlDecomposition.path, - username, groupsAndEmails.groups, Level.ONE_FACTOR) - .then(() => BluebirdPromise.resolve({ - username: username, - groups: groupsAndEmails.groups - })); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject( - new Error("Unable to authenticate the user with basic auth. Cause: " - + err.message)); - }); -} \ No newline at end of file diff --git a/themes/main/server/src/lib/routes/verify/get_session_cookie.ts b/themes/main/server/src/lib/routes/verify/get_session_cookie.ts deleted file mode 100644 index 070344812..000000000 --- a/themes/main/server/src/lib/routes/verify/get_session_cookie.ts +++ /dev/null @@ -1,78 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import Util = require("util"); -import ObjectPath = require("object-path"); - -import Exceptions = require("../../Exceptions"); -import { Configuration } from "../../configuration/schema/Configuration"; -import { ServerVariables } from "../../ServerVariables"; -import { IRequestLogger } from "../../logging/IRequestLogger"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; -import { AuthenticationSessionHandler } - from "../../AuthenticationSessionHandler"; -import AccessControl from "./access_control"; -import { URLDecomposer } from "../../utils/URLDecomposer"; - -function verify_inactivity(req: Express.Request, - authSession: AuthenticationSession, - configuration: Configuration, logger: IRequestLogger) - : BluebirdPromise { - - // If inactivity is not specified, then inactivity timeout does not apply - if (!configuration.session.inactivity || authSession.keep_me_logged_in) { - return BluebirdPromise.resolve(); - } - - const lastActivityTime = authSession.last_activity_datetime; - const currentTime = new Date().getTime(); - authSession.last_activity_datetime = currentTime; - - const inactivityPeriodMs = currentTime - lastActivityTime; - logger.debug(req, "Inactivity period was %s s and max period was %s.", - inactivityPeriodMs / 1000, configuration.session.inactivity / 1000); - if (inactivityPeriodMs < configuration.session.inactivity) { - return BluebirdPromise.resolve(); - } - - logger.debug(req, "Session has been reset after too long inactivity period."); - AuthenticationSessionHandler.reset(req); - return BluebirdPromise.reject(new Error("Inactivity period exceeded.")); -} - -export default function (req: Express.Request, res: Express.Response, - vars: ServerVariables, authSession: AuthenticationSession) - : BluebirdPromise<{ username: string, groups: string[] }> { - - return BluebirdPromise.resolve() - .then(() => { - const username = authSession.userid; - const groups = authSession.groups; - - if (!authSession.userid) { - return BluebirdPromise.reject(new Exceptions.AccessDeniedError( - "userid is missing")); - } - - const originalUrl = ObjectPath.get( - req, "headers.x-original-url"); - const originalUri = - ObjectPath.get(req, "headers.x-original-uri"); - - const d = URLDecomposer.fromUrl(originalUrl); - vars.logger.debug(req, "domain=%s, path=%s, user=%s, groups=%s", d.domain, - d.path, username, groups.join(",")); - return AccessControl(req, vars, d.domain, d.path, username, groups, - authSession.authentication_level); - }) - .then(() => { - return verify_inactivity(req, authSession, - vars.config, vars.logger); - }) - .then(() => { - return BluebirdPromise.resolve({ - username: authSession.userid, - groups: authSession.groups - }); - }); -} \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/AuthenticationTraceDocument.d.ts b/themes/main/server/src/lib/storage/AuthenticationTraceDocument.d.ts deleted file mode 100644 index 69818c055..000000000 --- a/themes/main/server/src/lib/storage/AuthenticationTraceDocument.d.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export interface AuthenticationTraceDocument { - userId: string; - date: Date; - isAuthenticationSuccessful: boolean; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/CollectionFactoryFactory.ts b/themes/main/server/src/lib/storage/CollectionFactoryFactory.ts deleted file mode 100644 index 92b29abfa..000000000 --- a/themes/main/server/src/lib/storage/CollectionFactoryFactory.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ICollectionFactory } from "./ICollectionFactory"; -import { NedbCollectionFactory } from "./nedb/NedbCollectionFactory"; -import { MongoCollectionFactory } from "./mongo/MongoCollectionFactory"; -import { IMongoClient } from "../connectors/mongo/IMongoClient"; - - -export class CollectionFactoryFactory { - static createNedb(options: Nedb.DataStoreOptions): ICollectionFactory { - return new NedbCollectionFactory(options); - } - - static createMongo(client: IMongoClient): ICollectionFactory { - return new MongoCollectionFactory(client); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/CollectionFactoryStub.spec.ts b/themes/main/server/src/lib/storage/CollectionFactoryStub.spec.ts deleted file mode 100644 index 17f8bb021..000000000 --- a/themes/main/server/src/lib/storage/CollectionFactoryStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import { ICollection } from "./ICollection"; -import { ICollectionFactory } from "./ICollectionFactory"; - -export class CollectionFactoryStub implements ICollectionFactory { - buildStub: Sinon.SinonStub; - - constructor() { - this.buildStub = Sinon.stub(); - } - - build(collectionName: string): ICollection { - return this.buildStub(collectionName); - } -} diff --git a/themes/main/server/src/lib/storage/CollectionStub.spec.ts b/themes/main/server/src/lib/storage/CollectionStub.spec.ts deleted file mode 100644 index 42895d672..000000000 --- a/themes/main/server/src/lib/storage/CollectionStub.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import { ICollection } from "./ICollection"; - -export class CollectionStub implements ICollection { - findStub: Sinon.SinonStub; - findOneStub: Sinon.SinonStub; - updateStub: Sinon.SinonStub; - removeStub: Sinon.SinonStub; - insertStub: Sinon.SinonStub; - - constructor() { - this.findStub = Sinon.stub(); - this.findOneStub = Sinon.stub(); - this.updateStub = Sinon.stub(); - this.removeStub = Sinon.stub(); - this.insertStub = Sinon.stub(); - } - - find(filter: any, sortKeys: any, count: number): BluebirdPromise { - return this.findStub(filter, sortKeys, count); - } - - findOne(filter: any): BluebirdPromise { - return this.findOneStub(filter); - } - - update(filter: any, document: any, options: any): BluebirdPromise { - return this.updateStub(filter, document, options); - } - - remove(filter: any): BluebirdPromise { - return this.removeStub(filter); - } - - insert(document: any): BluebirdPromise { - return this.insertStub(document); - } -} diff --git a/themes/main/server/src/lib/storage/ICollection.d.ts b/themes/main/server/src/lib/storage/ICollection.d.ts deleted file mode 100644 index caa6c2a87..000000000 --- a/themes/main/server/src/lib/storage/ICollection.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* istanbul ignore next */ -import BluebirdPromise = require("bluebird"); - -/* istanbul ignore next */ -export interface ICollection { - find(query: any, sortKeys: any, count: number): BluebirdPromise; - findOne(query: any): BluebirdPromise; - update(query: any, updateQuery: any, options?: any): BluebirdPromise; - remove(query: any): BluebirdPromise; - insert(document: any): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/ICollectionFactory.d.ts b/themes/main/server/src/lib/storage/ICollectionFactory.d.ts deleted file mode 100644 index 39eb42c77..000000000 --- a/themes/main/server/src/lib/storage/ICollectionFactory.d.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import { ICollection } from "./ICollection"; - -export interface ICollectionFactory { - build(collectionName: string): ICollection; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/IUserDataStore.d.ts b/themes/main/server/src/lib/storage/IUserDataStore.d.ts deleted file mode 100644 index 81df482aa..000000000 --- a/themes/main/server/src/lib/storage/IUserDataStore.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; - -export interface IUserDataStore { - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise; - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise; - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise; - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise; - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise; - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise; - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise; - retrieveTOTPSecret(userId: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/IdentityValidationDocument.d.ts b/themes/main/server/src/lib/storage/IdentityValidationDocument.d.ts deleted file mode 100644 index e7fd7b3f4..000000000 --- a/themes/main/server/src/lib/storage/IdentityValidationDocument.d.ts +++ /dev/null @@ -1,7 +0,0 @@ - -export interface IdentityValidationDocument { - userId: string; - token: string; - challenge: string; - maxDate: Date; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/TOTPSecretDocument.d.ts b/themes/main/server/src/lib/storage/TOTPSecretDocument.d.ts deleted file mode 100644 index a6c0bf9e6..000000000 --- a/themes/main/server/src/lib/storage/TOTPSecretDocument.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { TOTPSecret } from "../../../types/TOTPSecret"; - -export interface TOTPSecretDocument { - userid: string; - secret: TOTPSecret; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/U2FRegistrationDocument.d.ts b/themes/main/server/src/lib/storage/U2FRegistrationDocument.d.ts deleted file mode 100644 index efec6cb1d..000000000 --- a/themes/main/server/src/lib/storage/U2FRegistrationDocument.d.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import { U2FRegistration } from "../../../types/U2FRegistration"; - -export interface U2FRegistrationDocument { - userId: string; - appId: string; - registration: U2FRegistration; -} \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/UserDataStore.spec.ts b/themes/main/server/src/lib/storage/UserDataStore.spec.ts deleted file mode 100644 index 66fb85461..000000000 --- a/themes/main/server/src/lib/storage/UserDataStore.spec.ts +++ /dev/null @@ -1,264 +0,0 @@ - -import * as Assert from "assert"; -import * as Sinon from "sinon"; -import * as MockDate from "mockdate"; -import BluebirdPromise = require("bluebird"); - -import { UserDataStore } from "./UserDataStore"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { CollectionStub } from "./CollectionStub.spec"; -import { CollectionFactoryStub } from "./CollectionFactoryStub.spec"; - -describe("storage/UserDataStore", function () { - let factory: CollectionFactoryStub; - let collection: CollectionStub; - let userId: string; - let appId: string; - let totpSecret: TOTPSecret; - let u2fRegistration: U2FRegistration; - - beforeEach(function () { - factory = new CollectionFactoryStub(); - collection = new CollectionStub(); - - userId = "user"; - appId = "https://myappId"; - - totpSecret = { - ascii: "abc", - base32: "ABCDKZLEFZGREJK", - otpauth_url: "totp://test", - google_auth_qr: "dummy", - hex: "dummy", - qr_code_ascii: "dummy", - qr_code_base32: "dummy", - qr_code_hex: "dummy" - }; - - u2fRegistration = { - keyHandle: "KEY_HANDLE", - publicKey: "publickey" - }; - }); - - it("should correctly creates collections", function () { - new UserDataStore(factory); - - Assert.equal(4, factory.buildStub.callCount); - Assert(factory.buildStub.calledWith("authentication_traces")); - Assert(factory.buildStub.calledWith("identity_validation_tokens")); - Assert(factory.buildStub.calledWith("u2f_registrations")); - Assert(factory.buildStub.calledWith("totp_secrets")); - }); - - describe("TOTP secrets collection", function () { - it("should save a totp secret", function () { - factory.buildStub.returns(collection); - collection.updateStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveTOTPSecret(userId, totpSecret) - .then(function (doc) { - Assert(collection.updateStub.calledOnce); - Assert(collection.updateStub.calledWith({ userId: userId }, { - userId: userId, - secret: totpSecret - }, { upsert: true })); - return BluebirdPromise.resolve(); - }); - }); - - it("should retrieve a totp secret", function () { - factory.buildStub.returns(collection); - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveTOTPSecret(userId) - .then(function (doc) { - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ userId: userId })); - return BluebirdPromise.resolve(); - }); - }); - }); - - describe("U2F secrets collection", function () { - it("should save a U2F secret", function () { - factory.buildStub.returns(collection); - collection.updateStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveU2FRegistration(userId, appId, u2fRegistration) - .then(function (doc) { - Assert(collection.updateStub.calledOnce); - Assert(collection.updateStub.calledWith({ - userId: userId, - appId: appId - }, { - userId: userId, - appId: appId, - registration: u2fRegistration - }, { upsert: true })); - return BluebirdPromise.resolve(); - }); - }); - - it("should retrieve a U2F secret", function () { - factory.buildStub.returns(collection); - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveU2FRegistration(userId, appId) - .then(function (doc) { - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - userId: userId, - appId: appId - })); - return BluebirdPromise.resolve(); - }); - }); - }); - - - describe("Regulator traces collection", function () { - it("should save a trace", function () { - factory.buildStub.returns(collection); - collection.insertStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveAuthenticationTrace(userId, true) - .then(function (doc) { - Assert(collection.insertStub.calledOnce); - Assert(collection.insertStub.calledWith({ - userId: userId, - date: Sinon.match.date, - isAuthenticationSuccessful: true - })); - return BluebirdPromise.resolve(); - }); - }); - - function should_retrieve_latest_authentication_traces(count: number) { - factory.buildStub.returns(collection); - collection.findStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveLatestAuthenticationTraces(userId, count) - .then(function (doc: AuthenticationTraceDocument[]) { - Assert(collection.findStub.calledOnce); - Assert(collection.findStub.calledWith({ - userId: userId, - }, { date: -1 }, count)); - return BluebirdPromise.resolve(); - }); - } - - it("should retrieve 3 latest failed authentication traces", function () { - should_retrieve_latest_authentication_traces(3); - }); - }); - - - describe("Identity validation collection", function () { - it("should save a identity validation token", function () { - factory.buildStub.returns(collection); - collection.insertStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - const maxAge = 400; - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - return dataStore.produceIdentityValidationToken(userId, token, challenge, maxAge) - .then(function (doc) { - Assert(collection.insertStub.calledOnce); - Assert(collection.insertStub.calledWith({ - userId: userId, - token: token, - challenge: challenge, - maxDate: Sinon.match.date - })); - return BluebirdPromise.resolve(); - }); - }); - - it("should consume an identity token successfully", function () { - factory.buildStub.returns(collection); - - MockDate.set(100); - - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve({ - userId: "USER", - token: token, - challenge: challenge, - maxDate: new Date() - })); - collection.removeStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - MockDate.set(80); - - return dataStore.consumeIdentityValidationToken(token, challenge) - .then(function (doc) { - MockDate.reset(); - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - token: token, - challenge: challenge - })); - - Assert(collection.removeStub.calledOnce); - Assert(collection.removeStub.calledWith({ - token: token, - challenge: challenge - })); - return BluebirdPromise.resolve(); - }); - }); - - it("should consume an expired identity token", function () { - factory.buildStub.returns(collection); - - MockDate.set(0); - - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve({ - userId: "USER", - token: token, - challenge: challenge, - maxDate: new Date() - })); - - const dataStore = new UserDataStore(factory); - - MockDate.set(80000); - - return dataStore.consumeIdentityValidationToken(token, challenge) - .then(function () { return BluebirdPromise.reject(new Error("should not be here")); }) - .catch(function () { - MockDate.reset(); - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - token: token, - challenge: challenge - })); - return BluebirdPromise.resolve(); - }); - }); - }); -}); diff --git a/themes/main/server/src/lib/storage/UserDataStore.ts b/themes/main/server/src/lib/storage/UserDataStore.ts deleted file mode 100644 index 27b0cddbd..000000000 --- a/themes/main/server/src/lib/storage/UserDataStore.ts +++ /dev/null @@ -1,143 +0,0 @@ -import * as BluebirdPromise from "bluebird"; -import * as path from "path"; -import { IUserDataStore } from "./IUserDataStore"; -import { ICollection } from "./ICollection"; -import { ICollectionFactory } from "./ICollectionFactory"; -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; - -// Constants - -const IDENTITY_VALIDATION_TOKENS_COLLECTION_NAME = "identity_validation_tokens"; -const AUTHENTICATION_TRACES_COLLECTION_NAME = "authentication_traces"; - -const U2F_REGISTRATIONS_COLLECTION_NAME = "u2f_registrations"; -const TOTP_SECRETS_COLLECTION_NAME = "totp_secrets"; - - -export interface U2FRegistrationKey { - userId: string; - appId: string; -} - -// Source - -export class UserDataStore implements IUserDataStore { - private u2fSecretCollection: ICollection; - private identityCheckTokensCollection: ICollection; - private authenticationTracesCollection: ICollection; - private totpSecretCollection: ICollection; - - private collectionFactory: ICollectionFactory; - - constructor(collectionFactory: ICollectionFactory) { - this.collectionFactory = collectionFactory; - - this.u2fSecretCollection = this.collectionFactory.build(U2F_REGISTRATIONS_COLLECTION_NAME); - this.identityCheckTokensCollection = this.collectionFactory.build(IDENTITY_VALIDATION_TOKENS_COLLECTION_NAME); - this.authenticationTracesCollection = this.collectionFactory.build(AUTHENTICATION_TRACES_COLLECTION_NAME); - this.totpSecretCollection = this.collectionFactory.build(TOTP_SECRETS_COLLECTION_NAME); - } - - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise { - const newDocument: U2FRegistrationDocument = { - userId: userId, - appId: appId, - registration: registration - }; - - const filter: U2FRegistrationKey = { - userId: userId, - appId: appId - }; - - return this.u2fSecretCollection.update(filter, newDocument, { upsert: true }); - } - - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise { - const filter: U2FRegistrationKey = { - userId: userId, - appId: appId - }; - return this.u2fSecretCollection.findOne(filter); - } - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - const newDocument: AuthenticationTraceDocument = { - userId: userId, - date: new Date(), - isAuthenticationSuccessful: isAuthenticationSuccessful, - }; - - return this.authenticationTracesCollection.insert(newDocument); - } - - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise { - const q = { - userId: userId - }; - - return this.authenticationTracesCollection.find(q, { date: -1 }, count); - } - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise { - const newDocument: IdentityValidationDocument = { - userId: userId, - token: token, - challenge: challenge, - maxDate: new Date(new Date().getTime() + maxAge) - }; - - return this.identityCheckTokensCollection.insert(newDocument); - } - - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise { - const that = this; - const filter = { - token: token, - challenge: challenge - }; - - let identityValidationDocument: IdentityValidationDocument; - - return this.identityCheckTokensCollection.findOne(filter) - .then(function (doc: IdentityValidationDocument) { - if (!doc) { - return BluebirdPromise.reject(new Error("Registration token does not exist")); - } - - identityValidationDocument = doc; - const current_date = new Date(); - if (current_date > doc.maxDate) - return BluebirdPromise.reject(new Error("Registration token is not valid anymore")); - - return that.identityCheckTokensCollection.remove(filter); - }) - .then(() => { - return BluebirdPromise.resolve(identityValidationDocument); - }); - } - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise { - const doc = { - userId: userId, - secret: secret - }; - - const filter = { - userId: userId - }; - return this.totpSecretCollection.update(filter, doc, { upsert: true }); - } - - retrieveTOTPSecret(userId: string): BluebirdPromise { - const filter = { - userId: userId - }; - return this.totpSecretCollection.findOne(filter); - } -} diff --git a/themes/main/server/src/lib/storage/UserDataStoreStub.spec.ts b/themes/main/server/src/lib/storage/UserDataStoreStub.spec.ts deleted file mode 100644 index 5ea27a2de..000000000 --- a/themes/main/server/src/lib/storage/UserDataStoreStub.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); - -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; -import { IUserDataStore } from "./IUserDataStore"; - -export class UserDataStoreStub implements IUserDataStore { - saveU2FRegistrationStub: Sinon.SinonStub; - retrieveU2FRegistrationStub: Sinon.SinonStub; - saveAuthenticationTraceStub: Sinon.SinonStub; - retrieveLatestAuthenticationTracesStub: Sinon.SinonStub; - produceIdentityValidationTokenStub: Sinon.SinonStub; - consumeIdentityValidationTokenStub: Sinon.SinonStub; - saveTOTPSecretStub: Sinon.SinonStub; - retrieveTOTPSecretStub: Sinon.SinonStub; - - constructor() { - this.saveU2FRegistrationStub = Sinon.stub(); - this.retrieveU2FRegistrationStub = Sinon.stub(); - this.saveAuthenticationTraceStub = Sinon.stub(); - this.retrieveLatestAuthenticationTracesStub = Sinon.stub(); - this.produceIdentityValidationTokenStub = Sinon.stub(); - this.consumeIdentityValidationTokenStub = Sinon.stub(); - this.saveTOTPSecretStub = Sinon.stub(); - this.retrieveTOTPSecretStub = Sinon.stub(); - } - - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise { - return this.saveU2FRegistrationStub(userId, appId, registration); - } - - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise { - return this.retrieveU2FRegistrationStub(userId, appId); - } - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - return this.saveAuthenticationTraceStub(userId, isAuthenticationSuccessful); - } - - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise { - return this.retrieveLatestAuthenticationTracesStub(userId, count); - } - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise { - return this.produceIdentityValidationTokenStub(userId, token, challenge, maxAge); - } - - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise { - return this.consumeIdentityValidationTokenStub(token, challenge); - } - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise { - return this.saveTOTPSecretStub(userId, secret); - } - - retrieveTOTPSecret(userId: string): BluebirdPromise { - return this.retrieveTOTPSecretStub(userId); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/mongo/MongoCollection.spec.ts b/themes/main/server/src/lib/storage/mongo/MongoCollection.spec.ts deleted file mode 100644 index 74a773a1e..000000000 --- a/themes/main/server/src/lib/storage/mongo/MongoCollection.spec.ts +++ /dev/null @@ -1,110 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import MongoDB = require("mongodb"); -import BluebirdPromise = require("bluebird"); -import { MongoClientStub } from "../../connectors/mongo/MongoClientStub.spec"; -import { MongoCollection } from "./MongoCollection"; - -describe("storage/mongo/MongoCollection", function () { - let mongoCollectionStub: any; - let mongoClientStub: MongoClientStub; - let findStub: Sinon.SinonStub; - let findOneStub: Sinon.SinonStub; - let insertOneStub: Sinon.SinonStub; - let updateStub: Sinon.SinonStub; - let removeStub: Sinon.SinonStub; - let countStub: Sinon.SinonStub; - const COLLECTION_NAME = "collection"; - - before(function () { - mongoClientStub = new MongoClientStub(); - mongoCollectionStub = Sinon.createStubInstance(require("mongodb").Collection as any); - findStub = mongoCollectionStub.find as Sinon.SinonStub; - findOneStub = mongoCollectionStub.findOne as Sinon.SinonStub; - insertOneStub = mongoCollectionStub.insertOne as Sinon.SinonStub; - updateStub = mongoCollectionStub.update as Sinon.SinonStub; - removeStub = mongoCollectionStub.remove as Sinon.SinonStub; - countStub = mongoCollectionStub.count as Sinon.SinonStub; - mongoClientStub.collectionStub.returns( - BluebirdPromise.resolve(mongoCollectionStub) - ); - }); - - describe("find", function () { - it("should find a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - findStub.returns({ - sort: Sinon.stub().returns({ - limit: Sinon.stub().returns({ - toArray: Sinon.stub().returns(BluebirdPromise.resolve([])) - }) - }) - }); - - return collection.find({ key: "KEY" }) - .then(function () { - Assert(findStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("findOne", function () { - it("should find one document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - findOneStub.returns(BluebirdPromise.resolve({})); - - return collection.findOne({ key: "KEY" }) - .then(function () { - Assert(findOneStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("insert", function () { - it("should insert a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - insertOneStub.returns(BluebirdPromise.resolve({})); - - return collection.insert({ key: "KEY" }) - .then(function () { - Assert(insertOneStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("update", function () { - it("should update a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - updateStub.returns(BluebirdPromise.resolve({})); - - return collection.update({ key: "KEY" }, { key: "KEY", value: 1 }) - .then(function () { - Assert(updateStub.calledWith({ key: "KEY" }, { key: "KEY", value: 1 })); - }); - }); - }); - - describe("remove", function () { - it("should remove a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - removeStub.returns(BluebirdPromise.resolve({})); - - return collection.remove({ key: "KEY" }) - .then(function () { - Assert(removeStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("count", function () { - it("should count documents in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - countStub.returns(BluebirdPromise.resolve({})); - - return collection.count({ key: "KEY" }) - .then(function () { - Assert(countStub.calledWith({ key: "KEY" })); - }); - }); - }); -}); diff --git a/themes/main/server/src/lib/storage/mongo/MongoCollection.ts b/themes/main/server/src/lib/storage/mongo/MongoCollection.ts deleted file mode 100644 index 9771389f1..000000000 --- a/themes/main/server/src/lib/storage/mongo/MongoCollection.ts +++ /dev/null @@ -1,50 +0,0 @@ -import Bluebird = require("bluebird"); -import { ICollection } from "../ICollection"; -import MongoDB = require("mongodb"); -import { IMongoClient } from "../../connectors/mongo/IMongoClient"; - - -export class MongoCollection implements ICollection { - private mongoClient: IMongoClient; - private collectionName: string; - - constructor(collectionName: string, mongoClient: IMongoClient) { - this.collectionName = collectionName; - this.mongoClient = mongoClient; - } - - private collection(): Bluebird { - return this.mongoClient.collection(this.collectionName); - } - - find(query: any, sortKeys?: any, count?: number): Bluebird { - return this.collection() - .then((collection) => collection.find(query).sort(sortKeys).limit(count)) - .then((query) => query.toArray()); - } - - findOne(query: any): Bluebird { - return this.collection() - .then((collection) => collection.findOne(query)); - } - - update(query: any, updateQuery: any, options?: any): Bluebird { - return this.collection() - .then((collection) => collection.update(query, updateQuery, options)); - } - - remove(query: any): Bluebird { - return this.collection() - .then((collection) => collection.remove(query)); - } - - insert(document: any): Bluebird { - return this.collection() - .then((collection) => collection.insertOne(document)); - } - - count(query: any): Bluebird { - return this.collection() - .then((collection) => collection.count(query)); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts b/themes/main/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts deleted file mode 100644 index bd959cacb..000000000 --- a/themes/main/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import { MongoClientStub } from "../../connectors/mongo/MongoClientStub.spec"; -import { MongoCollectionFactory } from "./MongoCollectionFactory"; - -describe("storage/mongo/MongoCollectionFactory", function () { - let mongoClient: MongoClientStub; - - before(function() { - mongoClient = new MongoClientStub(); - }); - - describe("create", function () { - it("should create a collection", function () { - const COLLECTION_NAME = "COLLECTION_NAME"; - - const factory = new MongoCollectionFactory(mongoClient); - Assert(factory.build(COLLECTION_NAME)); - }); - }); -}); diff --git a/themes/main/server/src/lib/storage/mongo/MongoCollectionFactory.ts b/themes/main/server/src/lib/storage/mongo/MongoCollectionFactory.ts deleted file mode 100644 index 14a8262c9..000000000 --- a/themes/main/server/src/lib/storage/mongo/MongoCollectionFactory.ts +++ /dev/null @@ -1,19 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ICollection } from "../ICollection"; -import { ICollectionFactory } from "../ICollectionFactory"; -import { MongoCollection } from "./MongoCollection"; -import path = require("path"); -import MongoDB = require("mongodb"); -import { IMongoClient } from "../../connectors/mongo/IMongoClient"; - -export class MongoCollectionFactory implements ICollectionFactory { - private mongoClient: IMongoClient; - - constructor(mongoClient: IMongoClient) { - this.mongoClient = mongoClient; - } - - build(collectionName: string): ICollection { - return new MongoCollection(collectionName, this.mongoClient); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/nedb/NedbCollection.spec.ts b/themes/main/server/src/lib/storage/nedb/NedbCollection.spec.ts deleted file mode 100644 index a69962b67..000000000 --- a/themes/main/server/src/lib/storage/nedb/NedbCollection.spec.ts +++ /dev/null @@ -1,136 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); - -import { NedbCollection } from "./NedbCollection"; - -describe("storage/nedb/NedbCollection", function () { - describe("insert", function () { - it("should insert one entry", function () { - const nedbOptions = { - inMemoryOnly: true - }; - const collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - - return collection.count({}).then(function (count: number) { - Assert.equal(1, count); - }); - }); - - it("should insert three entries", function () { - const nedbOptions = { - inMemoryOnly: true - }; - const collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - collection.insert({ key: "hello" }); - collection.insert({ key: "hey" }); - - return collection.count({}).then(function (count: number) { - Assert.equal(3, count); - }); - }); - }); - - describe("find", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "hello" }); - collection.insert({ key: "hey" }); - collection.insert({ key: "coucou", value: 2 }); - }); - - it("should find one hello", function () { - return collection.find({ key: "hello" }, { key: 1 }) - .then(function (docs: { key: string }[]) { - Assert.equal(1, docs.length); - Assert(docs[0].key == "hello"); - }); - }); - - it("should find two coucou", function () { - return collection.find({ key: "coucou" }, { value: 1 }) - .then(function (docs: { value: number }[]) { - Assert.equal(2, docs.length); - }); - }); - }); - - describe("findOne", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - }); - - it("should find two coucou", function () { - const doc = { key: "coucou", value: 1 }; - return collection.count(doc) - .then(function (count: number) { - Assert.equal(4, count); - return collection.findOne(doc); - }); - }); - }); - - describe("update", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - }); - - it("should update the value", function () { - return collection.update({ key: "coucou" }, { key: "coucou", value: 2 }, { multi: true }) - .then(function () { - return collection.find({ key: "coucou" }); - }) - .then(function (docs: { key: string, value: number }[]) { - Assert.equal(1, docs.length); - Assert.equal(2, docs[0].value); - }); - }); - }); - - describe("update", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - collection.insert({ key: "hello" }); - }); - - it("should update the value", function () { - return collection.remove({ key: "coucou" }) - .then(function () { - return collection.count({}); - }) - .then(function (count: number) { - Assert.equal(1, count); - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/nedb/NedbCollection.ts b/themes/main/server/src/lib/storage/nedb/NedbCollection.ts deleted file mode 100644 index 88a93ad05..000000000 --- a/themes/main/server/src/lib/storage/nedb/NedbCollection.ts +++ /dev/null @@ -1,47 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ICollection } from "../ICollection"; -import Nedb = require("nedb"); - -declare module "nedb" { - export class NedbAsync extends Nedb { - constructor(pathOrOptions?: string | Nedb.DataStoreOptions); - updateAsync(query: any, updateQuery: any, options?: Nedb.UpdateOptions): BluebirdPromise; - findOneAsync(query: any): BluebirdPromise; - insertAsync(newDoc: T): BluebirdPromise; - removeAsync(query: any): BluebirdPromise; - countAsync(query: any): BluebirdPromise; - } -} - -export class NedbCollection implements ICollection { - private collection: Nedb.NedbAsync; - - constructor(options: Nedb.DataStoreOptions) { - this.collection = BluebirdPromise.promisifyAll(new Nedb(options)) as Nedb.NedbAsync; - } - - find(query: any, sortKeys?: any, count?: number): BluebirdPromise { - const q = this.collection.find(query).sort(sortKeys).limit(count); - return BluebirdPromise.promisify(q.exec, { context: q })(); - } - - findOne(query: any): BluebirdPromise { - return this.collection.findOneAsync(query); - } - - update(query: any, updateQuery: any, options?: any): BluebirdPromise { - return this.collection.updateAsync(query, updateQuery, options); - } - - remove(query: any): BluebirdPromise { - return this.collection.removeAsync(query); - } - - insert(document: any): BluebirdPromise { - return this.collection.insertAsync(document); - } - - count(query: any): BluebirdPromise { - return this.collection.countAsync(query); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts b/themes/main/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts deleted file mode 100644 index da90c661f..000000000 --- a/themes/main/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); - -import { NedbCollectionFactory } from "./NedbCollectionFactory"; - -describe("storage/nedb/NedbCollectionFactory", function() { - it("should create a nedb collection", function() { - const nedbOptions = { - inMemoryOnly: true - }; - const factory = new NedbCollectionFactory(nedbOptions); - - const collection = factory.build("mycollection"); - Assert(collection); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/storage/nedb/NedbCollectionFactory.ts b/themes/main/server/src/lib/storage/nedb/NedbCollectionFactory.ts deleted file mode 100644 index 49c4dc853..000000000 --- a/themes/main/server/src/lib/storage/nedb/NedbCollectionFactory.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ICollection } from "../ICollection"; -import { ICollectionFactory } from "../ICollectionFactory"; -import { NedbCollection } from "./NedbCollection"; -import path = require("path"); -import Nedb = require("nedb"); - -export interface NedbOptions { - inMemoryOnly?: boolean; - directory?: string; -} - -export class NedbCollectionFactory implements ICollectionFactory { - private options: Nedb.DataStoreOptions; - - constructor(options: Nedb.DataStoreOptions) { - this.options = options; - } - - build(collectionName: string): ICollection { - const datastoreOptions: Nedb.DataStoreOptions = { - inMemoryOnly: this.options.inMemoryOnly || false, - autoload: true, - filename: (this.options.filename) ? path.resolve(this.options.filename, collectionName) : undefined - }; - - return new NedbCollection(datastoreOptions); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/stubs/express.spec.ts b/themes/main/server/src/lib/stubs/express.spec.ts deleted file mode 100644 index 48f15d7e1..000000000 --- a/themes/main/server/src/lib/stubs/express.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ - -import sinon = require("sinon"); -import express = require("express"); - -export interface RequestMock { - app?: any; - body?: any; - session?: any; - headers?: any; - get?: any; - query?: any; - originalUrl: string; -} - -export interface ResponseMock { - send: sinon.SinonStub | sinon.SinonSpy; - sendStatus: sinon.SinonStub; - sendFile: sinon.SinonStub; - sendfile: sinon.SinonStub; - status: sinon.SinonStub | sinon.SinonSpy; - json: sinon.SinonStub | sinon.SinonSpy; - links: sinon.SinonStub; - jsonp: sinon.SinonStub; - download: sinon.SinonStub; - contentType: sinon.SinonStub; - type: sinon.SinonStub; - format: sinon.SinonStub; - attachment: sinon.SinonStub; - set: sinon.SinonStub; - header: sinon.SinonStub; - headersSent: boolean; - get: sinon.SinonStub; - clearCookie: sinon.SinonStub; - cookie: sinon.SinonStub; - location: sinon.SinonStub; - redirect: sinon.SinonStub | sinon.SinonSpy; - render: sinon.SinonStub | sinon.SinonSpy; - locals: sinon.SinonStub; - charset: string; - vary: sinon.SinonStub; - app: any; - write: sinon.SinonStub; - writeContinue: sinon.SinonStub; - writeHead: sinon.SinonStub; - statusCode: number; - statusMessage: string; - setHeader: sinon.SinonStub; - setTimeout: sinon.SinonStub; - sendDate: boolean; - getHeader: sinon.SinonStub; -} - -export function RequestMock(): RequestMock { - return { - originalUrl: "/non-api/xxx", - app: { - get: sinon.stub() - }, - headers: { - "x-forwarded-for": "127.0.0.1" - }, - session: {} - }; -} -export function ResponseMock(): ResponseMock { - return { - send: sinon.stub(), - status: sinon.stub(), - json: sinon.stub(), - sendStatus: sinon.stub(), - links: sinon.stub(), - jsonp: sinon.stub(), - sendFile: sinon.stub(), - sendfile: sinon.stub(), - download: sinon.stub(), - contentType: sinon.stub(), - type: sinon.stub(), - format: sinon.stub(), - attachment: sinon.stub(), - set: sinon.stub(), - header: sinon.stub(), - headersSent: true, - get: sinon.stub(), - clearCookie: sinon.stub(), - cookie: sinon.stub(), - location: sinon.stub(), - redirect: sinon.stub(), - render: sinon.stub(), - locals: sinon.stub(), - charset: "utf-8", - vary: sinon.stub(), - app: sinon.stub(), - write: sinon.stub(), - writeContinue: sinon.stub(), - writeHead: sinon.stub(), - statusCode: 200, - statusMessage: "message", - setHeader: sinon.stub(), - setTimeout: sinon.stub(), - sendDate: true, - getHeader: sinon.stub() - }; -} diff --git a/themes/main/server/src/lib/stubs/ldapjs.spec.ts b/themes/main/server/src/lib/stubs/ldapjs.spec.ts deleted file mode 100644 index 045c0e11b..000000000 --- a/themes/main/server/src/lib/stubs/ldapjs.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ - -import Sinon = require("sinon"); - -export class LdapjsMock { - createClientStub: sinon.SinonStub; - - constructor() { - this.createClientStub = Sinon.stub(); - } - - createClient(params: any) { - return this.createClientStub(params); - } -} - -export class LdapjsClientMock { - bindStub: sinon.SinonStub; - unbindStub: sinon.SinonStub; - searchStub: sinon.SinonStub; - modifyStub: sinon.SinonStub; - onStub: sinon.SinonStub; - - constructor() { - this.bindStub = Sinon.stub(); - this.unbindStub = Sinon.stub(); - this.searchStub = Sinon.stub(); - this.modifyStub = Sinon.stub(); - this.onStub = Sinon.stub(); - } - - bind() { - return this.bindStub(); - } - - unbind() { - return this.unbindStub(); - } - - search() { - return this.searchStub(); - } - - modify() { - return this.modifyStub(); - } - - on() { - return this.onStub(); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/stubs/speakeasy.spec.ts b/themes/main/server/src/lib/stubs/speakeasy.spec.ts deleted file mode 100644 index 023614dcd..000000000 --- a/themes/main/server/src/lib/stubs/speakeasy.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import sinon = require("sinon"); - -export = { - totp: sinon.stub(), - generateSecret: sinon.stub() -}; diff --git a/themes/main/server/src/lib/stubs/u2f.spec.ts b/themes/main/server/src/lib/stubs/u2f.spec.ts deleted file mode 100644 index 234b28c11..000000000 --- a/themes/main/server/src/lib/stubs/u2f.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ - -import sinon = require("sinon"); - -export interface U2FMock { - request: sinon.SinonStub; - checkSignature: sinon.SinonStub; - checkRegistration: sinon.SinonStub; -} - -export function U2FMock(): U2FMock { - return { - request: sinon.stub(), - checkSignature: sinon.stub(), - checkRegistration: sinon.stub() - }; -} diff --git a/themes/main/server/src/lib/utils/HashGenerator.spec.ts b/themes/main/server/src/lib/utils/HashGenerator.spec.ts deleted file mode 100644 index f19619a65..000000000 --- a/themes/main/server/src/lib/utils/HashGenerator.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Assert = require("assert"); -import { HashGenerator } from "./HashGenerator"; - -describe("utils/HashGenerator", function () { - it("should compute correct ssha512 (password)", function () { - return HashGenerator.ssha512("password", 500000, "jgiCMRyGXzoqpxS3") - .then(function (hash: string) { - Assert.equal(hash, "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"); - }); - }); - - it("should compute correct ssha512 (test)", function () { - return HashGenerator.ssha512("test", 500000, "abcdefghijklmnop") - .then(function (hash: string) { - Assert.equal(hash, "{CRYPT}$6$rounds=500000$abcdefghijklmnop$sTlNGf0VO/HTQIOXemmaBbV28HUch/qhWOA1/4dsDj6CDQYhUgXbYSPL6gccAsWMr2zD5fFWwhKmPdG.yxphs."); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/utils/HashGenerator.ts b/themes/main/server/src/lib/utils/HashGenerator.ts deleted file mode 100644 index e67de32b7..000000000 --- a/themes/main/server/src/lib/utils/HashGenerator.ts +++ /dev/null @@ -1,23 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import RandomString = require("randomstring"); -import Util = require("util"); -const crypt = require("crypt3"); - -export class HashGenerator { - static ssha512( - password: string, - rounds: number = 500000, - salt?: string): BluebirdPromise { - const saltSize = 16; - // $6 means SHA512 - const _salt = Util.format("$6$rounds=%d$%s", rounds, - (salt) ? salt : RandomString.generate(16)); - - const cryptAsync = BluebirdPromise.promisify(crypt); - - return cryptAsync(password, _salt) - .then(function (hash: string) { - return BluebirdPromise.resolve(Util.format("{CRYPT}%s", hash)); - }); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/utils/ObjectCloner.ts b/themes/main/server/src/lib/utils/ObjectCloner.ts deleted file mode 100644 index 3e125d749..000000000 --- a/themes/main/server/src/lib/utils/ObjectCloner.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export class ObjectCloner { - static clone(obj: any): any { - return JSON.parse(JSON.stringify(obj)); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/utils/SafeRedirection.spec.ts b/themes/main/server/src/lib/utils/SafeRedirection.spec.ts deleted file mode 100644 index 4126949fd..000000000 --- a/themes/main/server/src/lib/utils/SafeRedirection.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import { SafeRedirector } from "./SafeRedirection"; - -describe("web_server/middlewares/SafeRedirection", () => { - describe("Url is in protected domain", () => { - before(() => { - this.redirector = new SafeRedirector("example.com"); - this.res = {redirect: Sinon.stub()}; - }); - - it("should redirect to provided url", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.example.com:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://mysubdomain.example.com:8080/abc")); - }); - - it("should redirect to default url when wrong domain", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.domain.rtf:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://authelia.example.com")); - }); - - it("should redirect to default url when not terminating by domain", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.example.com.rtf:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://authelia.example.com")); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/utils/SafeRedirection.ts b/themes/main/server/src/lib/utils/SafeRedirection.ts deleted file mode 100644 index 9e6a32e0c..000000000 --- a/themes/main/server/src/lib/utils/SafeRedirection.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Express = require("express"); -import { DomainExtractor } from "../../../../shared/DomainExtractor"; -import { BelongToDomain } from "../../../../shared/BelongToDomain"; - - -export class SafeRedirector { - private domain: string; - - constructor(domain: string) { - this.domain = domain; - } - - redirectOrElse( - res: Express.Response, - url: string, - defaultUrl: string): void { - if (BelongToDomain(url, this.domain)) { - res.redirect(url); - } - res.redirect(defaultUrl); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/utils/URLDecomposer.spec.ts b/themes/main/server/src/lib/utils/URLDecomposer.spec.ts deleted file mode 100644 index cbb038738..000000000 --- a/themes/main/server/src/lib/utils/URLDecomposer.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { URLDecomposer } from "./URLDecomposer"; -import Assert = require("assert"); - -describe("utils/URLDecomposer", function () { - describe("test fromUrl", function () { - it("should return domain from https url", function () { - const d = URLDecomposer.fromUrl("https://www.example.com/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return domain from http url", function () { - const d = URLDecomposer.fromUrl("http://www.example.com/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return domain when url contains port", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return default path when no path provided", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/"); - }); - - it("should return default path when provided", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080/"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/"); - }); - - it("should return undefined when does not match", function () { - const d = URLDecomposer.fromUrl("https:///abc/test"); - Assert.equal(d, undefined); - }); - - it("should return undefined when does not match", function () { - const d = URLDecomposer.fromUrl("https:///abc/test"); - Assert.equal(d, undefined); - }); - }); -}); \ No newline at end of file diff --git a/themes/main/server/src/lib/utils/URLDecomposer.ts b/themes/main/server/src/lib/utils/URLDecomposer.ts deleted file mode 100644 index 9bdf2e9d2..000000000 --- a/themes/main/server/src/lib/utils/URLDecomposer.ts +++ /dev/null @@ -1,15 +0,0 @@ -export class URLDecomposer { - static fromUrl(url: string): {domain: string, path: string} { - if (!url) return; - const match = url.match(/https?:\/\/([a-z0-9_.-]+)(:[0-9]+)?(.*)/); - - if (!match) return; - - if (match[1] && !match[3]) { - return {domain: match[1], path: "/"}; - } else if (match[1] && match[3]) { - return {domain: match[1], path: match[3]}; - } - return; - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/web_server/Configurator.ts b/themes/main/server/src/lib/web_server/Configurator.ts deleted file mode 100644 index 6e404874a..000000000 --- a/themes/main/server/src/lib/web_server/Configurator.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Configuration } from "../configuration/schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; -import { SessionConfigurationBuilder } from - "../configuration/SessionConfigurationBuilder"; -import Path = require("path"); -import Express = require("express"); -import * as BodyParser from "body-parser"; -import { RestApi } from "./RestApi"; -import { WithHeadersLogged } from "./middlewares/WithHeadersLogged"; -import { ServerVariables } from "../ServerVariables"; -import Helmet = require("helmet"); - -const addRequestId = require("express-request-id")(); - -// Constants -const TRUST_PROXY = "trust proxy"; -const X_POWERED_BY = "x-powered-by"; -const VIEWS = "views"; -const VIEW_ENGINE = "view engine"; -const PUG = "pug"; - -export class Configurator { - static configure(config: Configuration, - app: Express.Application, - vars: ServerVariables, - deps: GlobalDependencies): void { - const viewsDirectory = Path.resolve(__dirname, "../../views"); - const publicHtmlDirectory = Path.resolve(__dirname, "../../public_html"); - - const expressSessionOptions = SessionConfigurationBuilder.build(config, deps); - - app.use(Express.static(publicHtmlDirectory)); - app.use(BodyParser.urlencoded({ extended: false })); - app.use(BodyParser.json()); - app.use(deps.session(expressSessionOptions)); - app.use(addRequestId); - app.use(WithHeadersLogged.middleware(vars.logger)); - app.disable(X_POWERED_BY); - app.enable(TRUST_PROXY); - app.use(Helmet()); - - app.set(VIEWS, viewsDirectory); - app.set(VIEW_ENGINE, PUG); - - RestApi.setup(app, vars); - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/web_server/RestApi.ts b/themes/main/server/src/lib/web_server/RestApi.ts deleted file mode 100644 index 9144a15b9..000000000 --- a/themes/main/server/src/lib/web_server/RestApi.ts +++ /dev/null @@ -1,125 +0,0 @@ -import Express = require("express"); - -import FirstFactorGet = require("../routes/firstfactor/get"); -import SecondFactorGet = require("../routes/secondfactor/get"); - -import FirstFactorPost = require("../routes/firstfactor/post"); -import LogoutGet = require("../routes/logout/get"); -import VerifyGet = require("../routes/verify/get"); -import TOTPSignGet = require("../routes/secondfactor/totp/sign/post"); - -import IdentityCheckMiddleware = require("../IdentityCheckMiddleware"); - -import TOTPRegistrationIdentityHandler from "../routes/secondfactor/totp/identity/RegistrationHandler"; -import U2FRegistrationIdentityHandler from "../routes/secondfactor/u2f/identity/RegistrationHandler"; -import ResetPasswordIdentityHandler from "../routes/password-reset/identity/PasswordResetHandler"; - -import U2FSignPost = require("../routes/secondfactor/u2f/sign/post"); -import U2FSignRequestGet = require("../routes/secondfactor/u2f/sign_request/get"); - -import U2FRegisterPost = require("../routes/secondfactor/u2f/register/post"); -import U2FRegisterRequestGet = require("../routes/secondfactor/u2f/register_request/get"); - -import ResetPasswordFormPost = require("../routes/password-reset/form/post"); -import ResetPasswordRequestPost = require("../routes/password-reset/request/get"); - -import Error401Get = require("../routes/error/401/get"); -import Error403Get = require("../routes/error/403/get"); -import Error404Get = require("../routes/error/404/get"); - -import LoggedIn = require("../routes/loggedin/get"); - -import { ServerVariables } from "../ServerVariables"; -import Endpoints = require("../../../../shared/api"); -import { RequireValidatedFirstFactor } from "./middlewares/RequireValidatedFirstFactor"; - -function setupTotp(app: Express.Application, vars: ServerVariables) { - app.post(Endpoints.SECOND_FACTOR_TOTP_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - TOTPSignGet.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - app.get(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - IdentityCheckMiddleware.register(app, - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET, - new TOTPRegistrationIdentityHandler(vars.logger, - vars.userDataStore, vars.totpHandler, vars.config.totp), - vars); -} - -function setupU2f(app: Express.Application, vars: ServerVariables) { - app.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FSignRequestGet.default(vars)); - - app.post(Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FSignPost.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FRegisterRequestGet.default(vars)); - - app.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FRegisterPost.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - app.get(Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - IdentityCheckMiddleware.register(app, - Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, - Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, - new U2FRegistrationIdentityHandler(vars.logger), vars); -} - -function setupResetPassword(app: Express.Application, vars: ServerVariables) { - IdentityCheckMiddleware.register(app, - Endpoints.RESET_PASSWORD_IDENTITY_START_GET, - Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET, - new ResetPasswordIdentityHandler(vars.logger, vars.usersDatabase), - vars); - - app.get(Endpoints.RESET_PASSWORD_REQUEST_GET, - ResetPasswordRequestPost.default); - app.post(Endpoints.RESET_PASSWORD_FORM_POST, - ResetPasswordFormPost.default(vars)); -} - -function setupErrors(app: Express.Application, vars: ServerVariables) { - app.get(Endpoints.ERROR_401_GET, Error401Get.default(vars)); - app.get(Endpoints.ERROR_403_GET, Error403Get.default(vars)); - app.get(Endpoints.ERROR_404_GET, Error404Get.default); -} - -export class RestApi { - static setup(app: Express.Application, vars: ServerVariables): void { - app.get(Endpoints.FIRST_FACTOR_GET, FirstFactorGet.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - SecondFactorGet.default(vars)); - - app.get(Endpoints.LOGOUT_GET, LogoutGet.default(vars)); - - app.get(Endpoints.VERIFY_GET, VerifyGet.default(vars)); - app.post(Endpoints.FIRST_FACTOR_POST, FirstFactorPost.default(vars)); - - setupTotp(app, vars); - setupU2f(app, vars); - setupResetPassword(app, vars); - setupErrors(app, vars); - - app.get(Endpoints.LOGGED_IN, - RequireValidatedFirstFactor.middleware(vars.logger), - LoggedIn.default(vars)); - } -} diff --git a/themes/main/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts b/themes/main/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts deleted file mode 100644 index ecfd75765..000000000 --- a/themes/main/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts +++ /dev/null @@ -1,27 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import ErrorReplies = require("../../ErrorReplies"); -import { IRequestLogger } from "../../logging/IRequestLogger"; -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Exceptions = require("../../Exceptions"); -import { Level } from "../../authentication/Level"; - -export class RequireValidatedFirstFactor { - static middleware(logger: IRequestLogger) { - return function (req: Express.Request, res: Express.Response, - next: Express.NextFunction): BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, logger); - if (!authSession.userid || authSession.authentication_level < Level.ONE_FACTOR) - return reject( - new Exceptions.FirstFactorValidationError( - "First factor has not been validated yet.")); - - next(); - resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, logger)); - }; - } -} \ No newline at end of file diff --git a/themes/main/server/src/lib/web_server/middlewares/WithHeadersLogged.ts b/themes/main/server/src/lib/web_server/middlewares/WithHeadersLogged.ts deleted file mode 100644 index 139db1141..000000000 --- a/themes/main/server/src/lib/web_server/middlewares/WithHeadersLogged.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Express = require("express"); -import { IRequestLogger } from "../../logging/IRequestLogger"; - -export class WithHeadersLogged { - static middleware(logger: IRequestLogger) { - return function (req: Express.Request, res: Express.Response, - next: Express.NextFunction): void { - logger.debug(req, "Headers = %s", JSON.stringify(req.headers)); - next(); - }; - } -} \ No newline at end of file diff --git a/themes/main/server/test/requests.ts b/themes/main/server/test/requests.ts deleted file mode 100644 index 93fa0de47..000000000 --- a/themes/main/server/test/requests.ts +++ /dev/null @@ -1,94 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import request = require("request"); -import assert = require("assert"); -import express = require("express"); -import nodemailer = require("nodemailer"); -import Endpoints = require("../../shared/api"); - -declare module "request" { - export interface RequestAPI { - getAsync(uri: string, options?: RequiredUriUrl): BluebirdPromise; - getAsync(uri: string): BluebirdPromise; - getAsync(options: RequiredUriUrl & CoreOptions): BluebirdPromise; - - postAsync(uri: string, options?: CoreOptions): BluebirdPromise; - postAsync(uri: string): BluebirdPromise; - postAsync(options: RequiredUriUrl & CoreOptions): BluebirdPromise; - } -} - -const requestAsync: typeof request = BluebirdPromise.promisifyAll(request) as typeof request; - -export = function (port: number) { - const PORT = port; - const BASE_URL = "http://localhost:" + PORT; - - function execute_totp(jar: request.CookieJar, token: string) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_TOTP_POST, - jar: jar, - form: { - token: token - } - }); - } - - function execute_u2f_authentication(jar: request.CookieJar) { - return requestAsync.getAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, - jar: jar - }) - .then(function (res: request.RequestResponse) { - assert.equal(res.statusCode, 200); - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - jar: jar, - form: { - } - }); - }); - } - - function execute_verification(jar: request.CookieJar) { - return requestAsync.getAsync({ url: BASE_URL + Endpoints.VERIFY_GET, jar: jar }); - } - - function execute_login(jar: request.CookieJar) { - return requestAsync.getAsync({ url: BASE_URL + Endpoints.FIRST_FACTOR_GET, jar: jar }); - } - - function execute_first_factor(jar: request.CookieJar) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.FIRST_FACTOR_POST, - jar: jar, - form: { - username: "test_ok", - password: "password" - } - }); - } - - function execute_failing_first_factor(jar: request.CookieJar) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.FIRST_FACTOR_POST, - jar: jar, - form: { - username: "test_nok", - password: "password" - } - }); - } - - return { - login: execute_login, - verify: execute_verification, - u2f_authentication: execute_u2f_authentication, - first_factor: execute_first_factor, - failing_first_factor: execute_failing_first_factor, - totp: execute_totp, - }; -}; - diff --git a/themes/main/server/tsconfig.json b/themes/main/server/tsconfig.json deleted file mode 100644 index ebe98c5ed..000000000 --- a/themes/main/server/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "moduleResolution": "node", - "noImplicitAny": true, - "sourceMap": true, - "removeComments": true, - "outDir": "../dist", - "baseUrl": ".", - "paths": { - "*": [ - "./types/*", - "../shared/types/*" - ] - } - }, - "exclude": [ - "src/**/*.spec.ts" - ] -} diff --git a/themes/main/server/tslint.json b/themes/main/server/tslint.json deleted file mode 100644 index c2c1b7501..000000000 --- a/themes/main/server/tslint.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "no-var-keyword": true, - "quotemark": [ - true, - "double", - "avoid-escape" - ], - "semicolon": [ - true, - "always", - "ignore-bound-class-methods" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-module", - "check-separator", - "check-type" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "onespace", - "index-signature": "onespace", - "parameter": "onespace", - "property-declaration": "onespace", - "variable-declaration": "onespace" - } - ], - "no-internal-module": true, - "no-trailing-whitespace": true, - "no-null-keyword": true, - "prefer-const": true, - "jsdoc-format": true - } -} diff --git a/themes/main/server/types/.directory b/themes/main/server/types/.directory deleted file mode 100644 index 63f8e11d5..000000000 --- a/themes/main/server/types/.directory +++ /dev/null @@ -1,4 +0,0 @@ -[Dolphin] -Timestamp=2018,12,17,20,58,28 -Version=3 -ViewMode=1 diff --git a/themes/main/server/types/AuthenticationSession.ts b/themes/main/server/types/AuthenticationSession.ts deleted file mode 100644 index bbed0e715..000000000 --- a/themes/main/server/types/AuthenticationSession.ts +++ /dev/null @@ -1,18 +0,0 @@ -import U2f = require("u2f"); -import { Level } from "../src/lib/authentication/Level"; - -export interface AuthenticationSession { - userid: string; - authentication_level: Level; - keep_me_logged_in: boolean; - last_activity_datetime: number; - identity_check?: { - challenge: string; - userid: string; - }; - register_request?: U2f.Request; - sign_request?: U2f.Request; - email: string; - groups: string[]; - redirect?: string; -} \ No newline at end of file diff --git a/themes/main/server/types/Dependencies.ts b/themes/main/server/types/Dependencies.ts deleted file mode 100644 index f20404dbc..000000000 --- a/themes/main/server/types/Dependencies.ts +++ /dev/null @@ -1,29 +0,0 @@ -import winston = require("winston"); -import speakeasy = require("speakeasy"); -import nodemailer = require("nodemailer"); -import session = require("express-session"); -import nedb = require("nedb"); -import ldapjs = require("ldapjs"); -import u2f = require("u2f"); -import RedisSession = require("connect-redis"); -import Redis = require("redis"); - -export type Speakeasy = typeof speakeasy; -export type Winston = typeof winston; -export type Session = typeof session; -export type Nedb = typeof nedb; -export type Ldapjs = typeof ldapjs; -export type U2f = typeof u2f; -export type ConnectRedis = typeof RedisSession; -export type Redis = typeof Redis; - -export interface GlobalDependencies { - u2f: U2f; - ldapjs: Ldapjs; - session: Session; - Redis: Redis; - ConnectRedis: ConnectRedis; - winston: Winston; - speakeasy: Speakeasy; - nedb: Nedb; -} \ No newline at end of file diff --git a/themes/main/server/types/Identity.ts b/themes/main/server/types/Identity.ts deleted file mode 100644 index e985984e2..000000000 --- a/themes/main/server/types/Identity.ts +++ /dev/null @@ -1,6 +0,0 @@ - - -export interface Identity { - userid: string; - email: string; -} \ No newline at end of file diff --git a/themes/main/server/types/TOTPSecret.ts b/themes/main/server/types/TOTPSecret.ts deleted file mode 100644 index d6775f2f0..000000000 --- a/themes/main/server/types/TOTPSecret.ts +++ /dev/null @@ -1,11 +0,0 @@ - -export interface TOTPSecret { - ascii: string; - hex: string; - base32: string; - qr_code_ascii: string; - qr_code_hex: string; - qr_code_base32: string; - google_auth_qr: string; - otpauth_url: string; - } \ No newline at end of file diff --git a/themes/main/server/types/U2FRegistration.ts b/themes/main/server/types/U2FRegistration.ts deleted file mode 100644 index b6080af07..000000000 --- a/themes/main/server/types/U2FRegistration.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface U2FRegistration { - keyHandle: string; - publicKey: string; -} \ No newline at end of file diff --git a/themes/main/server/types/dovehash.d.ts b/themes/main/server/types/dovehash.d.ts deleted file mode 100644 index c354609c0..000000000 --- a/themes/main/server/types/dovehash.d.ts +++ /dev/null @@ -1,4 +0,0 @@ - -declare module "dovehash" { - function encode(algo: string, text: string): string; -} \ No newline at end of file diff --git a/themes/main/server/types/speakeasy.d.ts b/themes/main/server/types/speakeasy.d.ts deleted file mode 100644 index 6ea06948b..000000000 --- a/themes/main/server/types/speakeasy.d.ts +++ /dev/null @@ -1,96 +0,0 @@ -declare module "speakeasy" { - export = speakeasy - - interface SharedOptions { - encoding?: string - algorithm?: string - } - - interface DigestOptions extends SharedOptions { - secret: string - counter: number - } - - interface HOTPOptions extends SharedOptions { - secret: string - counter: number - digest?: Buffer - digits?: number - } - - interface HOTPVerifyOptions extends SharedOptions { - secret: string - token: string - counter: number - digits?: number - window?: number - } - - interface TOTPOptions extends SharedOptions { - secret: string - time?: number - step?: number - epoch?: number - counter?: number - digits?: number - } - - interface TOTPVerifyOptions extends SharedOptions { - secret: string - token: string - time?: number - step?: number - epoch?: number - counter?: number - digits?: number - window?: number - } - - interface GenerateSecretOptions { - length?: number - symbols?: boolean - otpauth_url?: boolean - name?: string - issuer?: string - } - - interface GeneratedSecret { - ascii: string - hex: string - base32: string - qr_code_ascii: string - qr_code_hex: string - qr_code_base32: string - google_auth_qr: string - otpauth_url: string - } - - interface OTPAuthURLOptions extends SharedOptions { - secret: string - label: string - type?: string - counter?: number - issuer?: string - digits?: number - period?: number - } - - interface Speakeasy { - digest: (options: DigestOptions) => Buffer - hotp: { - (options: HOTPOptions): string, - verifyDelta: (options: HOTPVerifyOptions) => boolean, - verify: (options: HOTPVerifyOptions) => boolean, - } - totp: { - (options: TOTPOptions): string - verifyDelta: (options: TOTPVerifyOptions) => boolean, - verify: (options: TOTPVerifyOptions) => boolean, - } - generateSecret: (options?: GenerateSecretOptions) => GeneratedSecret - generateSecretASCII: (length?: number, symbols?: boolean) => string - otpauthURL: (options: OTPAuthURLOptions) => string - } - - const speakeasy: Speakeasy -} \ No newline at end of file diff --git a/themes/matrix/client/src/css/.directory b/themes/matrix/client/src/css/.directory old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/css/00-bootstrap.min.css b/themes/matrix/client/src/css/00-bootstrap.min.css old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/css/01-main.css b/themes/matrix/client/src/css/01-main.css old mode 100644 new mode 100755 index 318b90e25..e62ff8dd0 --- a/themes/matrix/client/src/css/01-main.css +++ b/themes/matrix/client/src/css/01-main.css @@ -2,7 +2,7 @@ body { /*background-image: url("//*img//*LargeTriangles.svg");*/ /*background-image: url("//*img//*RandomizedPattern.svg");*/ /*background-image: url("//*img//*background.svg");*/ - background-color:#000000;*/ + background-color:#000000; } canvas{ position:absolute; diff --git a/themes/matrix/client/src/css/02-login.css b/themes/matrix/client/src/css/02-login.css old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/css/03-errors.css b/themes/matrix/client/src/css/03-errors.css old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/css/03-password-reset-form.css b/themes/matrix/client/src/css/03-password-reset-form.css old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/css/03-password-reset-request.css b/themes/matrix/client/src/css/03-password-reset-request.css old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/css/03-totp-register.css b/themes/matrix/client/src/css/03-totp-register.css old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/css/03-u2f-register.css b/themes/matrix/client/src/css/03-u2f-register.css old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/background.jpg b/themes/matrix/client/src/img/background.jpg old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/icon.png b/themes/matrix/client/src/img/icon.png old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/mail.png b/themes/matrix/client/src/img/mail.png old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/matrix_circle_128x128.png b/themes/matrix/client/src/img/matrix_circle_128x128.png old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/notifications/.directory b/themes/matrix/client/src/img/notifications/.directory old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/notifications/error.png b/themes/matrix/client/src/img/notifications/error.png old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/notifications/info.png b/themes/matrix/client/src/img/notifications/info.png old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/notifications/success.png b/themes/matrix/client/src/img/notifications/success.png old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/notifications/warning.png b/themes/matrix/client/src/img/notifications/warning.png old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/padlock.png b/themes/matrix/client/src/img/padlock.png old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/password_white.png b/themes/matrix/client/src/img/password_white.png old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/pendrive.png b/themes/matrix/client/src/img/pendrive.png old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/stores/.directory b/themes/matrix/client/src/img/stores/.directory old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/stores/applestore-badge.svg b/themes/matrix/client/src/img/stores/applestore-badge.svg old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/stores/googleplay-badge.svg b/themes/matrix/client/src/img/stores/googleplay-badge.svg old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/success.png b/themes/matrix/client/src/img/success.png old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/user.png b/themes/matrix/client/src/img/user.png old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/img/warning.png b/themes/matrix/client/src/img/warning.png old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/index.ts b/themes/matrix/client/src/index.ts deleted file mode 100644 index 802004a8f..000000000 --- a/themes/matrix/client/src/index.ts +++ /dev/null @@ -1,34 +0,0 @@ - -import FirstFactorValidator = require("./lib/firstfactor/FirstFactorValidator"); - -import FirstFactor from "./lib/firstfactor/index"; -import SecondFactor from "./lib/secondfactor/index"; -import TOTPRegister from "./lib/totp-register/totp-register"; -import U2fRegister from "./lib/u2f-register/u2f-register"; -import ResetPasswordRequest from "./lib/reset-password/reset-password-request"; -import ResetPasswordForm from "./lib/reset-password/reset-password-form"; -import jslogger = require("js-logger"); -import jQuery = require("jquery"); -import Endpoints = require("../../shared/api"); - -jslogger.useDefaults(); -jslogger.setLevel(jslogger.INFO); - -(function () { - (window).jQuery = jQuery; - require("bootstrap"); - - jQuery('[data-toggle="tooltip"]').tooltip(); - if (window.location.pathname == Endpoints.FIRST_FACTOR_GET) - FirstFactor(window, jQuery, FirstFactorValidator, jslogger); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_GET) - SecondFactor(window, jQuery, (global as any).u2f); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET) - TOTPRegister(window, jQuery); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET) - U2fRegister(window, jQuery, (global as any).u2f); - else if (window.location.pathname == Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET) - ResetPasswordForm(window, jQuery); - else if (window.location.pathname == Endpoints.RESET_PASSWORD_REQUEST_GET) - ResetPasswordRequest(window, jQuery); -})(); diff --git a/themes/matrix/client/src/lib/GetPromised.ts b/themes/matrix/client/src/lib/GetPromised.ts deleted file mode 100644 index 779139654..000000000 --- a/themes/matrix/client/src/lib/GetPromised.ts +++ /dev/null @@ -1,14 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -export default function ($: JQueryStatic, url: string, data: Object, fn: any, - dataType: string): BluebirdPromise { - return new BluebirdPromise((resolve, reject) => { - $.get(url, {}, undefined, dataType) - .done((data: any) => { - resolve(data); - }) - .fail((xhr: JQueryXHR, textStatus: string) => { - reject(textStatus); - }); - }); -} \ No newline at end of file diff --git a/themes/matrix/client/src/lib/INotifier.ts b/themes/matrix/client/src/lib/INotifier.ts deleted file mode 100644 index df947538f..000000000 --- a/themes/matrix/client/src/lib/INotifier.ts +++ /dev/null @@ -1,14 +0,0 @@ - -declare type Handler = () => void; - -export interface Handlers { - onFadedIn: Handler; - onFadedOut: Handler; -} - -export interface INotifier { - success(msg: string, handlers?: Handlers): void; - error(msg: string, handlers?: Handlers): void; - warning(msg: string, handlers?: Handlers): void; - info(msg: string, handlers?: Handlers): void; -} \ No newline at end of file diff --git a/themes/matrix/client/src/lib/Notifier.ts b/themes/matrix/client/src/lib/Notifier.ts deleted file mode 100644 index c0252b9b9..000000000 --- a/themes/matrix/client/src/lib/Notifier.ts +++ /dev/null @@ -1,83 +0,0 @@ - - -import util = require("util"); -import { INotifier, Handlers } from "./INotifier"; - -class NotificationEvent { - private element: JQuery; - private message: string; - private statusType: string; - private timeoutId: any; - - constructor(element: JQuery, msg: string, statusType: string) { - this.message = msg; - this.statusType = statusType; - this.element = element; - } - - private clearNotification() { - this.element.removeClass(this.statusType); - this.element.html(""); - } - - start(handlers?: Handlers) { - const that = this; - const FADE_TIME = 500; - const html = util.format('status %s\ - %s', this.statusType, this.statusType, this.message); - this.element.html(html); - this.element.addClass(this.statusType); - this.element.fadeIn(FADE_TIME, function () { - if (handlers) - handlers.onFadedIn(); - }); - - this.timeoutId = setTimeout(function () { - that.element.fadeOut(FADE_TIME, function () { - that.clearNotification(); - if (handlers) - handlers.onFadedOut(); - }); - }, 4000); - } - - interrupt() { - this.clearNotification(); - this.element.hide(); - clearTimeout(this.timeoutId); - } -} - -export class Notifier implements INotifier { - private element: JQuery; - private onGoingEvent: NotificationEvent; - - constructor(selector: string, $: JQueryStatic) { - this.element = $(selector); - this.onGoingEvent = undefined; - } - - private displayAndFadeout(msg: string, statusType: string, handlers?: Handlers): void { - if (this.onGoingEvent) - this.onGoingEvent.interrupt(); - - this.onGoingEvent = new NotificationEvent(this.element, msg, statusType); - this.onGoingEvent.start(handlers); - } - - success(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "success", handlers); - } - - error(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "error", handlers); - } - - warning(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "warning", handlers); - } - - info(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "info", handlers); - } -} \ No newline at end of file diff --git a/themes/matrix/client/src/lib/QueryParametersRetriever.ts b/themes/matrix/client/src/lib/QueryParametersRetriever.ts deleted file mode 100644 index a529adb6e..000000000 --- a/themes/matrix/client/src/lib/QueryParametersRetriever.ts +++ /dev/null @@ -1,12 +0,0 @@ - -export class QueryParametersRetriever { - static get(name: string, url?: string): string { - if (!url) url = window.location.href; - name = name.replace(/[\[\]]/g, "\\$&"); - const regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), - results = regex.exec(url); - if (!results) return undefined; - if (!results[2]) return ""; - return decodeURIComponent(results[2].replace(/\+/g, " ")); - } -} \ No newline at end of file diff --git a/themes/matrix/client/src/lib/SafeRedirect.ts b/themes/matrix/client/src/lib/SafeRedirect.ts deleted file mode 100644 index 7e7684b8a..000000000 --- a/themes/matrix/client/src/lib/SafeRedirect.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BelongToDomain } from "../../../shared/BelongToDomain"; - -export function SafeRedirect(url: string, cb: () => void): void { - const domain = window.location.hostname.split(".").slice(-2).join("."); - if (url.startsWith("/") || BelongToDomain(url, domain)) { - window.location.href = url; - return; - } - cb(); -} \ No newline at end of file diff --git a/themes/matrix/client/src/lib/firstfactor/FirstFactorValidator.ts b/themes/matrix/client/src/lib/firstfactor/FirstFactorValidator.ts deleted file mode 100644 index eaa496fdd..000000000 --- a/themes/matrix/client/src/lib/firstfactor/FirstFactorValidator.ts +++ /dev/null @@ -1,46 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import Constants = require("../../../../shared/constants"); -import Util = require("util"); -import UserMessages = require("../../../../shared/UserMessages"); - -export function validate(username: string, password: string, - keepMeLoggedIn: boolean, redirectUrl: string, $: JQueryStatic) - : BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - let url: string; - if (redirectUrl != undefined) { - const redirectParam = Util.format("%s=%s", Constants.REDIRECT_QUERY_PARAM, redirectUrl); - url = Util.format("%s?%s", Endpoints.FIRST_FACTOR_POST, redirectParam); - } - else { - url = Util.format("%s", Endpoints.FIRST_FACTOR_POST); - } - - const data: any = { - username: username, - password: password, - }; - - if (keepMeLoggedIn) { - data.keepMeLoggedIn = "true"; - } - - $.ajax({ - method: "POST", - url: url, - data: data - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(body.redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(UserMessages.AUTHENTICATION_FAILED)); - }); - }); -} diff --git a/themes/matrix/client/src/lib/firstfactor/UISelectors.ts b/themes/matrix/client/src/lib/firstfactor/UISelectors.ts deleted file mode 100644 index 0e971b3c3..000000000 --- a/themes/matrix/client/src/lib/firstfactor/UISelectors.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export const USERNAME_FIELD_ID = "#username"; -export const PASSWORD_FIELD_ID = "#password"; -export const SIGN_IN_BUTTON_ID = "#signin"; -export const KEEP_ME_LOGGED_IN_ID = "#keep_me_logged_in"; diff --git a/themes/matrix/client/src/lib/firstfactor/index.ts b/themes/matrix/client/src/lib/firstfactor/index.ts deleted file mode 100644 index 24affee2d..000000000 --- a/themes/matrix/client/src/lib/firstfactor/index.ts +++ /dev/null @@ -1,49 +0,0 @@ -import FirstFactorValidator = require("./FirstFactorValidator"); -import JSLogger = require("js-logger"); -import UISelectors = require("./UISelectors"); -import { Notifier } from "../Notifier"; -import { QueryParametersRetriever } from "../QueryParametersRetriever"; -import Constants = require("../../../../shared/constants"); -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic, - firstFactorValidator: typeof FirstFactorValidator, jslogger: typeof JSLogger) { - - const notifier = new Notifier(".notification", $); - - function onFormSubmitted() { - const username: string = $(UISelectors.USERNAME_FIELD_ID).val() as string; - const password: string = $(UISelectors.PASSWORD_FIELD_ID).val() as string; - const keepMeLoggedIn: boolean = $(UISelectors.KEEP_ME_LOGGED_IN_ID).is(":checked"); - - $("form").css("opacity", 0.5); - $("input,button").attr("disabled", "true"); - $(UISelectors.SIGN_IN_BUTTON_ID).text("Please wait..."); - - const redirectUrl = QueryParametersRetriever.get(Constants.REDIRECT_QUERY_PARAM); - firstFactorValidator.validate(username, password, keepMeLoggedIn, redirectUrl, $) - .then(onFirstFactorSuccess, onFirstFactorFailure); - return false; - } - - function onFirstFactorSuccess(redirectUrl: string) { - SafeRedirect(redirectUrl, () => { - notifier.error("Cannot redirect to an external domain."); - }); - } - - function onFirstFactorFailure(err: Error) { - $("input,button").removeAttr("disabled"); - $("form").css("opacity", 1); - notifier.error(UserMessages.AUTHENTICATION_FAILED); - $(UISelectors.PASSWORD_FIELD_ID).select(); - $(UISelectors.SIGN_IN_BUTTON_ID).text("Sign in"); - } - - $(window.document).ready(function () { - $("form").on("submit", onFormSubmitted); - }); -} - diff --git a/themes/matrix/client/src/lib/reset-password/constants.ts b/themes/matrix/client/src/lib/reset-password/constants.ts deleted file mode 100644 index d48d4e67d..000000000 --- a/themes/matrix/client/src/lib/reset-password/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const FORM_SELECTOR = ".form-signin"; \ No newline at end of file diff --git a/themes/matrix/client/src/lib/reset-password/reset-password-form.ts b/themes/matrix/client/src/lib/reset-password/reset-password-form.ts deleted file mode 100644 index b94279cde..000000000 --- a/themes/matrix/client/src/lib/reset-password/reset-password-form.ts +++ /dev/null @@ -1,57 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); - -import Constants = require("./constants"); -import { Notifier } from "../Notifier"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function modifyPassword(newPassword: string) { - return new BluebirdPromise(function (resolve, reject) { - $.post(Endpoints.RESET_PASSWORD_FORM_POST, { - password: newPassword, - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(body); - }) - .fail(function (xhr, status) { - reject(status); - }); - }); - } - - function onFormSubmitted() { - const password1 = $("#password1").val() as string; - const password2 = $("#password2").val() as string; - - if (!password1 || !password2) { - notifier.warning(UserMessages.MISSING_PASSWORD); - return false; - } - - if (password1 != password2) { - notifier.warning(UserMessages.DIFFERENT_PASSWORDS); - return false; - } - - modifyPassword(password1) - .then(function () { - window.location.href = Endpoints.FIRST_FACTOR_GET; - }) - .error(function () { - notifier.error(UserMessages.RESET_PASSWORD_FAILED); - }); - return false; - } - - $(document).ready(function () { - $(Constants.FORM_SELECTOR).on("submit", onFormSubmitted); - }); -} diff --git a/themes/matrix/client/src/lib/reset-password/reset-password-request.ts b/themes/matrix/client/src/lib/reset-password/reset-password-request.ts deleted file mode 100644 index 846226d75..000000000 --- a/themes/matrix/client/src/lib/reset-password/reset-password-request.ts +++ /dev/null @@ -1,56 +0,0 @@ - -import BluebirdPromise = require("bluebird"); - -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import Constants = require("./constants"); -import jslogger = require("js-logger"); -import { Notifier } from "../Notifier"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function requestPasswordReset(username: string) { - return new BluebirdPromise(function (resolve, reject) { - $.get(Endpoints.RESET_PASSWORD_IDENTITY_START_GET, { - userid: username, - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); - } - - function onFormSubmitted() { - const username = $("#username").val() as string; - - if (!username) { - notifier.warning(UserMessages.MISSING_USERNAME); - return; - } - - requestPasswordReset(username) - .then(function () { - notifier.success(UserMessages.MAIL_SENT); - setTimeout(function () { - window.location.replace(Endpoints.FIRST_FACTOR_GET); - }, 1000); - }) - .error(function () { - notifier.error(UserMessages.MAIL_NOT_SENT); - }); - return false; - } - - $(document).ready(function () { - $(Constants.FORM_SELECTOR).on("submit", onFormSubmitted); - }); -} - diff --git a/themes/matrix/client/src/lib/secondfactor/TOTPValidator.ts b/themes/matrix/client/src/lib/secondfactor/TOTPValidator.ts deleted file mode 100644 index 5394139a6..000000000 --- a/themes/matrix/client/src/lib/secondfactor/TOTPValidator.ts +++ /dev/null @@ -1,28 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; - -export function validate(token: string, $: JQueryStatic): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.ajax({ - url: Endpoints.SECOND_FACTOR_TOTP_POST, - data: { - token: token, - }, - method: "POST", - dataType: "json" - } as JQueryAjaxSettings) - .done(function (body: RedirectionMessage | ErrorMessage) { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); -} \ No newline at end of file diff --git a/themes/matrix/client/src/lib/secondfactor/U2FValidator.ts b/themes/matrix/client/src/lib/secondfactor/U2FValidator.ts deleted file mode 100644 index 5812922f7..000000000 --- a/themes/matrix/client/src/lib/secondfactor/U2FValidator.ts +++ /dev/null @@ -1,42 +0,0 @@ -import U2f = require("u2f"); -import U2fApi from "u2f-api"; -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { INotifier } from "../INotifier"; -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; -import GetPromised from "../GetPromised"; - -function finishU2fAuthentication(responseData: U2fApi.SignResponse, - $: JQueryStatic): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.ajax({ - url: Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - data: responseData, - method: "POST", - dataType: "json" - } as JQueryAjaxSettings) - .done(function (body: RedirectionMessage | ErrorMessage) { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); -} - -export function validate($: JQueryStatic): BluebirdPromise { - return GetPromised($, Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, {}, - undefined, "json") - .then(function (signRequest: U2f.Request) { - return U2fApi.sign(signRequest, 60); - }) - .then(function (signResponse: U2fApi.SignResponse) { - return finishU2fAuthentication(signResponse, $); - }); -} diff --git a/themes/matrix/client/src/lib/secondfactor/constants.ts b/themes/matrix/client/src/lib/secondfactor/constants.ts deleted file mode 100644 index 50bba7571..000000000 --- a/themes/matrix/client/src/lib/secondfactor/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ - -export const TOTP_FORM_SELECTOR = ".form-signin.totp"; -export const TOTP_TOKEN_SELECTOR = ".form-signin #token"; diff --git a/themes/matrix/client/src/lib/secondfactor/index.ts b/themes/matrix/client/src/lib/secondfactor/index.ts deleted file mode 100644 index 279723dce..000000000 --- a/themes/matrix/client/src/lib/secondfactor/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import TOTPValidator = require("./TOTPValidator"); -import U2FValidator = require("./U2FValidator"); -import ClientConstants = require("./constants"); -import { Notifier } from "../Notifier"; -import { QueryParametersRetriever } from "../QueryParametersRetriever"; -import UserMessages = require("../../../../shared/UserMessages"); -import SharedConstants = require("../../../../shared/constants"); -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function onAuthenticationSuccess(serverRedirectUrl: string) { - const queryRedirectUrl = QueryParametersRetriever.get(SharedConstants.REDIRECT_QUERY_PARAM); - if (queryRedirectUrl) { - SafeRedirect(queryRedirectUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - } else if (serverRedirectUrl) { - SafeRedirect(serverRedirectUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - } else { - notifier.success(UserMessages.AUTHENTICATION_SUCCEEDED); - } - } - - function onSecondFactorTotpSuccess(redirectUrl: string) { - onAuthenticationSuccess(redirectUrl); - } - - function onSecondFactorTotpFailure(err: Error) { - notifier.error(UserMessages.AUTHENTICATION_TOTP_FAILED); - } - - function onU2fAuthenticationSuccess(redirectUrl: string) { - onAuthenticationSuccess(redirectUrl); - } - - function onU2fAuthenticationFailure() { - // TODO(clems4ever): we should not display this error message until a device - // is registered. - // notifier.error(UserMessages.AUTHENTICATION_U2F_FAILED); - } - - function onTOTPFormSubmitted(): boolean { - const token = $(ClientConstants.TOTP_TOKEN_SELECTOR).val() as string; - TOTPValidator.validate(token, $) - .then(onSecondFactorTotpSuccess) - .catch(onSecondFactorTotpFailure); - return false; - } - - $(window.document).ready(function () { - $(ClientConstants.TOTP_FORM_SELECTOR).on("submit", onTOTPFormSubmitted); - U2FValidator.validate($) - .then(onU2fAuthenticationSuccess, onU2fAuthenticationFailure); - }); -} \ No newline at end of file diff --git a/themes/matrix/client/src/lib/totp-register/totp-register.ts b/themes/matrix/client/src/lib/totp-register/totp-register.ts deleted file mode 100644 index 6a9aa7ee0..000000000 --- a/themes/matrix/client/src/lib/totp-register/totp-register.ts +++ /dev/null @@ -1,11 +0,0 @@ - -import jslogger = require("js-logger"); -import UISelector = require("./ui-selector"); - -export default function(window: Window, $: JQueryStatic) { - jslogger.debug("Creating QRCode from OTPAuth url"); - const qrcode = $(UISelector.QRCODE_ID_SELECTOR); - const val = qrcode.text(); - qrcode.empty(); - new (window as any).QRCode(qrcode.get(0), val); -} diff --git a/themes/matrix/client/src/lib/totp-register/ui-selector.ts b/themes/matrix/client/src/lib/totp-register/ui-selector.ts deleted file mode 100644 index 9d43fabea..000000000 --- a/themes/matrix/client/src/lib/totp-register/ui-selector.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const QRCODE_ID_SELECTOR = "#qrcode"; \ No newline at end of file diff --git a/themes/matrix/client/src/lib/u2f-register/u2f-register.ts b/themes/matrix/client/src/lib/u2f-register/u2f-register.ts deleted file mode 100644 index abf40ee05..000000000 --- a/themes/matrix/client/src/lib/u2f-register/u2f-register.ts +++ /dev/null @@ -1,56 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import U2f = require("u2f"); -import * as U2fApi from "u2f-api"; -import { Notifier } from "../Notifier"; -import GetPromised from "../GetPromised"; -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function checkRegistration(regResponse: U2fApi.RegisterResponse): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, regResponse, undefined, "json") - .done((body: RedirectionMessage | ErrorMessage) => { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail((xhr, status) => { - reject(new Error("Failed to register device.")); - }); - }); - } - - function requestRegistration(): BluebirdPromise { - return GetPromised($, Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, {}, - undefined, "json") - .then((registrationRequest: U2f.Request) => { - return U2fApi.register(registrationRequest, [], 60); - }) - .then((res) => checkRegistration(res)); - } - - function onRegisterFailure(err: Error) { - notifier.error(UserMessages.REGISTRATION_U2F_FAILED); - } - - $(document).ready(function () { - requestRegistration() - .then((redirectionUrl: string) => { - SafeRedirect(redirectionUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - }) - .catch((err) => { - onRegisterFailure(err); - }); - }); -} diff --git a/themes/matrix/client/src/thirdparties/matrix.js b/themes/matrix/client/src/thirdparties/matrix.js old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/thirdparties/qrcode.min.js b/themes/matrix/client/src/thirdparties/qrcode.min.js old mode 100644 new mode 100755 diff --git a/themes/matrix/client/src/thirdparties/u2f-api.js b/themes/matrix/client/src/thirdparties/u2f-api.js old mode 100644 new mode 100755 diff --git a/themes/matrix/client/test/Notifier.test.ts b/themes/matrix/client/test/Notifier.test.ts deleted file mode 100644 index 70bfea146..000000000 --- a/themes/matrix/client/test/Notifier.test.ts +++ /dev/null @@ -1,71 +0,0 @@ - -import Assert = require("assert"); -import Sinon = require("sinon"); -import JQueryMock = require("./mocks/jquery"); - -import { Notifier } from "../src/lib/Notifier"; - -describe("test notifier", function() { - const SELECTOR = "dummy-selector"; - const MESSAGE = "This is a message"; - let jqueryMock: { jquery: JQueryMock.JQueryMock, element: JQueryMock.JQueryElementsMock }; - let clock: any; - - beforeEach(function() { - jqueryMock = JQueryMock.JQueryMock(); - clock = Sinon.useFakeTimers(); - }); - - afterEach(function() { - clock.restore(); - }); - - function should_fade_in_and_out_on_notification(notificationType: string): void { - const delayReturn = { - fadeOut: Sinon.stub() - }; - - jqueryMock.element.fadeIn.yields(); - - function onFadedInCallback() { - Assert(jqueryMock.element.fadeIn.calledOnce); - Assert(jqueryMock.element.addClass.calledWith(notificationType)); - Assert(!jqueryMock.element.removeClass.calledWith(notificationType)); - clock.tick(10 * 1000); - } - - function onFadedOutCallback() { - Assert(jqueryMock.element.removeClass.calledWith(notificationType)); - Assert(jqueryMock.element.fadeOut.calledOnce); - } - - const notifier = new Notifier(SELECTOR, jqueryMock.jquery as any); - - // Call the method by its name... Bad but allows code reuse. - (notifier as any)[notificationType](MESSAGE, { - onFadedIn: onFadedInCallback, - onFadedOut: onFadedOutCallback - }); - - clock.tick(510); - - Assert(jqueryMock.element.fadeIn.calledOnce); - } - - - it("should fade in and fade out an error message", function() { - should_fade_in_and_out_on_notification("error"); - }); - - it("should fade in and fade out an info message", function() { - should_fade_in_and_out_on_notification("info"); - }); - - it("should fade in and fade out an warning message", function() { - should_fade_in_and_out_on_notification("warning"); - }); - - it("should fade in and fade out an success message", function() { - should_fade_in_and_out_on_notification("success"); - }); -}); \ No newline at end of file diff --git a/themes/matrix/client/test/firstfactor/FirstFactorValidator.test.ts b/themes/matrix/client/test/firstfactor/FirstFactorValidator.test.ts deleted file mode 100644 index ac8353278..000000000 --- a/themes/matrix/client/test/firstfactor/FirstFactorValidator.test.ts +++ /dev/null @@ -1,44 +0,0 @@ - -import FirstFactorValidator = require("../../src/lib/firstfactor/FirstFactorValidator"); -import JQueryMock = require("../mocks/jquery"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -describe("test FirstFactorValidator", function () { - it("should validate first factor successfully", () => { - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.done.yields({ redirect: "http://redirect" }); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return FirstFactorValidator.validate("username", "password", "http://redirect", jqueryMock.jquery as any); - }); - - function should_fail_first_factor_validation(errorMessage: string) { - const xhr = { - status: 401 - }; - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.fail.yields(xhr, errorMessage); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return FirstFactorValidator.validate("username", "password", "http://redirect", jqueryMock.jquery as any) - .then(function () { - return BluebirdPromise.reject(new Error("First factor validation successfully finished while it should have not.")); - }, function (err: Error) { - Assert.equal(errorMessage, err.message); - return BluebirdPromise.resolve(); - }); - } - - describe("should fail first factor validation", () => { - it("should fail with error", () => { - return should_fail_first_factor_validation("Authentication failed. Please check your credentials."); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/client/test/mocks/NotifierStub.ts b/themes/matrix/client/test/mocks/NotifierStub.ts deleted file mode 100644 index 9c268d66d..000000000 --- a/themes/matrix/client/test/mocks/NotifierStub.ts +++ /dev/null @@ -1,33 +0,0 @@ - -import Sinon = require("sinon"); -import { INotifier } from "../../src/lib/INotifier"; - -export class NotifierStub implements INotifier { - successStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - warnStub: Sinon.SinonStub; - infoStub: Sinon.SinonStub; - - constructor() { - this.successStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - this.warnStub = Sinon.stub(); - this.infoStub = Sinon.stub(); - } - - success(msg: string) { - this.successStub(); - } - - error(msg: string) { - this.errorStub(); - } - - warning(msg: string) { - this.warnStub(); - } - - info(msg: string) { - this.infoStub(); - } -} \ No newline at end of file diff --git a/themes/matrix/client/test/mocks/jquery.ts b/themes/matrix/client/test/mocks/jquery.ts deleted file mode 100644 index 273f90861..000000000 --- a/themes/matrix/client/test/mocks/jquery.ts +++ /dev/null @@ -1,59 +0,0 @@ - -import sinon = require("sinon"); -import jquery = require("jquery"); - - -export interface JQueryMock extends sinon.SinonStub { - get: sinon.SinonStub; - post: sinon.SinonStub; - ajax: sinon.SinonStub; - notify: sinon.SinonStub; -} - -export interface JQueryElementsMock { - ready: sinon.SinonStub; - show: sinon.SinonStub; - hide: sinon.SinonStub; - html: sinon.SinonStub; - addClass: sinon.SinonStub; - removeClass: sinon.SinonStub; - fadeIn: sinon.SinonStub; - fadeOut: sinon.SinonStub; - on: sinon.SinonStub; -} - -export interface JQueryDeferredMock { - done: sinon.SinonStub; - fail: sinon.SinonStub; -} - -export function JQueryMock(): { jquery: JQueryMock, element: JQueryElementsMock } { - const jquery = sinon.stub() as any; - const jqueryInstance: JQueryElementsMock = { - ready: sinon.stub(), - show: sinon.stub(), - hide: sinon.stub(), - html: sinon.stub(), - addClass: sinon.stub(), - removeClass: sinon.stub(), - fadeIn: sinon.stub(), - fadeOut: sinon.stub(), - on: sinon.stub() - }; - jquery.ajax = sinon.stub(); - jquery.get = sinon.stub(); - jquery.post = sinon.stub(); - jquery.notify = sinon.stub(); - jquery.returns(jqueryInstance); - return { - jquery: jquery, - element: jqueryInstance - }; -} - -export function JQueryDeferredMock(): JQueryDeferredMock { - return { - done: sinon.stub(), - fail: sinon.stub() - }; -} diff --git a/themes/matrix/client/test/mocks/u2f-api.ts b/themes/matrix/client/test/mocks/u2f-api.ts deleted file mode 100644 index d123f6a95..000000000 --- a/themes/matrix/client/test/mocks/u2f-api.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import sinon = require("sinon"); - -export interface U2FApiMock { - sign: sinon.SinonStub; - register: sinon.SinonStub; -} - -export function U2FApiMock(): U2FApiMock { - return { - sign: sinon.stub(), - register: sinon.stub() - }; -} \ No newline at end of file diff --git a/themes/matrix/client/test/secondfactor/TOTPValidator.test.ts b/themes/matrix/client/test/secondfactor/TOTPValidator.test.ts deleted file mode 100644 index 5dd6f15c3..000000000 --- a/themes/matrix/client/test/secondfactor/TOTPValidator.test.ts +++ /dev/null @@ -1,37 +0,0 @@ - -import TOTPValidator = require("../../src/lib/secondfactor/TOTPValidator"); -import JQueryMock = require("../mocks/jquery"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -describe("test TOTPValidator", function () { - it("should initiate an identity check successfully", () => { - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.done.yields({ redirect: "https://home.test.url" }); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return TOTPValidator.validate("totp_token", jqueryMock.jquery as any); - }); - - it("should fail validating TOTP token", () => { - const errorMessage = "Error while validating TOTP token"; - - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.fail.yields(undefined, errorMessage); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return TOTPValidator.validate("totp_token", jqueryMock.jquery as any) - .then(function () { - return BluebirdPromise.reject(new Error("Registration successfully finished while it should have not.")); - }, function (err: Error) { - Assert.equal(errorMessage, err.message); - return BluebirdPromise.resolve(); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/client/test/totp-register/totp-register.test.ts b/themes/matrix/client/test/totp-register/totp-register.test.ts deleted file mode 100644 index 86fc455a1..000000000 --- a/themes/matrix/client/test/totp-register/totp-register.test.ts +++ /dev/null @@ -1,31 +0,0 @@ - -import sinon = require("sinon"); -import assert = require("assert"); - -import UISelector = require("../../src/lib/totp-register/ui-selector"); -import TOTPRegister = require("../../src/lib/totp-register/totp-register"); - -describe("test totp-register", function() { - let jqueryMock: any; - let windowMock: any; - before(function() { - jqueryMock = sinon.stub(); - windowMock = { - QRCode: sinon.spy() - }; - }); - - it("should create qrcode in page", function() { - const mock = { - text: sinon.stub(), - empty: sinon.stub(), - get: sinon.stub() - }; - jqueryMock.withArgs(UISelector.QRCODE_ID_SELECTOR).returns(mock); - - TOTPRegister.default(windowMock, jqueryMock); - - assert(mock.text.calledOnce); - assert(mock.empty.calledOnce); - }); -}); \ No newline at end of file diff --git a/themes/matrix/client/tsconfig.json b/themes/matrix/client/tsconfig.json deleted file mode 100644 index 0bb4d62ff..000000000 --- a/themes/matrix/client/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "moduleResolution": "node", - "noImplicitAny": true, - "sourceMap": true, - "removeComments": true, - "outDir": "../dist", - "baseUrl": ".", - "paths": { - "*": [ - "./types/*", - "../shared/types/*" - ] - } - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "test/**/*" - ] -} diff --git a/themes/matrix/client/tslint.json b/themes/matrix/client/tslint.json deleted file mode 100644 index c2c1b7501..000000000 --- a/themes/matrix/client/tslint.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "no-var-keyword": true, - "quotemark": [ - true, - "double", - "avoid-escape" - ], - "semicolon": [ - true, - "always", - "ignore-bound-class-methods" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-module", - "check-separator", - "check-type" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "onespace", - "index-signature": "onespace", - "parameter": "onespace", - "property-declaration": "onespace", - "variable-declaration": "onespace" - } - ], - "no-internal-module": true, - "no-trailing-whitespace": true, - "no-null-keyword": true, - "prefer-const": true, - "jsdoc-format": true - } -} diff --git a/themes/matrix/server/.directory b/themes/matrix/server/.directory old mode 100644 new mode 100755 diff --git a/themes/matrix/server/src/index.ts b/themes/matrix/server/src/index.ts deleted file mode 100755 index fcbf4d022..000000000 --- a/themes/matrix/server/src/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -#! /usr/bin/env node - -import Server from "./lib/Server"; -import { GlobalDependencies } from "../types/Dependencies"; -import YAML = require("yamljs"); - -const configurationFilepath = process.argv[2]; -if (!configurationFilepath) { - console.log("No config file has been provided."); - console.log("Usage: authelia "); - process.exit(0); -} - -const yamlContent = YAML.load(configurationFilepath); - -const deps: GlobalDependencies = { - u2f: require("u2f"), - ldapjs: require("ldapjs"), - session: require("express-session"), - winston: require("winston"), - speakeasy: require("speakeasy"), - nedb: require("nedb"), - ConnectRedis: require("connect-redis"), - Redis: require("redis") -}; - -const server = new Server(deps); -server.start(yamlContent, deps); diff --git a/themes/matrix/server/src/lib/.directory b/themes/matrix/server/src/lib/.directory deleted file mode 100644 index 006b379ad..000000000 --- a/themes/matrix/server/src/lib/.directory +++ /dev/null @@ -1,4 +0,0 @@ -[Dolphin] -Timestamp=2018,12,17,20,59,13 -Version=3 -ViewMode=1 diff --git a/themes/matrix/server/src/lib/AuthenticationSessionHandler.ts b/themes/matrix/server/src/lib/AuthenticationSessionHandler.ts deleted file mode 100644 index 57361bf8a..000000000 --- a/themes/matrix/server/src/lib/AuthenticationSessionHandler.ts +++ /dev/null @@ -1,45 +0,0 @@ - - -import express = require("express"); -import U2f = require("u2f"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { IRequestLogger } from "./logging/IRequestLogger"; -import { Level } from "./authentication/Level"; - -const INITIAL_AUTHENTICATION_SESSION: AuthenticationSession = { - keep_me_logged_in: false, - authentication_level: Level.NOT_AUTHENTICATED, - last_activity_datetime: undefined, - userid: undefined, - email: undefined, - groups: [], - register_request: undefined, - sign_request: undefined, - identity_check: undefined, - redirect: undefined -}; - -export class AuthenticationSessionHandler { - static reset(req: express.Request): void { - req.session.auth = Object.assign({}, INITIAL_AUTHENTICATION_SESSION, {}); - - // Initialize last activity with current time - req.session.auth.last_activity_datetime = new Date().getTime(); - } - - static get(req: express.Request, logger: IRequestLogger): AuthenticationSession { - if (!req.session) { - const errorMsg = "Something is wrong with session cookies. Please check Redis is running and Authelia can connect to it."; - logger.error(req, errorMsg); - throw new Error(errorMsg); - } - - if (!req.session.auth) { - logger.debug(req, "Authentication session %s was undefined. Resetting.", req.sessionID); - AuthenticationSessionHandler.reset(req); - } - - return req.session.auth; - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/ErrorReplies.ts b/themes/matrix/server/src/lib/ErrorReplies.ts deleted file mode 100644 index f1c5f4fd1..000000000 --- a/themes/matrix/server/src/lib/ErrorReplies.ts +++ /dev/null @@ -1,49 +0,0 @@ -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import { IRequestLogger } from "./logging/IRequestLogger"; - -function replyWithError(req: express.Request, res: express.Response, - code: number, logger: IRequestLogger, body?: Object): (err: Error) => void { - return function (err: Error): void { - if (req.originalUrl.startsWith("/api/") || code == 200) { - logger.error(req, "Reply with error %d: %s", code, err.message); - logger.debug(req, "%s", err.stack); - res.status(code); - res.send(body); - } - else { - logger.error(req, "Redirect to error %d: %s", code, err.message); - logger.debug(req, "%s", err.stack); - res.redirect("/error/" + code); - } - }; -} - -export function redirectTo(redirectUrl: string, req: express.Request, - res: express.Response, logger: IRequestLogger) { - return function(err: Error) { - logger.error(req, "Error: %s", err.message); - logger.debug(req, "Redirecting to %s", redirectUrl); - res.redirect(redirectUrl); - }; -} - -export function replyWithError400(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 400, logger); -} - -export function replyWithError401(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 401, logger); -} - -export function replyWithError403(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 403, logger); -} - -export function replyWithError200(req: express.Request, - res: express.Response, logger: IRequestLogger, message: string) { - return replyWithError(req, res, 200, logger, { error: message }); -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/Exceptions.ts b/themes/matrix/server/src/lib/Exceptions.ts deleted file mode 100644 index 83fa4eb6b..000000000 --- a/themes/matrix/server/src/lib/Exceptions.ts +++ /dev/null @@ -1,88 +0,0 @@ - -export class LdapSearchError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapSearchError"; - (Object).setPrototypeOf(this, LdapSearchError.prototype); - } -} - -export class LdapBindError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapBindError"; - (Object).setPrototypeOf(this, LdapBindError.prototype); - } -} - -export class LdapError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapError"; - (Object).setPrototypeOf(this, LdapError.prototype); - } -} - -export class IdentityError extends Error { - constructor(message?: string) { - super(message); - this.name = "IdentityError"; - (Object).setPrototypeOf(this, IdentityError.prototype); - } -} - -export class AccessDeniedError extends Error { - constructor(message?: string) { - super(message); - this.name = "AccessDeniedError"; - (Object).setPrototypeOf(this, AccessDeniedError.prototype); - } -} - -export class AuthenticationRegulationError extends Error { - constructor(message?: string) { - super(message); - this.name = "AuthenticationRegulationError"; - (Object).setPrototypeOf(this, AuthenticationRegulationError.prototype); - } -} - -export class InvalidTOTPError extends Error { - constructor(message?: string) { - super(message); - this.name = "InvalidTOTPError"; - (Object).setPrototypeOf(this, InvalidTOTPError.prototype); - } -} - -export class NotAuthenticatedError extends Error { - constructor(message?: string) { - super(message); - this.name = "NotAuthenticatedError"; - (Object).setPrototypeOf(this, NotAuthenticatedError.prototype); - } -} - -export class NotAuthorizedError extends Error { - constructor(message?: string) { - super(message); - this.name = "NotAuthanticatedError"; - (Object).setPrototypeOf(this, NotAuthorizedError.prototype); - } -} - -export class FirstFactorValidationError extends Error { - constructor(message?: string) { - super(message); - this.name = "FirstFactorValidationError"; - (Object).setPrototypeOf(this, FirstFactorValidationError.prototype); - } -} - -export class SecondFactorValidationError extends Error { - constructor(message?: string) { - super(message); - this.name = "SecondFactorValidationError"; - (Object).setPrototypeOf(this, FirstFactorValidationError.prototype); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/FirstFactorValidator.ts b/themes/matrix/server/src/lib/FirstFactorValidator.ts deleted file mode 100644 index 231060002..000000000 --- a/themes/matrix/server/src/lib/FirstFactorValidator.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import objectPath = require("object-path"); -import Exceptions = require("./Exceptions"); -import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler"; -import { IRequestLogger } from "./logging/IRequestLogger"; -import { Level } from "./authentication/Level"; - -export function validate(req: express.Request, logger: IRequestLogger): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, logger); - - if (!authSession.userid || authSession.authentication_level < Level.ONE_FACTOR) - return reject(new Exceptions.FirstFactorValidationError( - "First factor has not been validated yet.")); - - resolve(); - }); -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/IdentityCheckMiddleware.spec.ts b/themes/matrix/server/src/lib/IdentityCheckMiddleware.spec.ts deleted file mode 100644 index 842ed6bcb..000000000 --- a/themes/matrix/server/src/lib/IdentityCheckMiddleware.spec.ts +++ /dev/null @@ -1,176 +0,0 @@ - -import sinon = require("sinon"); -import IdentityValidator = require("./IdentityCheckMiddleware"); -import { AuthenticationSessionHandler } - from "./AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { UserDataStore } from "./storage/UserDataStore"; -import exceptions = require("./Exceptions"); -import { ServerVariables } from "./ServerVariables"; -import Assert = require("assert"); -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("./stubs/express.spec"); -import NotifierMock = require("./notifiers/NotifierStub.spec"); -import { IdentityValidableStub } from "./IdentityValidableStub.spec"; -import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "./ServerVariablesMockBuilder.spec"; -import { PRE_VALIDATION_TEMPLATE } - from "./IdentityCheckPreValidationTemplate"; - - -describe("IdentityCheckMiddleware", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let app: express.Application; - let app_get: sinon.SinonStub; - let app_post: sinon.SinonStub; - let identityValidable: IdentityValidableStub; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - - req.headers = {}; - req.originalUrl = "/non-api/xxx"; - req.session = {}; - - req.query = {}; - req.app = {}; - - identityValidable = new IdentityValidableStub(); - - mocks.notifier.notifyStub.returns(BluebirdPromise.resolve()); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve()); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({ userId: "user" })); - - app = express(); - app_get = sinon.stub(app, "get"); - app_post = sinon.stub(app, "post"); - }); - - afterEach(function () { - app_get.restore(); - app_post.restore(); - }); - - describe("test start GET", function () { - it("should redirect to error 401 if pre validation initialization \ -throws a first factor error", function () { - identityValidable.preValidationInitStub.returns(BluebirdPromise.reject( - new exceptions.FirstFactorValidationError( - "Error during prevalidation"))); - const callback = IdentityValidator.get_start_validation( - identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(() => { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - - // In that case we answer with 200 to avoid user enumeration. - it("should send 200 if email is missing in provided identity", function () { - const identity = { userid: "abc" }; - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(identityValidable.preValidationResponseStub.called); - }); - }); - - // In that case we answer with 200 to avoid user enumeration. - it("should send 200 if userid is missing in provided identity", - function () { - const endpoint = "/protected"; - const identity = { email: "abc@example.com" }; - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(identityValidable.preValidationResponseStub.called); - }); - }); - - it("should issue a token, send an email and return 204", function () { - const endpoint = "/protected"; - const identity = { userid: "user", email: "abc@example.com" }; - req.get = sinon.stub().withArgs("Host").returns("localhost"); - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/finish_endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(mocks.notifier.notifyStub.calledOnce); - Assert(mocks.userDataStore.produceIdentityValidationTokenStub - .calledOnce); - Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub - .getCall(0).args[0], "user"); - Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub - .getCall(0).args[3], 240000); - }); - }); - }); - - - - describe("test finish GET", function () { - it("should send 401 if no identity_token is provided", () => { - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - - it("should call postValidation if identity_token is provided and still \ -valid", function () { - req.query.identity_token = "token"; - - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - return callback(req as any, res as any, undefined); - }); - - it("should return 401 if identity_token is provided but invalid", - function () { - req.query.identity_token = "token"; - - identityValidable.postValidationInitStub - .returns(BluebirdPromise.resolve()); - mocks.userDataStore.consumeIdentityValidationTokenStub.reset(); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.reject(new Error("Invalid token"))); - - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - return callback(req as any, res as any, undefined) - .then(() => { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - }); -}); diff --git a/themes/matrix/server/src/lib/IdentityCheckMiddleware.ts b/themes/matrix/server/src/lib/IdentityCheckMiddleware.ts deleted file mode 100644 index e72ea4db1..000000000 --- a/themes/matrix/server/src/lib/IdentityCheckMiddleware.ts +++ /dev/null @@ -1,138 +0,0 @@ -import objectPath = require("object-path"); -import randomstring = require("randomstring"); -import BluebirdPromise = require("bluebird"); -import util = require("util"); -import Exceptions = require("./Exceptions"); -import fs = require("fs"); -import ejs = require("ejs"); -import { IUserDataStore } from "./storage/IUserDataStore"; -import Express = require("express"); -import ErrorReplies = require("./ErrorReplies"); -import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { ServerVariables } from "./ServerVariables"; -import { IdentityValidable } from "./IdentityValidable"; - -import Identity = require("../../types/Identity"); -import { IdentityValidationDocument } - from "./storage/IdentityValidationDocument"; - -const filePath = __dirname + "/../resources/email-template.ejs"; -const email_template = fs.readFileSync(filePath, "utf8"); - -function createAndSaveToken(userid: string, challenge: string, - userDataStore: IUserDataStore): BluebirdPromise { - - const five_minutes = 4 * 60 * 1000; - const token = randomstring.generate({ length: 64 }); - const that = this; - - return userDataStore.produceIdentityValidationToken(userid, token, challenge, - five_minutes) - .then(function () { - return BluebirdPromise.resolve(token); - }); -} - -function consumeToken(token: string, challenge: string, - userDataStore: IUserDataStore) - : BluebirdPromise { - return userDataStore.consumeIdentityValidationToken(token, challenge); -} - -export function register(app: Express.Application, - pre_validation_endpoint: string, - post_validation_endpoint: string, - handler: IdentityValidable, - vars: ServerVariables) { - - app.get(pre_validation_endpoint, - get_start_validation(handler, post_validation_endpoint, vars)); - app.get(post_validation_endpoint, - get_finish_validation(handler, vars)); -} - -function checkIdentityToken(req: Express.Request, identityToken: string) - : BluebirdPromise { - if (!identityToken) - return BluebirdPromise.reject( - new Exceptions.AccessDeniedError("No identity token provided")); - return BluebirdPromise.resolve(); -} - -export function get_finish_validation(handler: IdentityValidable, - vars: ServerVariables) - : Express.RequestHandler { - - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - - let authSession: AuthenticationSession; - const identityToken = objectPath.get( - req, "query.identity_token"); - vars.logger.debug(req, "Identity token provided is %s", identityToken); - - return checkIdentityToken(req, identityToken) - .then(() => { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - return handler.postValidationInit(req); - }) - .then(() => { - return consumeToken(identityToken, handler.challenge(), - vars.userDataStore); - }) - .then((doc: IdentityValidationDocument) => { - authSession.identity_check = { - challenge: handler.challenge(), - userid: doc.userId - }; - handler.postValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - }; -} - -export function get_start_validation(handler: IdentityValidable, - postValidationEndpoint: string, - vars: ServerVariables) - : Express.RequestHandler { - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - let identity: Identity.Identity; - - return handler.preValidationInit(req) - .then((id: Identity.Identity) => { - identity = id; - const email = identity.email; - const userid = identity.userid; - vars.logger.info(req, "Start identity validation of user \"%s\"", - userid); - - if (!(email && userid)) - return BluebirdPromise.reject(new Exceptions.IdentityError( - "Missing user id or email address")); - - return createAndSaveToken(userid, handler.challenge(), - vars.userDataStore); - }) - .then((token) => { - const host = req.get("Host"); - const link_url = util.format("https://%s%s?identity_token=%s", host, - postValidationEndpoint, token); - vars.logger.info(req, "Notification sent to user \"%s\"", - identity.userid); - return vars.notifier.notify(identity.email, handler.mailSubject(), - link_url); - }) - .then(() => { - handler.preValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(Exceptions.IdentityError, (err: Error) => { - handler.preValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - }; -} diff --git a/themes/matrix/server/src/lib/IdentityCheckPreValidationTemplate.ts b/themes/matrix/server/src/lib/IdentityCheckPreValidationTemplate.ts deleted file mode 100644 index 0161ce406..000000000 --- a/themes/matrix/server/src/lib/IdentityCheckPreValidationTemplate.ts +++ /dev/null @@ -1,3 +0,0 @@ - - -export const PRE_VALIDATION_TEMPLATE = "need-identity-validation"; \ No newline at end of file diff --git a/themes/matrix/server/src/lib/IdentityValidable.ts b/themes/matrix/server/src/lib/IdentityValidable.ts deleted file mode 100644 index 075580c9e..000000000 --- a/themes/matrix/server/src/lib/IdentityValidable.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Bluebird = require("bluebird"); -import Identity = require("../../types/Identity"); - -// IdentityValidator allows user to go through a identity validation process -// in two steps: -// - Request an operation to be performed (password reset, registration). -// - Confirm operation with email. - -export interface IdentityValidable { - challenge(): string; - preValidationInit(req: Express.Request): Bluebird; - postValidationInit(req: Express.Request): Bluebird; - - // Serves a page after identity check request - preValidationResponse(req: Express.Request, res: Express.Response): void; - // Serves the page if identity validated - postValidationResponse(req: Express.Request, res: Express.Response): void; - mailSubject(): string; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/IdentityValidableStub.spec.ts b/themes/matrix/server/src/lib/IdentityValidableStub.spec.ts deleted file mode 100644 index 20a977140..000000000 --- a/themes/matrix/server/src/lib/IdentityValidableStub.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ - -import Sinon = require("sinon"); -import { IdentityValidable } from "./IdentityValidable"; -import express = require("express"); -import Bluebird = require("bluebird"); -import { Identity } from "../../types/Identity"; - - -export class IdentityValidableStub implements IdentityValidable { - challengeStub: Sinon.SinonStub; - preValidationInitStub: Sinon.SinonStub; - postValidationInitStub: Sinon.SinonStub; - preValidationResponseStub: Sinon.SinonStub; - postValidationResponseStub: Sinon.SinonStub; - mailSubjectStub: Sinon.SinonStub; - - constructor() { - this.challengeStub = Sinon.stub(); - - this.preValidationInitStub = Sinon.stub(); - this.postValidationInitStub = Sinon.stub(); - - this.preValidationResponseStub = Sinon.stub(); - this.postValidationResponseStub = Sinon.stub(); - - this.mailSubjectStub = Sinon.stub(); - } - - challenge(): string { - return this.challengeStub(); - } - - preValidationInit(req: Express.Request): Bluebird { - return this.preValidationInitStub(req); - } - - postValidationInit(req: Express.Request): Bluebird { - return this.postValidationInitStub(req); - } - - preValidationResponse(req: Express.Request, res: Express.Response): void { - return this.preValidationResponseStub(req, res); - } - - postValidationResponse(req: Express.Request, res: Express.Response): void { - return this.postValidationResponseStub(req, res); - } - - mailSubject(): string { - return this.mailSubjectStub(); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/Server.spec.ts b/themes/matrix/server/src/lib/Server.spec.ts deleted file mode 100644 index 365163254..000000000 --- a/themes/matrix/server/src/lib/Server.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ - -import Assert = require("assert"); -import Sinon = require("sinon"); -import nedb = require("nedb"); -import express = require("express"); -import winston = require("winston"); -import speakeasy = require("speakeasy"); -import u2f = require("u2f"); -import session = require("express-session"); -import { Configuration } from "./configuration/schema/Configuration"; -import { GlobalDependencies } from "../../types/Dependencies"; -import Server from "./Server"; -import { LdapjsMock, LdapjsClientMock } from "./stubs/ldapjs.spec"; - - -describe("Server", function () { - let deps: GlobalDependencies; - let sessionMock: Sinon.SinonSpy; - let ldapjsMock: LdapjsMock; - - before(function () { - sessionMock = Sinon.spy(session); - ldapjsMock = new LdapjsMock(); - - deps = { - speakeasy: speakeasy, - u2f: u2f, - nedb: nedb, - winston: winston, - ldapjs: ldapjsMock as any, - session: sessionMock as any, - ConnectRedis: Sinon.spy(), - Redis: Sinon.spy() as any - }; - }); - - - it("should set cookie scope to domain set in the config", function () { - const config: Configuration = { - port: 8081, - session: { - domain: "example.com", - secret: "secret" - }, - authentication_backend: { - ldap: { - url: "http://ldap", - user: "user", - password: "password", - base_dn: "dc=example,dc=com" - }, - }, - notifier: { - email: { - username: "user@example.com", - password: "password", - sender: "test@authelia.com", - service: "gmail" - } - }, - regulation: { - max_retries: 3, - ban_time: 5 * 60, - find_time: 5 * 60 - }, - storage: { - local: { - in_memory: true - } - } - }; - - const server = new Server(deps); - server.start(config, deps) - .then(function () { - Assert(sessionMock.calledOnce); - Assert.equal(sessionMock.getCall(0).args[0].cookie.domain, "example.com"); - server.stop(); - }); - }); -}); diff --git a/themes/matrix/server/src/lib/Server.ts b/themes/matrix/server/src/lib/Server.ts deleted file mode 100644 index 4090f6294..000000000 --- a/themes/matrix/server/src/lib/Server.ts +++ /dev/null @@ -1,93 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import ObjectPath = require("object-path"); - -import { Configuration } from "./configuration/schema/Configuration"; -import { GlobalDependencies } from "../../types/Dependencies"; -import { UserDataStore } from "./storage/UserDataStore"; -import { ConfigurationParser } from "./configuration/ConfigurationParser"; -import { SessionConfigurationBuilder } from "./configuration/SessionConfigurationBuilder"; -import { GlobalLogger } from "./logging/GlobalLogger"; -import { RequestLogger } from "./logging/RequestLogger"; -import { ServerVariables } from "./ServerVariables"; -import { ServerVariablesInitializer } from "./ServerVariablesInitializer"; -import { Configurator } from "./web_server/Configurator"; - -import * as Express from "express"; -import * as Path from "path"; -import * as http from "http"; - -function clone(obj: any) { - return JSON.parse(JSON.stringify(obj)); -} - -export default class Server { - private httpServer: http.Server; - private globalLogger: GlobalLogger; - private requestLogger: RequestLogger; - - constructor(deps: GlobalDependencies) { - this.globalLogger = new GlobalLogger(deps.winston); - this.requestLogger = new RequestLogger(deps.winston); - } - - private displayConfigurations(configuration: Configuration) { - const displayableConfiguration: Configuration = clone(configuration); - const STARS = "*****"; - - if (displayableConfiguration.authentication_backend.ldap) { - displayableConfiguration.authentication_backend.ldap.password = STARS; - } - - displayableConfiguration.session.secret = STARS; - if (displayableConfiguration.notifier && displayableConfiguration.notifier.email) - displayableConfiguration.notifier.email.password = STARS; - if (displayableConfiguration.notifier && displayableConfiguration.notifier.smtp) - displayableConfiguration.notifier.smtp.password = STARS; - - this.globalLogger.debug("User configuration is %s", - JSON.stringify(displayableConfiguration, undefined, 2)); - } - - private setup(config: Configuration, app: Express.Application, deps: GlobalDependencies): BluebirdPromise { - const that = this; - return ServerVariablesInitializer.initialize( - config, this.globalLogger, this.requestLogger, deps) - .then(function (vars: ServerVariables) { - Configurator.configure(config, app, vars, deps); - return BluebirdPromise.resolve(); - }); - } - - private startServer(app: Express.Application, port: number) { - const that = this; - that.globalLogger.info("Starting Authelia..."); - return new BluebirdPromise((resolve, reject) => { - this.httpServer = app.listen(port, function (err: string) { - that.globalLogger.info("Listening on port %d...", port); - resolve(); - }); - }); - } - - start(configuration: Configuration, deps: GlobalDependencies) - : BluebirdPromise { - const that = this; - const app = Express(); - - const appConfiguration = ConfigurationParser.parse(configuration); - - // by default the level of logs is info - deps.winston.level = appConfiguration.logs_level; - this.displayConfigurations(appConfiguration); - - return this.setup(appConfiguration, app, deps) - .then(function () { - return that.startServer(app, appConfiguration.port); - }); - } - - stop() { - this.httpServer.close(); - } -} - diff --git a/themes/matrix/server/src/lib/ServerVariables.ts b/themes/matrix/server/src/lib/ServerVariables.ts deleted file mode 100644 index cd3dd6dc8..000000000 --- a/themes/matrix/server/src/lib/ServerVariables.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { IRequestLogger } from "./logging/IRequestLogger"; -import { ITotpHandler } from "./authentication/totp/ITotpHandler"; -import { IU2fHandler } from "./authentication/u2f/IU2fHandler"; -import { IUserDataStore } from "./storage/IUserDataStore"; -import { INotifier } from "./notifiers/INotifier"; -import { IRegulator } from "./regulation/IRegulator"; -import { Configuration } from "./configuration/schema/Configuration"; -import { IAuthorizer } from "./authorization/IAuthorizer"; -import { IUsersDatabase } from "./authentication/backends/IUsersDatabase"; - -export interface ServerVariables { - logger: IRequestLogger; - usersDatabase: IUsersDatabase; - totpHandler: ITotpHandler; - u2f: IU2fHandler; - userDataStore: IUserDataStore; - notifier: INotifier; - regulator: IRegulator; - config: Configuration; - authorizer: IAuthorizer; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/ServerVariablesInitializer.ts b/themes/matrix/server/src/lib/ServerVariablesInitializer.ts deleted file mode 100644 index df79238cc..000000000 --- a/themes/matrix/server/src/lib/ServerVariablesInitializer.ts +++ /dev/null @@ -1,116 +0,0 @@ - -import winston = require("winston"); -import BluebirdPromise = require("bluebird"); -import U2F = require("u2f"); -import Nodemailer = require("nodemailer"); - -import { IRequestLogger } from "./logging/IRequestLogger"; -import { RequestLogger } from "./logging/RequestLogger"; - -import { TotpHandler } from "./authentication/totp/TotpHandler"; -import { ITotpHandler } from "./authentication/totp/ITotpHandler"; -import { NotifierFactory } from "./notifiers/NotifierFactory"; -import { MailSenderBuilder } from "./notifiers/MailSenderBuilder"; -import { LdapUsersDatabase } from "./authentication/backends/ldap/LdapUsersDatabase"; -import { ConnectorFactory } from "./authentication/backends/ldap/connector/ConnectorFactory"; - -import { IUserDataStore } from "./storage/IUserDataStore"; -import { UserDataStore } from "./storage/UserDataStore"; -import { INotifier } from "./notifiers/INotifier"; -import { Regulator } from "./regulation/Regulator"; -import { IRegulator } from "./regulation/IRegulator"; -import Configuration = require("./configuration/schema/Configuration"); -import { CollectionFactoryFactory } from "./storage/CollectionFactoryFactory"; -import { ICollectionFactory } from "./storage/ICollectionFactory"; -import { MongoCollectionFactory } from "./storage/mongo/MongoCollectionFactory"; -import { IMongoClient } from "./connectors/mongo/IMongoClient"; - -import { GlobalDependencies } from "../../types/Dependencies"; -import { ServerVariables } from "./ServerVariables"; -import { MongoClient } from "./connectors/mongo/MongoClient"; -import { IGlobalLogger } from "./logging/IGlobalLogger"; -import { SessionFactory } from "./authentication/backends/ldap/SessionFactory"; -import { IUsersDatabase } from "./authentication/backends/IUsersDatabase"; -import { FileUsersDatabase } from "./authentication/backends/file/FileUsersDatabase"; -import { Authorizer } from "./authorization/Authorizer"; - -class UserDataStoreFactory { - static create(config: Configuration.Configuration, globalLogger: IGlobalLogger): BluebirdPromise { - if (config.storage.local) { - const nedbOptions: Nedb.DataStoreOptions = { - filename: config.storage.local.path, - inMemoryOnly: config.storage.local.in_memory - }; - const collectionFactory = CollectionFactoryFactory.createNedb(nedbOptions); - return BluebirdPromise.resolve(new UserDataStore(collectionFactory)); - } - else if (config.storage.mongo) { - const mongoClient = new MongoClient( - config.storage.mongo, - globalLogger); - const collectionFactory = CollectionFactoryFactory.createMongo(mongoClient); - return BluebirdPromise.resolve(new UserDataStore(collectionFactory)); - } - - return BluebirdPromise.reject(new Error("Storage backend incorrectly configured.")); - } -} - -export class ServerVariablesInitializer { - static createUsersDatabase( - config: Configuration.Configuration, - deps: GlobalDependencies) - : IUsersDatabase { - - if (config.authentication_backend.ldap) { - const ldapConfig = config.authentication_backend.ldap; - return new LdapUsersDatabase( - new SessionFactory( - ldapConfig, - new ConnectorFactory(ldapConfig, deps.ldapjs), - deps.winston - ), - ldapConfig - ); - } - else if (config.authentication_backend.file) { - return new FileUsersDatabase(config.authentication_backend.file); - } - } - - static initialize( - config: Configuration.Configuration, - globalLogger: IGlobalLogger, - requestLogger: IRequestLogger, - deps: GlobalDependencies) - : BluebirdPromise { - - const mailSenderBuilder = - new MailSenderBuilder(Nodemailer); - const notifier = NotifierFactory.build( - config.notifier, mailSenderBuilder); - const authorizer = new Authorizer(config.access_control, deps.winston); - const totpHandler = new TotpHandler(deps.speakeasy); - const usersDatabase = this.createUsersDatabase( - config, deps); - - return UserDataStoreFactory.create(config, globalLogger) - .then(function (userDataStore: UserDataStore) { - const regulator = new Regulator(userDataStore, config.regulation.max_retries, - config.regulation.find_time, config.regulation.ban_time); - - const variables: ServerVariables = { - authorizer: authorizer, - config: config, - usersDatabase: usersDatabase, - logger: requestLogger, - notifier: notifier, - regulator: regulator, - totpHandler: totpHandler, - u2f: deps.u2f, - userDataStore: userDataStore - }; - return BluebirdPromise.resolve(variables); - }); - } -} diff --git a/themes/matrix/server/src/lib/ServerVariablesMockBuilder.spec.ts b/themes/matrix/server/src/lib/ServerVariablesMockBuilder.spec.ts deleted file mode 100644 index 7874702a0..000000000 --- a/themes/matrix/server/src/lib/ServerVariablesMockBuilder.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { ServerVariables } from "./ServerVariables"; - -import { Configuration } from "./configuration/schema/Configuration"; -import { IUsersDatabaseStub } from "./authentication/backends/IUsersDatabaseStub.spec"; -import { AuthorizerStub } from "./authorization/AuthorizerStub.spec"; -import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec"; -import { NotifierStub } from "./notifiers/NotifierStub.spec"; -import { RegulatorStub } from "./regulation/RegulatorStub.spec"; -import { TotpHandlerStub } from "./authentication/totp/TotpHandlerStub.spec"; -import { UserDataStoreStub } from "./storage/UserDataStoreStub.spec"; -import { U2fHandlerStub } from "./authentication/u2f/U2fHandlerStub.spec"; - -export interface ServerVariablesMock { - authorizer: AuthorizerStub; - config: Configuration; - usersDatabase: IUsersDatabaseStub; - logger: RequestLoggerStub; - notifier: NotifierStub; - regulator: RegulatorStub; - totpHandler: TotpHandlerStub; - userDataStore: UserDataStoreStub; - u2f: U2fHandlerStub; -} - -export class ServerVariablesMockBuilder { - static build(enableLogging?: boolean): { variables: ServerVariables, mocks: ServerVariablesMock} { - const mocks: ServerVariablesMock = { - authorizer: new AuthorizerStub(), - config: { - access_control: {}, - totp: { - issuer: "authelia.com" - }, - authentication_backend: { - ldap: { - url: "ldap://ldap", - base_dn: "dc=example,dc=com", - user: "user", - password: "password", - mail_attribute: "mail", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn" - }, - }, - logs_level: "debug", - notifier: {}, - port: 8080, - regulation: { - ban_time: 50, - find_time: 50, - max_retries: 3 - }, - session: { - secret: "my_secret", - domain: "mydomain" - }, - storage: {} - }, - usersDatabase: new IUsersDatabaseStub(), - logger: new RequestLoggerStub(enableLogging), - notifier: new NotifierStub(), - regulator: new RegulatorStub(), - totpHandler: new TotpHandlerStub(), - userDataStore: new UserDataStoreStub(), - u2f: new U2fHandlerStub() - }; - const vars: ServerVariables = { - authorizer: mocks.authorizer, - config: mocks.config, - usersDatabase: mocks.usersDatabase, - logger: mocks.logger, - notifier: mocks.notifier, - regulator: mocks.regulator, - totpHandler: mocks.totpHandler, - userDataStore: mocks.userDataStore, - u2f: mocks.u2f - }; - - return { - variables: vars, - mocks: mocks - }; - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/Level.ts b/themes/matrix/server/src/lib/authentication/Level.ts deleted file mode 100644 index 57b6a2346..000000000 --- a/themes/matrix/server/src/lib/authentication/Level.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum Level { - NOT_AUTHENTICATED = 0, - ONE_FACTOR = 1, - TWO_FACTOR = 2 -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/GroupsAndEmails.ts b/themes/matrix/server/src/lib/authentication/backends/GroupsAndEmails.ts deleted file mode 100644 index 3434ba66f..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/GroupsAndEmails.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface GroupsAndEmails { - groups: string[]; - emails: string[]; -} diff --git a/themes/matrix/server/src/lib/authentication/backends/IUsersDatabase.ts b/themes/matrix/server/src/lib/authentication/backends/IUsersDatabase.ts deleted file mode 100644 index d7fa13b7a..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/IUsersDatabase.ts +++ /dev/null @@ -1,10 +0,0 @@ -import Bluebird = require("bluebird"); - -import { GroupsAndEmails } from "./GroupsAndEmails"; - -export interface IUsersDatabase { - checkUserPassword(username: string, password: string): Bluebird; - getEmails(username: string): Bluebird; - getGroups(username: string): Bluebird; - updatePassword(username: string, newPassword: string): Bluebird; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts b/themes/matrix/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts deleted file mode 100644 index 19341a5dd..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); - -import { IUsersDatabase } from "./IUsersDatabase"; -import { GroupsAndEmails } from "./GroupsAndEmails"; - -export class IUsersDatabaseStub implements IUsersDatabase { - checkUserPasswordStub: Sinon.SinonStub; - getEmailsStub: Sinon.SinonStub; - getGroupsStub: Sinon.SinonStub; - updatePasswordStub: Sinon.SinonStub; - - constructor() { - this.checkUserPasswordStub = Sinon.stub(); - this.getEmailsStub = Sinon.stub(); - this.getGroupsStub = Sinon.stub(); - this.updatePasswordStub = Sinon.stub(); - } - - checkUserPassword(username: string, password: string): Bluebird { - return this.checkUserPasswordStub(username, password); - } - - getEmails(username: string): Bluebird { - return this.getEmailsStub(username); - } - - getGroups(username: string): Bluebird { - return this.getGroupsStub(username); - } - - updatePassword(username: string, newPassword: string): Bluebird { - return this.updatePasswordStub(username, newPassword); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts b/themes/matrix/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts deleted file mode 100644 index a258a78f7..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts +++ /dev/null @@ -1,224 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); -import Fs = require("fs"); -import Sinon = require("sinon"); -import Tmp = require("tmp"); - -import { FileUsersDatabase } from "./FileUsersDatabase"; -import { FileUsersDatabaseConfiguration } from "../../../configuration/schema/FileUsersDatabaseConfiguration"; -import { HashGenerator } from "../../../utils/HashGenerator"; - -const GOOD_DATABASE = ` -users: - john: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev - - harry: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - emails: harry.potter@authelia.com - groups: [] -`; - -const BAD_HASH = ` -users: - john: - password: "{CRYPT}$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev -`; - -const NO_PASSWORD_DATABASE = ` -users: - john: - email: john.doe@authelia.com - groups: - - admins - - dev -`; - -const NO_EMAIL_DATABASE = ` -users: - john: - password: "{CRYPT}$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - groups: - - admins - - dev -`; - -const SINGLE_USER_DATABASE = ` -users: - john: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev -` - -function createTmpFileFrom(yaml: string) { - const tmpFileAsync = Bluebird.promisify(Tmp.file); - return tmpFileAsync() - .then((path: string) => { - Fs.writeFileSync(path, yaml, "utf-8"); - return Bluebird.resolve(path); - }); -} - -describe("authentication/backends/file/FileUsersDatabase", function() { - let configuration: FileUsersDatabaseConfiguration; - - describe("checkUserPassword", () => { - describe("good config", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then((groupsAndEmails) => { - Assert.deepEqual(groupsAndEmails.groups, ["admins", "dev"]); - Assert.deepEqual(groupsAndEmails.emails, ["john.doe@authelia.com"]); - }); - }); - - it("should fail when password is wrong", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "bad_password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("no_user", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("bad hash", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail when hash is wrong", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("no password", () => { - beforeEach(() => { - return createTmpFileFrom(NO_PASSWORD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - }); - - describe("getEmails", () => { - describe("good config", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("john") - .then((emails) => { - Assert.deepEqual(emails, ["john.doe@authelia.com"]); - }); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("no_user") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("no email provided", () => { - beforeEach(() => { - return createTmpFileFrom(NO_EMAIL_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("john") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - }); - - describe("updatePassword", () => { - beforeEach(() => { - return createTmpFileFrom(SINGLE_USER_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - const NEW_HASH = "{CRYPT}$6$rounds=500000$Qw6MhgADvLyYMEq9$ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const stub = Sinon.stub(HashGenerator, "ssha512").returns(Bluebird.resolve(NEW_HASH)); - return usersDatabase.updatePassword("john", "mypassword") - .then(() => { - const content = Fs.readFileSync(configuration.path, "utf-8"); - const matches = content.match(/password: '(.+)'/); - Assert.equal(matches[1], NEW_HASH); - }) - .finally(() => stub.restore()); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.updatePassword("bad_user", "mypassword") - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => Bluebird.resolve()); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/file/FileUsersDatabase.ts b/themes/matrix/server/src/lib/authentication/backends/file/FileUsersDatabase.ts deleted file mode 100644 index d34dde21e..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/file/FileUsersDatabase.ts +++ /dev/null @@ -1,182 +0,0 @@ -import Bluebird = require("bluebird"); -import Fs = require("fs"); -import Yaml = require("yamljs"); - -import { FileUsersDatabaseConfiguration } - from "../../../configuration/schema/FileUsersDatabaseConfiguration"; -import { GroupsAndEmails } from "../GroupsAndEmails"; -import { IUsersDatabase } from "../IUsersDatabase"; -import { HashGenerator } from "../../../utils/HashGenerator"; -import { ReadWriteQueue } from "./ReadWriteQueue"; - -const loadAsync = Bluebird.promisify(Yaml.load); - -export class FileUsersDatabase implements IUsersDatabase { - private configuration: FileUsersDatabaseConfiguration; - private queue: ReadWriteQueue; - - constructor(configuration: FileUsersDatabaseConfiguration) { - this.configuration = configuration; - this.queue = new ReadWriteQueue(this.configuration.path); - } - - /** - * Read database from file. - * It enqueues the read task so that it is scheduled - * between other reads and writes. - */ - private readDatabase(): Bluebird { - return new Bluebird((resolve, reject) => { - this.queue.read((err: Error, data: string) => { - if (err) { - reject(err); - return; - } - resolve(data); - this.queue.next(); - }); - }) - .then((content) => { - const database = Yaml.parse(content); - if (!database) { - return Bluebird.reject(new Error("Unable to parse YAML file.")); - } - return Bluebird.resolve(database); - }); - } - - /** - * Checks the user exists in the database. - */ - private checkUserExists( - database: any, - username: string) - : Bluebird { - if (!(username in database.users)) { - return Bluebird.reject( - new Error(`User ${username} does not exist in database.`)); - } - return Bluebird.resolve(); - } - - /** - * Check the password of a given user. - */ - private checkPassword( - database: any, - username: string, - password: string) - : Bluebird { - const storedHash: string = database.users[username].password; - const matches = storedHash.match(/rounds=([0-9]+)\$([a-zA-z0-9]+)\$/); - if (!(matches && matches.length == 3)) { - return Bluebird.reject(new Error("Unable to detect the hash salt and rounds. " + - "Make sure the password is hashed with SSHA512.")); - } - - const rounds: number = parseInt(matches[1]); - const salt = matches[2]; - - return HashGenerator.ssha512(password, rounds, salt) - .then((hash: string) => { - if (hash !== storedHash) { - return Bluebird.reject(new Error("Wrong username/password.")); - } - return Bluebird.resolve(); - }); - } - - /** - * Retrieve email addresses of a given user. - */ - private retrieveEmails( - database: any, - username: string) - : Bluebird { - if (!("email" in database.users[username])) { - return Bluebird.reject( - new Error(`User ${username} has no email address.`)); - } - return Bluebird.resolve( - [database.users[username].email]); - } - - private retrieveGroups( - database: any, - username: string) - : Bluebird { - if (!("groups" in database.users[username])) { - return Bluebird.resolve([]); - } - return Bluebird.resolve( - database.users[username].groups); - } - - private replacePassword( - database: any, - username: string, - newPassword: string) - : Bluebird { - const that = this; - return HashGenerator.ssha512(newPassword) - .then((hash) => { - database.users[username].password = hash; - const str = Yaml.stringify(database, 4, 2); - return Bluebird.resolve(str); - }) - .then((content: string) => { - return new Bluebird((resolve, reject) => { - that.queue.write(content, (err) => { - if (err) { - return reject(err); - } - resolve(); - that.queue.next(); - }); - }); - }); - } - - checkUserPassword( - username: string, - password: string) - : Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.checkPassword(database, username, password)) - .then(() => { - return Bluebird.join( - this.retrieveEmails(database, username), - this.retrieveGroups(database, username) - ).spread((emails: string[], groups: string[]) => { - return { emails: emails, groups: groups }; - }); - }); - }); - } - - getEmails(username: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.retrieveEmails(database, username)); - }); - } - - getGroups(username: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.retrieveGroups(database, username)); - }); - } - - updatePassword(username: string, newPassword: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.replacePassword(database, username, newPassword)); - }); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/file/ReadWriteQueue.ts b/themes/matrix/server/src/lib/authentication/backends/file/ReadWriteQueue.ts deleted file mode 100644 index 957ddaec5..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/file/ReadWriteQueue.ts +++ /dev/null @@ -1,60 +0,0 @@ -import Fs = require("fs"); - -type Callback = (err: Error, data?: string) => void; -type ContentAndCallback = [string, Callback] | [string, string, Callback]; - -/** - * WriteQueue is a queue synchronizing writes to a file. - * - * Example of use: - * - * queue.add(mycontent, (err) => { - * // do whatever you want here. - * queue.next(); - * }) - */ -export class ReadWriteQueue { - private filePath: string; - private queue: ContentAndCallback[]; - - constructor (filePath: string) { - this.queue = []; - this.filePath = filePath; - } - - next () { - if (this.queue.length === 0) - return; - - const task = this.queue[0]; - - if (task[0] == "write") { - Fs.writeFile(this.filePath, task[1], "utf-8", (err) => { - this.queue.shift(); - const cb = task[2] as Callback; - cb(err); - }); - } - else if (task[0] == "read") { - Fs.readFile(this.filePath, { encoding: "utf-8"} , (err, data) => { - this.queue.shift(); - const cb = task[1] as Callback; - cb(err, data); - }); - } - } - - write (content: string, cb: Callback) { - this.queue.push(["write", content, cb]); - if (this.queue.length === 1) { - this.next(); - } - } - - read (cb: Callback) { - this.queue.push(["read", cb]); - if (this.queue.length === 1) { - this.next(); - } - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/ISession.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/ISession.ts deleted file mode 100644 index da2c74433..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/ISession.ts +++ /dev/null @@ -1,12 +0,0 @@ - -import BluebirdPromise = require("bluebird"); - -export interface ISession { - open(): BluebirdPromise; - close(): BluebirdPromise; - - searchUserDn(username: string): BluebirdPromise; - searchEmails(username: string): BluebirdPromise; - searchGroups(username: string): BluebirdPromise; - modifyPassword(username: string, newPassword: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/ISessionFactory.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/ISessionFactory.ts deleted file mode 100644 index 014d1eea5..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/ISessionFactory.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import { ISession } from "./ISession"; - -export interface ISessionFactory { - create(userDN: string, password: string): ISession; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts deleted file mode 100644 index f4a6e6306..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts +++ /dev/null @@ -1,386 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); - -import { LdapUsersDatabase } from "./LdapUsersDatabase"; - -import { SessionFactoryStub } from "./SessionFactoryStub.spec"; -import { SessionStub } from "./SessionStub.spec"; - -const ADMIN_USER_DN = "cn=admin,dc=example,dc=com"; -const ADMIN_PASSWORD = "password"; - -describe("ldap/connector/LdapUsersDatabase", function() { - let sessionFactory: SessionFactoryStub; - let usersDatabase: LdapUsersDatabase; - - const USERNAME = "user"; - const PASSWORD = "pass"; - const NEW_PASSWORD = "pass2"; - - const LDAP_CONFIG = { - url: "http://localhost:324", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={0}", - mail_attribute: "mail", - group_name_attribute: "cn", - user: ADMIN_USER_DN, - password: ADMIN_PASSWORD - }; - - beforeEach(function() { - sessionFactory = new SessionFactoryStub(); - usersDatabase = new LdapUsersDatabase(sessionFactory, LDAP_CONFIG); - }) - - describe("checkUserPassword", function() { - it("should return groups and emails when user/password matches", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME).returns(Bluebird.resolve(emails)); - adminSession.searchGroupsStub.withArgs(USERNAME).returns(Bluebird.resolve(groups)); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => { - Assert.deepEqual(groupsAndEmails.groups, groups); - Assert.deepEqual(groupsAndEmails.emails, emails); - }) - }); - - it("should fail when username/password is wrong", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - - userSession.openStub.returns(Bluebird.reject(new Error("Failed binding"))); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when admin binding fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.reject(new Error("Failed binding"))); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.notCalled); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when search for user dn fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.reject(new Error("Failed searching user dn"))); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.notCalled); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when groups retrieval fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME) - .returns(Bluebird.resolve(emails)); - adminSession.searchGroupsStub.withArgs(USERNAME) - .returns(Bluebird.reject(new Error("Failed retrieving groups"))); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - }) - }); - - it("should fail when emails retrieval fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME) - .returns(Bluebird.reject(new Error("Emails retrieval failed"))); - adminSession.searchGroupsStub.withArgs(USERNAME) - .returns(Bluebird.resolve(groups)); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - }) - }); - }); - - describe("getEmails", function() { - it("should succefully retrieves email", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.resolve(emails)); - - return usersDatabase.getEmails(USERNAME) - .then((foundEmails) => { - Assert(session.closeStub.called); - Assert.deepEqual(foundEmails, emails); - }) - }); - - it("should fail when binding fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbinding fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.resolve(emails)); - session.closeStub.returns(Bluebird.reject(new Error("Unbinding failed"))); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when search fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.reject(new Error("Search failed"))); - session.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - }); - - - describe("getGroups", function() { - it("should succefully retrieves groups", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.resolve(groups)); - - return usersDatabase.getGroups(USERNAME) - .then((foundGroups) => { - Assert(session.closeStub.called); - Assert.deepEqual(foundGroups, groups); - }) - }); - - it("should fail when binding fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbinding fails", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.resolve(groups)); - session.closeStub.returns(Bluebird.reject(new Error("Unbinding failed"))); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when search fails", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.reject(new Error("Search failed"))); - session.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - }); - - - describe("updatePassword", function() { - it("should successfully update password", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => { - Assert(session.modifyPasswordStub.calledWith(USERNAME, NEW_PASSWORD)); - Assert(session.closeStub.called); - }) - }); - - it("should fail when binding fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when update fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.reject(new Error("Update failed"))); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbind fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.reject(new Error("Unbind failed"))); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts deleted file mode 100644 index edda62ec6..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts +++ /dev/null @@ -1,107 +0,0 @@ -import Bluebird = require("bluebird"); -import { IUsersDatabase } from "../IUsersDatabase"; -import { ISessionFactory } from "./ISessionFactory"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { ISession } from "./ISession"; -import { GroupsAndEmails } from "../GroupsAndEmails"; -import Exceptions = require("../../../Exceptions"); - -type SessionCallback = (session: ISession) => Bluebird; - -export class LdapUsersDatabase implements IUsersDatabase { - private sessionFactory: ISessionFactory; - private configuration: LdapConfiguration; - - constructor( - sessionFactory: ISessionFactory, - configuration: LdapConfiguration) { - this.sessionFactory = sessionFactory; - this.configuration = configuration; - } - - private withSession( - username: string, - password: string, - cb: SessionCallback): Bluebird { - const session = this.sessionFactory.create(username, password); - return session.open() - .then(() => cb(session)) - .finally(() => session.close()); - } - - checkUserPassword(username: string, password: string): Bluebird { - const that = this; - function verifyUserPassword(userDN: string) { - return that.withSession( - userDN, - password, - (session) => Bluebird.resolve() - ); - } - - function getInfo(session: ISession) { - return Bluebird.join( - session.searchGroups(username), - session.searchEmails(username) - ) - .spread((groups: string[], emails: string[]) => { - return { groups: groups, emails: emails }; - }); - } - - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchUserDn(username) - .then(verifyUserPassword) - .then(() => getInfo(session)); - }) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError(err.message))); - } - - getEmails(username: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchEmails(username); - } - ) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError("Failed during email retrieval: " + err.message)) - ); - } - - getGroups(username: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchGroups(username); - } - ) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError("Failed during email retrieval: " + err.message)) - ); - } - - updatePassword(username: string, newPassword: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.modifyPassword(username, newPassword); - } - ) - .catch(function (err: Error) { - return Bluebird.reject( - new Exceptions.LdapError( - "Error while updating password: " + err.message)); - }); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts deleted file mode 100644 index 9dedfcb7c..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { SessionStub } from "./SessionStub.spec"; -import { SafeSession } from "./SafeSession"; - -describe("ldap/SanitizedClient", function () { - let client: SafeSession; - - beforeEach(function () { - const clientStub = new SessionStub(); - clientStub.searchUserDnStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.searchGroupsStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.searchEmailsStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.modifyPasswordStub.onCall(0).returns(BluebirdPromise.resolve()); - client = new SafeSession(clientStub); - }); - - describe("special chars are used", function () { - it("should fail when special chars are used in searchUserDn", function () { - // potential ldap injection"; - return client.searchUserDn("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in searchGroups", function () { - // potential ldap injection"; - return client.searchGroups("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in searchEmails", function () { - // potential ldap injection"; - return client.searchEmails("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in modifyPassword", function () { - // potential ldap injection"; - return client.modifyPassword("cn=dummy_user,ou=groupgs", "abc") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - }); - - describe("no special chars are used", function() { - it("should succeed when no special chars are used in searchUserDn", function () { - return client.searchUserDn("dummy_user"); - }); - - it("should succeed when no special chars are used in searchGroups", function () { - return client.searchGroups("dummy_user"); - }); - - it("should succeed when no special chars are used in searchEmails", function () { - return client.searchEmails("dummy_user"); - }); - - it("should succeed when no special chars are used in modifyPassword", function () { - return client.modifyPassword("dummy_user", "abc"); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/SafeSession.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/SafeSession.ts deleted file mode 100644 index 572209066..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/SafeSession.ts +++ /dev/null @@ -1,62 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ISession } from "./ISession"; -import { Sanitizer } from "./Sanitizer"; - -const SPECIAL_CHAR_USED_MESSAGE = "Special character used in LDAP query."; - - -export class SafeSession implements ISession { - private sesion: ISession; - - constructor(sesion: ISession) { - this.sesion = sesion; - } - - open(): BluebirdPromise { - return this.sesion.open(); - } - - close(): BluebirdPromise { - return this.sesion.close(); - } - - searchGroups(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchGroups(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - searchUserDn(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchUserDn(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - searchEmails(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchEmails(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - modifyPassword(username: string, newPassword: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.modifyPassword(sanitizedUsername, newPassword); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } -} diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts deleted file mode 100644 index 9dd33fed4..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import Assert = require("assert"); -import { Sanitizer } from "./Sanitizer"; - -describe("ldap/InputsSanitizer", function () { - it("should fail when special characters are used", function () { - Assert.throws(() => { Sanitizer.sanitize("ab,c"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a\\bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a'bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a#bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a+bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a { Sanitizer.sanitize("a>bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a;bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a\"bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a=bc"); }, Error); - }); - - it("should return original string", function () { - Assert.equal(Sanitizer.sanitize("abcdef"), "abcdef"); - }); - - it("should trim", function () { - Assert.throws(() => { Sanitizer.sanitize(" abc "); }, Error); - }); -}); diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/Sanitizer.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/Sanitizer.ts deleted file mode 100644 index be74132ad..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/Sanitizer.ts +++ /dev/null @@ -1,25 +0,0 @@ - -// returns true for 1 or more matches, where 'a' is an array and 'b' is a search string or an array of multiple search strings -function contains(a: string, character: string) { - // string match - return a.indexOf(character) > -1; -} - -function containsOneOf(s: string, characters: string[]) { - return characters - .map((character: string) => { return contains(s, character); }) - .reduce((acc: boolean, current: boolean) => { return acc || current; }, false); -} - -export class Sanitizer { - static sanitize(input: string): string { - const forbiddenChars = [",", "\\", "'", "#", "+", "<", ">", ";", "\"", "="]; - if (containsOneOf(input, forbiddenChars)) - throw new Error("Input containing unsafe characters."); - - if (input != input.trim()) - throw new Error("Input has unexpected spaces."); - - return input; - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/Session.spec.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/Session.spec.ts deleted file mode 100644 index d55f6a805..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/Session.spec.ts +++ /dev/null @@ -1,127 +0,0 @@ - -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Session } from "./Session"; -import { ConnectorFactoryStub } from "./connector/ConnectorFactoryStub.spec"; -import { ConnectorStub } from "./connector/ConnectorStub.spec"; - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import Winston = require("winston"); - -describe("ldap/Session", function () { - const USERNAME = "username"; - const ADMIN_USER_DN = "cn=admin,dc=example,dc=com"; - const ADMIN_PASSWORD = "password"; - - it("should replace {0} by username when searching for groups in LDAP", function () { - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member=cn={0},ou=users,dc=example,dc=com", - group_name_attribute: "cn", - mail_attribute: "mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const connectorStub = new ConnectorStub(); - connectorStub.searchAsyncStub.returns(BluebirdPromise.resolve([{ - cn: "group1" - }])); - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, connectorStub, Winston); - - return client.searchGroups("user1") - .then(function () { - Assert.equal(connectorStub.searchAsyncStub.getCall(0).args[1].filter, - "member=cn=user1,ou=users,dc=example,dc=com"); - }); - }); - - it("should replace {dn} by user DN when searching for groups in LDAP", function () { - const USER_DN = "cn=user1,ou=users,dc=example,dc=com"; - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn", - mail_attribute: "mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const ldapClient = new ConnectorStub(); - - // Retrieve user DN - ldapClient.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: "cn=user1" - }).returns(BluebirdPromise.resolve([{ - dn: USER_DN - }])); - - // Retrieve groups - ldapClient.searchAsyncStub.withArgs("ou=groups,dc=example,dc=com", { - scope: "sub", - attributes: ["cn"], - filter: "member=" + USER_DN - }).returns(BluebirdPromise.resolve([{ - cn: "group1" - }])); - - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, ldapClient, Winston); - - return client.searchGroups("user1") - .then(function (groups: string[]) { - Assert.deepEqual(groups, ["group1"]); - }); - }); - - it("should retrieve mail from custom attribute", function () { - const USER_DN = "cn=user1,ou=users,dc=example,dc=com"; - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn", - mail_attribute: "custom_mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const connector = new ConnectorStub(); - // Retrieve user DN - connector.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: "cn=user1" - }).returns(BluebirdPromise.resolve([{ - dn: USER_DN - }])); - - // Retrieve email - connector.searchAsyncStub.withArgs("cn=user1,ou=users,dc=example,dc=com", { - scope: "base", - sizeLimit: 1, - attributes: ["custom_mail"], - }).returns(BluebirdPromise.resolve([{ - custom_mail: "user1@example.com" - }])); - - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, connector, Winston); - - return client.searchEmails("user1") - .then(function (emails: string[]) { - Assert.deepEqual(emails, ["user1@example.com"]); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/Session.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/Session.ts deleted file mode 100644 index e0284b3c4..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/Session.ts +++ /dev/null @@ -1,156 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import exceptions = require("../../../Exceptions"); -import { EventEmitter } from "events"; -import { ISession } from "./ISession"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Winston } from "../../../../../types/Dependencies"; -import Util = require("util"); -import { HashGenerator } from "../../../utils/HashGenerator"; -import { IConnector } from "./connector/IConnector"; - -export class Session implements ISession { - private userDN: string; - private password: string; - private connector: IConnector; - private logger: Winston; - private options: LdapConfiguration; - - private groupsSearchBase: string; - private usersSearchBase: string; - - constructor(userDN: string, password: string, options: LdapConfiguration, - connector: IConnector, logger: Winston) { - this.options = options; - this.logger = logger; - this.userDN = userDN; - this.password = password; - this.connector = connector; - - this.groupsSearchBase = (this.options.additional_groups_dn) - ? Util.format("%s,%s", this.options.additional_groups_dn, this.options.base_dn) - : this.options.base_dn; - - this.usersSearchBase = (this.options.additional_users_dn) - ? Util.format("%s,%s", this.options.additional_users_dn, this.options.base_dn) - : this.options.base_dn; - } - - open(): BluebirdPromise { - this.logger.debug("LDAP: Bind user '%s'", this.userDN); - return this.connector.bindAsync(this.userDN, this.password) - .error(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapBindError(err.message)); - }); - } - - close(): BluebirdPromise { - this.logger.debug("LDAP: Unbind user '%s'", this.userDN); - return this.connector.unbindAsync() - .error(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapBindError(err.message)); - }); - } - - private createGroupsFilter(userGroupsFilter: string, username: string): BluebirdPromise { - if (userGroupsFilter.indexOf("{0}") > 0) { - return BluebirdPromise.resolve(userGroupsFilter.replace("{0}", username)); - } - else if (userGroupsFilter.indexOf("{dn}") > 0) { - return this.searchUserDn(username) - .then(function (userDN: string) { - return BluebirdPromise.resolve(userGroupsFilter.replace("{dn}", userDN)); - }); - } - return BluebirdPromise.resolve(userGroupsFilter); - } - - searchGroups(username: string): BluebirdPromise { - const that = this; - return this.createGroupsFilter(this.options.groups_filter, username) - .then(function (groupsFilter: string) { - that.logger.debug("Computed groups filter is %s", groupsFilter); - const query = { - scope: "sub", - attributes: [that.options.group_name_attribute], - filter: groupsFilter - }; - return that.connector.searchAsync(that.groupsSearchBase, query); - }) - .then(function (docs: { cn: string }[]) { - const groups = docs.map((doc: any) => { return doc.cn; }); - that.logger.debug("LDAP: groups of user %s are [%s]", username, groups.join(",")); - return BluebirdPromise.resolve(groups); - }); - } - - searchUserDn(username: string): BluebirdPromise { - const that = this; - const filter = this.options.users_filter.replace("{0}", username); - this.logger.debug("Computed users filter is %s", filter); - const query = { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: filter - }; - - that.logger.debug("LDAP: searching for user dn of %s", username); - return that.connector.searchAsync(this.usersSearchBase, query) - .then(function (users: { dn: string }[]) { - if (users.length > 0) { - that.logger.debug("LDAP: retrieved user dn is %s", users[0].dn); - return BluebirdPromise.resolve(users[0].dn); - } - return BluebirdPromise.reject(new Error( - Util.format("No user DN found for user '%s'", username))); - }); - } - - searchEmails(username: string): BluebirdPromise { - const that = this; - const query = { - scope: "base", - sizeLimit: 1, - attributes: [this.options.mail_attribute] - }; - - return this.searchUserDn(username) - .then(function (userDN) { - return that.connector.searchAsync(userDN, query); - }) - .then(function (docs: { [mail_attribute: string]: string }[]) { - const emails: string[] = docs - .filter((d) => { return typeof d[that.options.mail_attribute] === "string"; }) - .map((d) => { return d[that.options.mail_attribute]; }); - that.logger.debug("LDAP: emails of user '%s' are %s", username, emails); - return BluebirdPromise.resolve(emails); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapError("Error while searching emails. " + err.stack)); - }); - } - - modifyPassword(username: string, newPassword: string): BluebirdPromise { - const that = this; - this.logger.debug("LDAP: update password of user '%s'", username); - return this.searchUserDn(username) - .then(function (userDN: string) { - return BluebirdPromise.join( - HashGenerator.ssha512(newPassword), - BluebirdPromise.resolve(userDN)); - }) - .then(function (res: string[]) { - const change = { - operation: "replace", - modification: { - userPassword: res[0] - } - }; - that.logger.debug("Password new='%s'", change.modification.userPassword); - return that.connector.modifyAsync(res[1], change); - }) - .then(function () { - return that.connector.unbindAsync(); - }); - } -} diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/SessionFactory.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/SessionFactory.ts deleted file mode 100644 index 0b6c4bff3..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/SessionFactory.ts +++ /dev/null @@ -1,37 +0,0 @@ -import Ldapjs = require("ldapjs"); -import Winston = require("winston"); - -import { IConnectorFactory } from "./connector/IConnectorFactory"; -import { ISessionFactory } from "./ISessionFactory"; -import { ISession } from "./ISession"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Session } from "./Session"; -import { SafeSession } from "./SafeSession"; - - -export class SessionFactory implements ISessionFactory { - private config: LdapConfiguration; - private connectorFactory: IConnectorFactory; - private logger: typeof Winston; - - constructor(ldapConfiguration: LdapConfiguration, - connectorFactory: IConnectorFactory, - logger: typeof Winston) { - this.config = ldapConfiguration; - this.connectorFactory = connectorFactory; - this.logger = logger; - } - - create(userDN: string, password: string): ISession { - const connector = this.connectorFactory.create(); - return new SafeSession( - new Session( - userDN, - password, - this.config, - connector, - this.logger - ) - ); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts deleted file mode 100644 index face3930d..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); - -import { ISession } from "./ISession"; -import { ISessionFactory } from "./ISessionFactory"; - -export class SessionFactoryStub implements ISessionFactory { - createStub: Sinon.SinonStub; - - constructor() { - this.createStub = Sinon.stub(); - } - - create(userDN: string, password: string): ISession { - return this.createStub(userDN, password); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts deleted file mode 100644 index 5faf2ba18..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); - -import { ISession } from "./ISession"; - -export class SessionStub implements ISession { - openStub: Sinon.SinonStub; - closeStub: Sinon.SinonStub; - searchUserDnStub: Sinon.SinonStub; - searchEmailsStub: Sinon.SinonStub; - searchGroupsStub: Sinon.SinonStub; - modifyPasswordStub: Sinon.SinonStub; - - constructor() { - this.openStub = Sinon.stub(); - this.closeStub = Sinon.stub(); - this.searchUserDnStub = Sinon.stub(); - this.searchEmailsStub = Sinon.stub(); - this.searchGroupsStub = Sinon.stub(); - this.modifyPasswordStub = Sinon.stub(); - } - - open(): Bluebird { - return this.openStub(); - } - - close(): Bluebird { - return this.closeStub(); - } - - searchUserDn(username: string): Bluebird { - return this.searchUserDnStub(username); - } - - searchEmails(username: string): Bluebird { - return this.searchEmailsStub(username); - } - - searchGroups(username: string): Bluebird { - return this.searchGroupsStub(username); - } - - modifyPassword(username: string, newPassword: string): Bluebird { - return this.modifyPasswordStub(username, newPassword); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/connector/Connector.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/connector/Connector.ts deleted file mode 100644 index 2542ea7f9..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/connector/Connector.ts +++ /dev/null @@ -1,69 +0,0 @@ -import LdapJs = require("ldapjs"); -import EventEmitter = require("events"); -import Bluebird = require("bluebird"); -import { IConnector } from "./IConnector"; -import Exceptions = require("../../../../Exceptions"); - -interface SearchEntry { - object: any; -} - -export interface ClientAsync { - on(event: string, callback: (data?: any) => void): void; - bindAsync(username: string, password: string): Bluebird; - unbindAsync(): Bluebird; - searchAsync(base: string, query: LdapJs.SearchOptions): Bluebird; - modifyAsync(userdn: string, change: LdapJs.Change): Bluebird; -} - -export class Connector implements IConnector { - private client: ClientAsync; - - constructor(url: string, ldapjs: typeof LdapJs) { - const ldapClient = ldapjs.createClient({ - url: url, - reconnect: true - }); - - /*const clientLogger = (ldapClient as any).log; - if (clientLogger) { - clientLogger.level("trace"); - }*/ - - this.client = Bluebird.promisifyAll(ldapClient) as any; - } - - bindAsync(username: string, password: string): Bluebird { - return this.client.bindAsync(username, password); - } - - unbindAsync(): Bluebird { - return this.client.unbindAsync(); - } - - searchAsync(base: string, query: any): Bluebird { - const that = this; - return this.client.searchAsync(base, query) - .then(function (res: EventEmitter) { - const doc: SearchEntry[] = []; - return new Bluebird((resolve, reject) => { - res.on("searchEntry", function (entry: SearchEntry) { - doc.push(entry.object); - }); - res.on("error", function (err: Error) { - reject(new Exceptions.LdapSearchError(err.message)); - }); - res.on("end", function () { - resolve(doc); - }); - }); - }) - .catch(function (err: Error) { - return Bluebird.reject(new Exceptions.LdapSearchError(err.message)); - }); - } - - modifyAsync(dn: string, changeRequest: any): Bluebird { - return this.client.modifyAsync(dn, changeRequest); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts deleted file mode 100644 index 61fef07a4..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IConnector } from "./IConnector"; -import { Connector } from "./Connector"; -import { LdapConfiguration } from "../../../../configuration/schema/LdapConfiguration"; -import { Ldapjs } from "Dependencies"; - -export class ConnectorFactory { - private configuration: LdapConfiguration; - private ldapjs: Ldapjs; - - constructor(configuration: LdapConfiguration, ldapjs: Ldapjs) { - this.configuration = configuration; - this.ldapjs = ldapjs; - } - - create(): IConnector { - return new Connector(this.configuration.url, this.ldapjs); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts deleted file mode 100644 index d11fa6386..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); - -import { IConnectorFactory } from "./IConnectorFactory"; -import { IConnector } from "./IConnector"; - -export class ConnectorFactoryStub implements IConnectorFactory { - createStub: Sinon.SinonStub; - - constructor() { - this.createStub = Sinon.stub(); - } - - create(): IConnector { - return this.createStub(); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts deleted file mode 100644 index 0b78225bf..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); - -import { IConnector } from "./IConnector"; - -export class ConnectorStub implements IConnector { - bindAsyncStub: Sinon.SinonStub; - unbindAsyncStub: Sinon.SinonStub; - searchAsyncStub: Sinon.SinonStub; - modifyAsyncStub: Sinon.SinonStub; - - constructor() { - this.bindAsyncStub = Sinon.stub(); - this.unbindAsyncStub = Sinon.stub(); - this.searchAsyncStub = Sinon.stub(); - this.modifyAsyncStub = Sinon.stub(); - } - - bindAsync(username: string, password: string): BluebirdPromise { - return this.bindAsyncStub(username, password); - } - - unbindAsync(): BluebirdPromise { - return this.unbindAsyncStub(); - } - - searchAsync(base: string, query: any): BluebirdPromise { - return this.searchAsyncStub(base, query); - } - - modifyAsync(dn: string, changeRequest: any): BluebirdPromise { - return this.modifyAsyncStub(dn, changeRequest); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/connector/IConnector.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/connector/IConnector.ts deleted file mode 100644 index 1e63ab193..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/connector/IConnector.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Bluebird = require("bluebird"); -import EventEmitter = require("events"); - -export interface IConnector { - bindAsync(username: string, password: string): Bluebird; - unbindAsync(): Bluebird; - searchAsync(base: string, query: any): Bluebird; - modifyAsync(dn: string, changeRequest: any): Bluebird; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts b/themes/matrix/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts deleted file mode 100644 index f9ed65eff..000000000 --- a/themes/matrix/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { IConnector } from "./IConnector"; - -export interface IConnectorFactory { - create(): IConnector; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/totp/ITotpHandler.ts b/themes/matrix/server/src/lib/authentication/totp/ITotpHandler.ts deleted file mode 100644 index d600d31e4..000000000 --- a/themes/matrix/server/src/lib/authentication/totp/ITotpHandler.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { TOTPSecret } from "../../../../types/TOTPSecret"; - -export interface ITotpHandler { - generate(label: string, issuer: string): TOTPSecret; - validate(token: string, secret: string): boolean; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/totp/TotpHandler.spec.ts b/themes/matrix/server/src/lib/authentication/totp/TotpHandler.spec.ts deleted file mode 100644 index 67cffa638..000000000 --- a/themes/matrix/server/src/lib/authentication/totp/TotpHandler.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import { TotpHandler } from "./TotpHandler"; -import Sinon = require("sinon"); -import Speakeasy = require("speakeasy"); -import Assert = require("assert"); - -describe("authentication/totp/TotpHandler", function() { - let totpValidator: TotpHandler; - let validateStub: Sinon.SinonStub; - - beforeEach(() => { - validateStub = Sinon.stub(Speakeasy.totp, "verify"); - totpValidator = new TotpHandler(Speakeasy); - }); - - afterEach(function() { - validateStub.restore(); - }); - - it("should validate the TOTP token", function() { - const totp_secret = "NBD2ZV64R9UV1O7K"; - const token = "token"; - validateStub.withArgs({ - secret: totp_secret, - token: token, - encoding: "base32", - window: 1 - }).returns(true); - Assert(totpValidator.validate(token, totp_secret)); - }); - - it("should not validate a wrong TOTP token", function() { - const totp_secret = "NBD2ZV64R9UV1O7K"; - const token = "wrong token"; - validateStub.returns(false); - Assert(!totpValidator.validate(token, totp_secret)); - }); -}); - diff --git a/themes/matrix/server/src/lib/authentication/totp/TotpHandler.ts b/themes/matrix/server/src/lib/authentication/totp/TotpHandler.ts deleted file mode 100644 index dfab502a7..000000000 --- a/themes/matrix/server/src/lib/authentication/totp/TotpHandler.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ITotpHandler } from "./ITotpHandler"; -import { TOTPSecret } from "../../../../types/TOTPSecret"; -import Speakeasy = require("speakeasy"); - -const TOTP_ENCODING = "base32"; -const WINDOW: number = 1; - -export class TotpHandler implements ITotpHandler { - private speakeasy: typeof Speakeasy; - - constructor(speakeasy: typeof Speakeasy) { - this.speakeasy = speakeasy; - } - - generate(label: string, issuer: string): TOTPSecret { - const secret = this.speakeasy.generateSecret({ - otpauth_url: false - }) as TOTPSecret; - - secret.otpauth_url = this.speakeasy.otpauthURL({ - secret: secret.ascii, - label: label, - issuer: issuer - }); - return secret; - } - - validate(token: string, secret: string): boolean { - return this.speakeasy.totp.verify({ - secret: secret, - encoding: TOTP_ENCODING, - token: token, - window: WINDOW - }); - } -} diff --git a/themes/matrix/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts b/themes/matrix/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts deleted file mode 100644 index ea93330d9..000000000 --- a/themes/matrix/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import { ITotpHandler } from "./ITotpHandler"; -import { TOTPSecret } from "../../../../types/TOTPSecret"; - -export class TotpHandlerStub implements ITotpHandler { - generateStub: Sinon.SinonStub; - validateStub: Sinon.SinonStub; - - constructor() { - this.generateStub = Sinon.stub(); - this.validateStub = Sinon.stub(); - } - - generate(label: string, issuer: string): TOTPSecret { - return this.generateStub(label, issuer); - } - - validate(token: string, secret: string): boolean { - return this.validateStub(token, secret); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/u2f/IU2fHandler.ts b/themes/matrix/server/src/lib/authentication/u2f/IU2fHandler.ts deleted file mode 100644 index b9b7d6f26..000000000 --- a/themes/matrix/server/src/lib/authentication/u2f/IU2fHandler.ts +++ /dev/null @@ -1,9 +0,0 @@ -import U2f = require("u2f"); - -export interface IU2fHandler { - request(appId: string, keyHandle?: string): U2f.Request; - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error; - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authentication/u2f/U2fHandler.ts b/themes/matrix/server/src/lib/authentication/u2f/U2fHandler.ts deleted file mode 100644 index bf3891e5b..000000000 --- a/themes/matrix/server/src/lib/authentication/u2f/U2fHandler.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { IU2fHandler } from "./IU2fHandler"; -import U2f = require("u2f"); - -export class U2fHandler implements IU2fHandler { - private u2f: typeof U2f; - - constructor(u2f: typeof U2f) { - this.u2f = u2f; - } - - request(appId: string, keyHandle?: string): U2f.Request { - return this.u2f.request(appId, keyHandle); - } - - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error { - return this.u2f.checkRegistration(registrationRequest, registrationResponse); - } - - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error { - return this.u2f.checkSignature(signatureRequest, signatureResponse, publicKey); - } -} diff --git a/themes/matrix/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts b/themes/matrix/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts deleted file mode 100644 index 135d7eb06..000000000 --- a/themes/matrix/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import U2f = require("u2f"); -import { IU2fHandler } from "./IU2fHandler"; - - -export class U2fHandlerStub implements IU2fHandler { - requestStub: Sinon.SinonStub; - checkRegistrationStub: Sinon.SinonStub; - checkSignatureStub: Sinon.SinonStub; - - constructor() { - this.requestStub = Sinon.stub(); - this.checkRegistrationStub = Sinon.stub(); - this.checkSignatureStub = Sinon.stub(); - } - - request(appId: string, keyHandle?: string): U2f.Request { - return this.requestStub(appId, keyHandle); - } - - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error { - return this.checkRegistrationStub(registrationRequest, registrationResponse); - } - - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error { - return this.checkSignatureStub(signatureRequest, signatureResponse, publicKey); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authorization/Authorizer.spec.ts b/themes/matrix/server/src/lib/authorization/Authorizer.spec.ts deleted file mode 100644 index 586814042..000000000 --- a/themes/matrix/server/src/lib/authorization/Authorizer.spec.ts +++ /dev/null @@ -1,372 +0,0 @@ - -import Assert = require("assert"); -import winston = require("winston"); -import { Authorizer } from "./Authorizer"; -import { ACLConfiguration, ACLRule } from "../configuration/schema/AclConfiguration"; -import { Level } from "./Level"; - -describe("authorization/Authorizer", function () { - let authorizer: Authorizer; - let configuration: ACLConfiguration; - - describe("configuration is null", function() { - it("should allow access to anything, anywhere for anybody", function() { - configuration = undefined; - authorizer = new Authorizer(configuration, winston); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc"}, {user: "user1", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user2", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "admin.example.com", resource: "/"}, {user: "user3", groups: ["group3"]}), Level.BYPASS); - }); - }); - - describe("configuration is not null", function () { - beforeEach(function () { - configuration = { - default_policy: "deny", - rules: [] - }; - authorizer = new Authorizer(configuration, winston); - }); - - describe("check access control with default policy to deny", function () { - beforeEach(function () { - configuration.default_policy = "deny"; - }); - - it("should deny access when no rule is provided", function () { - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should control access when multiple domain matcher is provided", function () { - configuration.rules = [{ - domain: "*.mail.example.com", - policy: "two_factor", - subject: "user:user1", - resources: [".*"] - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "mx1.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mx1.server.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should allow access to all resources when resources is not provided", function () { - configuration.rules = [{ - domain: "*.mail.example.com", - policy: "two_factor", - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "mx1.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mx1.server.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - describe("check user rules", function () { - it("should allow access when user has a matching allowing rule", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/another/resource"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should deny to other users", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user2", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/another/resource"}, {user: "user2", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, {user: "user2", groups: ["group1"]}), Level.DENY); - }); - - it("should allow user access only to specific resources", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["/private/.*", "^/begin", "/end$"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/class"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/middle/private/class"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/begin"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/not/begin"}, {user: "user1", groups: ["group1"]}), Level.DENY); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc/end"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc/end/x"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should allow access to multiple domains", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }, { - domain: "home1.example.com", - policy: "one_factor", - resources: [".*"], - subject: "user:user1" - }, { - domain: "home2.example.com", - policy: "deny", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home1.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.ONE_FACTOR); - Assert.equal(authorizer.authorization({domain: "home2.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home3.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should apply rules in order", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "one_factor", - resources: ["/my/private/resource"], - subject: "user:user1" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/my/private/.*"], - subject: "user:user1" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/my/.*"], - subject: "user:user1" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/poney"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/private/duck"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/private/resource"}, {user: "user1", groups: ["group1"]}), Level.ONE_FACTOR); - }); - }); - - describe("check group rules", function () { - it("should allow access when user is in group having a matching allowing rule", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/$"], - subject: "group:group1" - }, { - domain: "home.example.com", - policy: "one_factor", - resources: ["^/test$"], - subject: "group:group2" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/private$"], - subject: "group:group2" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.ONE_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - }); - }); - }); - - describe("check any rules", function () { - it("should control access when any rules are defined", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "bypass", - resources: ["^/public$"] - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/private$"] - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, - {user: "user4", groups: ["group5"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user4", groups: ["group5"]}), Level.DENY); - }); - }); - - describe("check access control with default policy to allow", function () { - beforeEach(function () { - configuration.default_policy = "bypass"; - }); - - it("should allow access to anything when no rule is provided", function () { - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - }); - - it("should deny access to one resource when defined", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["/test"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - }); - }); - - describe("check access control with complete use case", function () { - beforeEach(function () { - configuration.default_policy = "deny"; - }); - - it("should control access of multiple user (real use case)", function () { - // Let say we have three users: admin, john, harry. - // admin is in groups ["admins"] - // john is in groups ["dev", "admin-private"] - // harry is in groups ["dev"] - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/public$", "^/$"] - }, { - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "group:admins" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/?.*"], - subject: "group:admin-private" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/john$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/harry"], - subject: "user:harry" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - }); - - it("should allow when allowed at group level and denied at user level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"], - subject: "group:dev" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should allow access when allowed at 'any' level and denied at user level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should allow access when allowed at 'any' level and denied at group level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "group:dev" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should respect rules precedence", function () { - // the priority from least to most is 'default_policy', 'all', 'group', 'user' - // and the first rules in each category as a lower priority than the latest. - // You can think of it that way: they override themselves inside each category. - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "group:dev" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - }); - }); - }); -}); diff --git a/themes/matrix/server/src/lib/authorization/Authorizer.ts b/themes/matrix/server/src/lib/authorization/Authorizer.ts deleted file mode 100644 index 889b7ec20..000000000 --- a/themes/matrix/server/src/lib/authorization/Authorizer.ts +++ /dev/null @@ -1,85 +0,0 @@ - -import { ACLConfiguration, ACLPolicy, ACLRule } from "../configuration/schema/AclConfiguration"; -import { IAuthorizer } from "./IAuthorizer"; -import { Winston } from "../../../types/Dependencies"; -import { MultipleDomainMatcher } from "./MultipleDomainMatcher"; -import { Level } from "./Level"; -import { Object } from "./Object"; -import { Subject } from "./Subject"; - -function MatchDomain(actualDomain: string) { - return function (rule: ACLRule): boolean { - return MultipleDomainMatcher.match(actualDomain, rule.domain); - }; -} - -function MatchResource(actualResource: string) { - return function (rule: ACLRule): boolean { - // If resources key is not provided, the rule applies to all resources. - if (!rule.resources) return true; - - for (let i = 0; i < rule.resources.length; ++i) { - const regexp = new RegExp(rule.resources[i]); - if (regexp.test(actualResource)) return true; - } - return false; - }; -} - -function MatchSubject(subject: Subject) { - return (rule: ACLRule) => { - // If no subject, matches anybody - if (!rule.subject) return true; - - if (rule.subject.startsWith("user:")) { - const ruleUser = rule.subject.split(":")[1]; - if (subject.user == ruleUser) return true; - } - - if (rule.subject.startsWith("group:")) { - const ruleGroup = rule.subject.split(":")[1]; - if (subject.groups.indexOf(ruleGroup) > -1) return true; - } - return false; - }; -} - -export class Authorizer implements IAuthorizer { - private logger: Winston; - private readonly configuration: ACLConfiguration; - - constructor(configuration: ACLConfiguration, logger_: Winston) { - this.logger = logger_; - this.configuration = configuration; - } - - private getMatchingRules(object: Object, subject: Subject): ACLRule[] { - const rules = this.configuration.rules; - if (!rules) return []; - return rules - .filter(MatchDomain(object.domain)) - .filter(MatchResource(object.resource)) - .filter(MatchSubject(subject)); - } - - private ruleToLevel(policy: string): Level { - if (policy == "bypass") { - return Level.BYPASS; - } else if (policy == "one_factor") { - return Level.ONE_FACTOR; - } else if (policy == "two_factor") { - return Level.TWO_FACTOR; - } - return Level.DENY; - } - - authorization(object: Object, subject: Subject): Level { - if (!this.configuration) return Level.BYPASS; - - const rules = this.getMatchingRules(object, subject); - - return (rules.length > 0) - ? this.ruleToLevel(rules[0].policy) // extract the policy of the first matching rule - : this.ruleToLevel(this.configuration.default_policy); // otherwise use the default policy - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authorization/AuthorizerStub.spec.ts b/themes/matrix/server/src/lib/authorization/AuthorizerStub.spec.ts deleted file mode 100644 index 9bd6f4a85..000000000 --- a/themes/matrix/server/src/lib/authorization/AuthorizerStub.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import Sinon = require("sinon"); -import { IAuthorizer } from "./IAuthorizer"; -import { Level } from "./Level"; -import { Object } from "./Object"; -import { Subject } from "./Subject"; - -export class AuthorizerStub implements IAuthorizer { - authorizationMock: Sinon.SinonStub; - - constructor() { - this.authorizationMock = Sinon.stub(); - } - - authorization(object: Object, subject: Subject): Level { - return this.authorizationMock(object, subject); - } -} diff --git a/themes/matrix/server/src/lib/authorization/IAuthorizer.ts b/themes/matrix/server/src/lib/authorization/IAuthorizer.ts deleted file mode 100644 index fe7ba367a..000000000 --- a/themes/matrix/server/src/lib/authorization/IAuthorizer.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Level } from "./Level"; -import { Subject } from "./Subject"; -import { Object } from "./Object"; - -export interface IAuthorizer { - authorization(object: Object, subject: Subject): Level; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authorization/Level.ts b/themes/matrix/server/src/lib/authorization/Level.ts deleted file mode 100644 index d12802610..000000000 --- a/themes/matrix/server/src/lib/authorization/Level.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum Level { - BYPASS = 0, - ONE_FACTOR = 1, - TWO_FACTOR = 2, - DENY = 3 -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authorization/MultipleDomainMatcher.ts b/themes/matrix/server/src/lib/authorization/MultipleDomainMatcher.ts deleted file mode 100644 index 64c647a4a..000000000 --- a/themes/matrix/server/src/lib/authorization/MultipleDomainMatcher.ts +++ /dev/null @@ -1,12 +0,0 @@ - -export class MultipleDomainMatcher { - static match(domain: string, pattern: string): boolean { - if (pattern.startsWith("*") && - domain.endsWith(pattern.substr(1))) { - return true; - } - else if (domain == pattern) { - return true; - } - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authorization/Object.ts b/themes/matrix/server/src/lib/authorization/Object.ts deleted file mode 100644 index 5411b0d24..000000000 --- a/themes/matrix/server/src/lib/authorization/Object.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface Object { - domain: string; - resource: string; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/authorization/Subject.ts b/themes/matrix/server/src/lib/authorization/Subject.ts deleted file mode 100644 index 310d6b4c0..000000000 --- a/themes/matrix/server/src/lib/authorization/Subject.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface Subject { - user: string; - groups: string[]; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/ConfigurationParser.spec.ts b/themes/matrix/server/src/lib/configuration/ConfigurationParser.spec.ts deleted file mode 100644 index 60c0f6182..000000000 --- a/themes/matrix/server/src/lib/configuration/ConfigurationParser.spec.ts +++ /dev/null @@ -1,171 +0,0 @@ -import * as Assert from "assert"; -import { Configuration } from "./schema/Configuration"; -import { ACLConfiguration } from "./schema/AclConfiguration"; -import { ConfigurationParser } from "./ConfigurationParser"; - -describe("configuration/ConfigurationParser", function () { - function buildYamlConfig(): Configuration { - const yaml_config: Configuration = { - port: 8080, - authentication_backend: { - ldap: { - url: "http://ldap", - base_dn: "dc=example,dc=com", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - user: "user", - password: "pass" - }, - }, - session: { - domain: "example.com", - secret: "secret", - expiration: 40000 - }, - storage: { - local: { - path: "/mydirectory" - } - }, - regulation: { - max_retries: 3, - find_time: 5 * 60, - ban_time: 5 * 60 - }, - logs_level: "debug", - notifier: { - email: { - username: "user", - password: "password", - sender: "admin@example.com", - service: "gmail" - } - } - }; - return yaml_config; - } - - describe("port", function () { - it("should read the port from the yaml file", function () { - const yaml_config = buildYamlConfig(); - yaml_config.port = 7070; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.port, 7070); - }); - - it("should default the port to 8080 if not provided", function () { - const yaml_config = buildYamlConfig(); - delete yaml_config.port; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.port, 8080); - }); - }); - - describe("test session configuration", function() { - it("should get the session attributes", function () { - const yaml_config = buildYamlConfig(); - yaml_config.session = { - domain: "example.com", - secret: "secret", - expiration: 3600, - inactivity: 4000 - }; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.session.domain, "example.com"); - Assert.equal(config.session.secret, "secret"); - Assert.equal(config.session.expiration, 3600); - Assert.equal(config.session.inactivity, 4000); - }); - - it("should be ok not specifying inactivity", function () { - const yaml_config = buildYamlConfig(); - yaml_config.session = { - domain: "example.com", - secret: "secret", - expiration: 3600 - }; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.session.domain, "example.com"); - Assert.equal(config.session.secret, "secret"); - Assert.equal(config.session.expiration, 3600); - Assert.equal(config.session.inactivity, undefined); - }); - }); - - it("should get the log level", function () { - const yaml_config = buildYamlConfig(); - yaml_config.logs_level = "debug"; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.logs_level, "debug"); - }); - - it("should get the notifier config", function () { - const userConfig = buildYamlConfig(); - userConfig.notifier = { - email: { - username: "user", - password: "pass", - sender: "admin@example.com", - service: "gmail" - } - }; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.notifier, { - email: { - username: "user", - password: "pass", - sender: "admin@example.com", - service: "gmail" - } - }); - }); - - describe("access_control", function() { - it("should adapt access_control when it is already ok", function () { - const userConfig = buildYamlConfig(); - userConfig.access_control = { - default_policy: "deny", - rules: [{ - domain: "www.example.com", - policy: "two_factor", - subject: "user:user" - }, { - domain: "public.example.com", - policy: "two_factor" - }] - }; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.access_control, { - default_policy: "deny", - rules: [{ - domain: "www.example.com", - policy: "two_factor", - subject: "user:user" - }, { - domain: "public.example.com", - policy: "two_factor" - }] - } as ACLConfiguration); - }); - - - it("should adapt access_control when it is empty", function () { - const userConfig = buildYamlConfig(); - userConfig.access_control = {} as any; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.access_control, { - default_policy: "bypass", - rules: [] - }); - }); - }); - - describe("default_redirection_url", function() { - it("should parse default_redirection_url", function() { - const userConfig = buildYamlConfig(); - userConfig.default_redirection_url = "dummy_url"; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.default_redirection_url, "dummy_url"); - }); - }); -}); diff --git a/themes/matrix/server/src/lib/configuration/ConfigurationParser.ts b/themes/matrix/server/src/lib/configuration/ConfigurationParser.ts deleted file mode 100644 index d92d163c4..000000000 --- a/themes/matrix/server/src/lib/configuration/ConfigurationParser.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import * as ObjectPath from "object-path"; -import { Configuration, complete } from "./schema/Configuration"; -import Ajv = require("ajv"); -import Path = require("path"); -import Util = require("util"); - -export class ConfigurationParser { - private static parseTypes(configuration: Configuration): string[] { - const schema = require(Path.resolve(__dirname, "./Configuration.schema.json")); - const ajv = new Ajv({ - allErrors: true, - missingRefs: "fail" - }); - ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-06.json")); - const valid = ajv.validate(schema, configuration); - if (!valid) - return ajv.errors.map( - (e: Ajv.ErrorObject) => { return ajv.errorsText([e]); }); - return []; - } - - static parse(configuration: Configuration): Configuration { - const validationErrors = this.parseTypes(configuration); - if (validationErrors.length > 0) { - validationErrors.forEach((e: string) => { console.log(e); }); - throw new Error("Malformed configuration (schema). Please double-check your configuration file."); - } - - const [newConfiguration, completionErrors] = complete(configuration); - - if (completionErrors.length > 0) { - completionErrors.forEach((e: string) => { console.log(e); }); - throw new Error("Malformed configuration (validator). Please double-check your configuration file."); - } - return newConfiguration; - } -} - diff --git a/themes/matrix/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts b/themes/matrix/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts deleted file mode 100644 index d4a3093ee..000000000 --- a/themes/matrix/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { SessionConfigurationBuilder } from "./SessionConfigurationBuilder"; -import { Configuration } from "./schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; - -import ExpressSession = require("express-session"); -import ConnectRedis = require("connect-redis"); -import Sinon = require("sinon"); -import Assert = require("assert"); - -describe("configuration/SessionConfigurationBuilder", function () { - const configuration: Configuration = { - access_control: { - default_policy: "deny", - rules: [] - }, - totp: { - issuer: "authelia.com" - }, - authentication_backend: { - ldap: { - url: "ldap://ldap", - user: "user", - base_dn: "dc=example,dc=com", - password: "password", - additional_groups_dn: "ou=groups", - additional_users_dn: "ou=users", - group_name_attribute: "", - groups_filter: "", - mail_attribute: "", - users_filter: "" - }, - }, - logs_level: "debug", - notifier: { - filesystem: { - filename: "/test" - } - }, - port: 8080, - session: { - name: "authelia_session", - domain: "example.com", - expiration: 3600, - secret: "secret" - }, - regulation: { - max_retries: 3, - ban_time: 5 * 60, - find_time: 5 * 60 - }, - storage: { - local: { - in_memory: true - } - } - }; - - const deps: GlobalDependencies = { - ConnectRedis: Sinon.spy() as any, - ldapjs: Sinon.spy() as any, - nedb: Sinon.spy() as any, - session: Sinon.spy() as any, - speakeasy: Sinon.spy() as any, - u2f: Sinon.spy() as any, - winston: Sinon.spy() as any, - Redis: Sinon.spy() as any - }; - - it("should return session options without redis options", function () { - const options = SessionConfigurationBuilder.build(configuration, deps); - const expectedOptions = { - name: "authelia_session", - secret: "secret", - resave: false, - saveUninitialized: true, - cookie: { - secure: true, - httpOnly: true, - maxAge: 3600, - domain: "example.com" - } - }; - - Assert.deepEqual(expectedOptions, options); - }); - - it("should return session options with redis options", function () { - configuration.session["redis"] = { - host: "redis.example.com", - port: 6379 - }; - const RedisStoreMock = Sinon.spy(); - const redisClient = Sinon.mock().returns({ on: Sinon.spy() }); - - deps.ConnectRedis = Sinon.stub().returns(RedisStoreMock) as any; - deps.Redis = { - createClient: Sinon.mock().returns(redisClient) - } as any; - - const options = SessionConfigurationBuilder.build(configuration, deps); - - const expectedOptions: ExpressSession.SessionOptions = { - secret: "secret", - resave: false, - saveUninitialized: true, - name: "authelia_session", - cookie: { - secure: true, - httpOnly: true, - maxAge: 3600, - domain: "example.com" - }, - store: Sinon.match.object as any - }; - - Assert((deps.ConnectRedis as Sinon.SinonStub).calledWith(deps.session)); - Assert.equal(options.secret, expectedOptions.secret); - Assert.equal(options.resave, expectedOptions.resave); - Assert.equal(options.saveUninitialized, expectedOptions.saveUninitialized); - Assert.deepEqual(options.cookie, expectedOptions.cookie); - Assert(options.store != undefined); - }); - - it("should return session options with redis password", function () { - configuration.session["redis"] = { - host: "redis.example.com", - port: 6379, - password: "authelia_pass" - }; - const RedisStoreMock = Sinon.spy(); - const redisClient = Sinon.mock().returns({ on: Sinon.spy() }); - const createClientStub = Sinon.stub(); - - deps.ConnectRedis = Sinon.stub().returns(RedisStoreMock) as any; - deps.Redis = { - createClient: createClientStub - } as any; - - createClientStub.returns(redisClient); - - const options = SessionConfigurationBuilder.build(configuration, deps); - - Assert(createClientStub.calledWith({ - host: "redis.example.com", - port: 6379, - password: "authelia_pass" - })); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/SessionConfigurationBuilder.ts b/themes/matrix/server/src/lib/configuration/SessionConfigurationBuilder.ts deleted file mode 100644 index 6ce643d9d..000000000 --- a/themes/matrix/server/src/lib/configuration/SessionConfigurationBuilder.ts +++ /dev/null @@ -1,52 +0,0 @@ -import ExpressSession = require("express-session"); -import Redis = require("redis"); - -import { Configuration } from "./schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; -import { RedisStoreOptions } from "connect-redis"; - -export class SessionConfigurationBuilder { - - static build(configuration: Configuration, deps: GlobalDependencies): ExpressSession.SessionOptions { - const sessionOptions: ExpressSession.SessionOptions = { - name: configuration.session.name, - secret: configuration.session.secret, - resave: false, - saveUninitialized: true, - cookie: { - secure: true, - httpOnly: true, - maxAge: configuration.session.expiration, - domain: configuration.session.domain - }, - }; - - if (configuration.session.redis) { - let redisOptions; - const options: Redis.ClientOpts = { - host: configuration.session.redis.host, - port: configuration.session.redis.port - }; - - if (configuration.session.redis.password) { - options["password"] = configuration.session.redis.password; - } - const client = deps.Redis.createClient(options); - - client.on("error", function (err: Error) { - console.error("Redis error:", err); - }); - - redisOptions = { - client: client, - logErrors: true - }; - - if (redisOptions) { - const RedisStore = deps.ConnectRedis(deps.session); - sessionOptions.store = new RedisStore(redisOptions); - } - } - return sessionOptions; - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/AclConfiguration.spec.ts b/themes/matrix/server/src/lib/configuration/schema/AclConfiguration.spec.ts deleted file mode 100644 index d1e2a03a2..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/AclConfiguration.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ACLConfiguration, complete } from "./AclConfiguration"; -import Assert = require("assert"); - -describe("configuration/schema/AclConfiguration", function() { - it("should complete ACLConfiguration", function() { - const configuration: ACLConfiguration = {}; - const [newConfiguration, errors] = complete(configuration); - - Assert.deepEqual(newConfiguration.default_policy, "bypass"); - Assert.deepEqual(newConfiguration.rules, []); - }); - - it("should return errors when subject is not good", function() { - const configuration: ACLConfiguration = { - default_policy: "deny", - rules: [{ - domain: "dev.example.com", - subject: "user:abc", - policy: "bypass" - }, { - domain: "dev.example.com", - subject: "user:def", - policy: "bypass" - }, { - domain: "dev.example.com", - subject: "badkey:abc", - policy: "bypass" - }] - }; - const [newConfiguration, errors] = complete(configuration); - - Assert.deepEqual(errors, ["Rule 2 has wrong subject. It should be starting with user: or group:."]); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/AclConfiguration.ts b/themes/matrix/server/src/lib/configuration/schema/AclConfiguration.ts deleted file mode 100644 index 40401dd64..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/AclConfiguration.ts +++ /dev/null @@ -1,41 +0,0 @@ - -export type ACLPolicy = "deny" | "bypass" | "one_factor" | "two_factor"; - -export type ACLRule = { - domain: string; - resources?: string[]; - subject?: string; - policy: ACLPolicy; -}; - -export interface ACLConfiguration { - default_policy?: ACLPolicy; - rules?: ACLRule[]; -} - -export function complete(configuration: ACLConfiguration): [ACLConfiguration, string[]] { - const newConfiguration: ACLConfiguration = (configuration) - ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.default_policy) { - newConfiguration.default_policy = "bypass"; - } - - if (!newConfiguration.rules) { - newConfiguration.rules = []; - } - - if (newConfiguration.rules.length > 0) { - const errors: string[] = []; - newConfiguration.rules.forEach((r, idx) => { - if (r.subject && !r.subject.match(/^(user|group):[a-zA-Z0-9]+$/)) { - errors.push(`Rule ${idx} has wrong subject. It should be starting with user: or group:.`); - } - }); - if (errors.length > 0) { - return [newConfiguration, errors]; - } - } - - return [newConfiguration, []]; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts b/themes/matrix/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts deleted file mode 100644 index 3ca86381c..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { AuthenticationBackendConfiguration, complete } from "./AuthenticationBackendConfiguration"; -import Assert = require("assert"); - -describe("configuration/schema/AuthenticationBackendConfiguration", function() { - it("should ensure there is at least one key", function() { - const configuration: AuthenticationBackendConfiguration = {} as any; - const [newConfiguration, error] = complete(configuration); - - Assert.equal(error, "Authentication backend must have one of the following keys:`ldap` or `file`"); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts b/themes/matrix/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts deleted file mode 100644 index 7f77f894f..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { LdapConfiguration } from "./LdapConfiguration"; -import { FileUsersDatabaseConfiguration } from "./FileUsersDatabaseConfiguration"; - -export interface AuthenticationBackendConfiguration { - ldap?: LdapConfiguration; - file?: FileUsersDatabaseConfiguration; -} - -export function complete( - configuration: AuthenticationBackendConfiguration) - : [AuthenticationBackendConfiguration, string] { - - const newConfiguration: AuthenticationBackendConfiguration = (configuration) - ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (Object.keys(newConfiguration).length != 1) { - return [ - newConfiguration, - "Authentication backend must have one of the following keys:" + - "`ldap` or `file`" - ]; - } - - return [newConfiguration, undefined]; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/Configuration.ts b/themes/matrix/server/src/lib/configuration/schema/Configuration.ts deleted file mode 100644 index 8d16a5fb2..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/Configuration.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { ACLConfiguration, complete as AclConfigurationComplete } from "./AclConfiguration"; -import { AuthenticationBackendConfiguration, complete as AuthenticationBackendComplete } from "./AuthenticationBackendConfiguration"; -import { NotifierConfiguration, complete as NotifierConfigurationComplete } from "./NotifierConfiguration"; -import { RegulationConfiguration, complete as RegulationConfigurationComplete } from "./RegulationConfiguration"; -import { SessionConfiguration, complete as SessionConfigurationComplete } from "./SessionConfiguration"; -import { StorageConfiguration, complete as StorageConfigurationComplete } from "./StorageConfiguration"; -import { TotpConfiguration, complete as TotpConfigurationComplete } from "./TotpConfiguration"; - -export interface Configuration { - access_control?: ACLConfiguration; - authentication_backend: AuthenticationBackendConfiguration; - default_redirection_url?: string; - logs_level?: string; - notifier?: NotifierConfiguration; - port?: number; - regulation?: RegulationConfiguration; - session?: SessionConfiguration; - storage?: StorageConfiguration; - totp?: TotpConfiguration; -} - -export function complete( - configuration: Configuration): - [Configuration, string[]] { - - const newConfiguration: Configuration = JSON.parse( - JSON.stringify(configuration)); - const errors: string[] = []; - - const [acls, aclsErrors] = AclConfigurationComplete( - newConfiguration.access_control); - - newConfiguration.access_control = acls; - if (aclsErrors.length > 0) { - errors.concat(aclsErrors); - } - - const [backend, error] = - AuthenticationBackendComplete( - newConfiguration.authentication_backend); - - if (error) errors.push(error); - newConfiguration.authentication_backend = backend; - - if (!newConfiguration.logs_level) { - newConfiguration.logs_level = "info"; - } - - const [notifier, notifierError] = NotifierConfigurationComplete( - newConfiguration.notifier); - newConfiguration.notifier = notifier; - if (notifierError) errors.push(notifierError); - - if (!newConfiguration.port) { - newConfiguration.port = 8080; - } - - newConfiguration.regulation = RegulationConfigurationComplete( - newConfiguration.regulation); - newConfiguration.session = SessionConfigurationComplete( - newConfiguration.session); - newConfiguration.storage = StorageConfigurationComplete( - newConfiguration.storage); - newConfiguration.totp = TotpConfigurationComplete( - newConfiguration.totp); - - return [newConfiguration, errors]; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts b/themes/matrix/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts deleted file mode 100644 index d19002ba9..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export interface FileUsersDatabaseConfiguration { - path: string; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/LdapConfiguration.spec.ts b/themes/matrix/server/src/lib/configuration/schema/LdapConfiguration.spec.ts deleted file mode 100644 index cc73d1085..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/LdapConfiguration.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import Assert = require("assert"); -import { LdapConfiguration, complete } from "./LdapConfiguration"; - -describe("configuration/schema/AuthenticationMethodsConfiguration", function() { - it("should ensure at least one key is provided", function() { - const configuration: LdapConfiguration = { - url: "ldap.example.com", - base_dn: "dc=example,dc=com", - user: "admin", - password: "password" - }; - const newConfiguration = complete(configuration); - - Assert.deepEqual(newConfiguration, { - url: "ldap.example.com", - base_dn: "dc=example,dc=com", - user: "admin", - password: "password", - users_filter: "cn={0}", - group_name_attribute: "cn", - groups_filter: "member={dn}", - mail_attribute: "mail" - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/LdapConfiguration.ts b/themes/matrix/server/src/lib/configuration/schema/LdapConfiguration.ts deleted file mode 100644 index 5dacb9390..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/LdapConfiguration.ts +++ /dev/null @@ -1,40 +0,0 @@ -import Util = require("util"); - -export interface LdapConfiguration { - url: string; - base_dn: string; - - additional_users_dn?: string; - users_filter?: string; - - additional_groups_dn?: string; - groups_filter?: string; - - group_name_attribute?: string; - mail_attribute?: string; - - user: string; // admin username - password: string; // admin password -} - -export function complete(configuration: LdapConfiguration): LdapConfiguration { - const newConfiguration: LdapConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.users_filter) { - newConfiguration.users_filter = "cn={0}"; - } - - if (!newConfiguration.groups_filter) { - newConfiguration.groups_filter = "member={dn}"; - } - - if (!newConfiguration.group_name_attribute) { - newConfiguration.group_name_attribute = "cn"; - } - - if (!newConfiguration.mail_attribute) { - newConfiguration.mail_attribute = "mail"; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts b/themes/matrix/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts deleted file mode 100644 index 6c576e8e0..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import Assert = require("assert"); -import { NotifierConfiguration, complete } from "./NotifierConfiguration"; - -describe("configuration/schema/NotifierConfiguration", function() { - it("should use a default notifier when none is provided", function() { - const configuration: NotifierConfiguration = {}; - const [newConfiguration, error] = complete(configuration); - - Assert.deepEqual(newConfiguration.filesystem, {filename: "/tmp/authelia/notification.txt"}); - }); - - it("should ensure correct key is provided", function() { - const configuration = { - abc: "badvalue" - }; - const [newConfiguration, error] = complete(configuration as any); - - Assert.equal(error, "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"); - }); - - it("should ensure there is no more than one key", function() { - const configuration: NotifierConfiguration = { - smtp: { - host: "smtp.example.com", - port: 25, - secure: false, - sender: "test@example.com" - }, - email: { - username: "test", - password: "test", - sender: "test@example.com", - service: "gmail" - } - }; - const [newConfiguration, error] = complete(configuration); - - Assert.equal(error, "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/NotifierConfiguration.ts b/themes/matrix/server/src/lib/configuration/schema/NotifierConfiguration.ts deleted file mode 100644 index 7bcce15c2..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/NotifierConfiguration.ts +++ /dev/null @@ -1,45 +0,0 @@ - -export interface EmailNotifierConfiguration { - username: string; - password: string; - sender: string; - service: string; -} - -export interface SmtpNotifierConfiguration { - username?: string; - password?: string; - host: string; - port: number; - secure: boolean; - sender: string; -} - -export interface FileSystemNotifierConfiguration { - filename: string; -} - -export interface NotifierConfiguration { - email?: EmailNotifierConfiguration; - smtp?: SmtpNotifierConfiguration; - filesystem?: FileSystemNotifierConfiguration; -} - -export function complete(configuration: NotifierConfiguration): [NotifierConfiguration, string] { - const newConfiguration: NotifierConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (Object.keys(newConfiguration).length == 0) - newConfiguration.filesystem = { filename: "/tmp/authelia/notification.txt" }; - - const ERROR = "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"; - - if (Object.keys(newConfiguration).length != 1) - return [newConfiguration, ERROR]; - - const key = Object.keys(newConfiguration)[0]; - - if (key != "filesystem" && key != "smtp" && key != "email") - return [newConfiguration, ERROR]; - - return [newConfiguration, undefined]; -} diff --git a/themes/matrix/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts b/themes/matrix/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts deleted file mode 100644 index dce2caf4e..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Assert = require("assert"); -import { RegulationConfiguration, complete } from "./RegulationConfiguration"; - -describe("configuration/schema/RegulationConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: RegulationConfiguration = {}; - const newConfiguration = complete(configuration); - - Assert.equal(newConfiguration.ban_time, 300); - Assert.equal(newConfiguration.find_time, 120); - Assert.equal(newConfiguration.max_retries, 3); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/RegulationConfiguration.ts b/themes/matrix/server/src/lib/configuration/schema/RegulationConfiguration.ts deleted file mode 100644 index 117463f43..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/RegulationConfiguration.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface RegulationConfiguration { - max_retries?: number; - find_time?: number; - ban_time?: number; -} - -export function complete(configuration: RegulationConfiguration): RegulationConfiguration { - const newConfiguration: RegulationConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.max_retries) { - newConfiguration.max_retries = 3; - } - - if (!newConfiguration.find_time) { - newConfiguration.find_time = 120; // seconds - } - - if (!newConfiguration.ban_time) { - newConfiguration.ban_time = 300; // seconds - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/SessionConfiguration.spec.ts b/themes/matrix/server/src/lib/configuration/schema/SessionConfiguration.spec.ts deleted file mode 100644 index e54010837..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/SessionConfiguration.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Assert = require("assert"); -import { SessionConfiguration, complete } from "./SessionConfiguration"; - -describe("configuration/schema/SessionConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: SessionConfiguration = { - domain: "example.com", - secret: "unsecure_secret" - }; - const newConfiguration = complete(configuration); - - Assert.equal(newConfiguration.name, 'authelia_session'); - Assert.equal(newConfiguration.expiration, 3600000); - Assert.equal(newConfiguration.inactivity, undefined); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/SessionConfiguration.ts b/themes/matrix/server/src/lib/configuration/schema/SessionConfiguration.ts deleted file mode 100644 index 2c88bb215..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/SessionConfiguration.ts +++ /dev/null @@ -1,32 +0,0 @@ -export interface SessionRedisOptions { - host: string; - port: number; - password?: string; -} - -export interface SessionConfiguration { - name?: string; - domain: string; - secret: string; - expiration?: number; - inactivity?: number; - redis?: SessionRedisOptions; -} - -export function complete(configuration: SessionConfiguration): SessionConfiguration { - const newConfiguration: SessionConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.name) { - newConfiguration.name = "authelia_session"; - } - - if (!newConfiguration.expiration) { - newConfiguration.expiration = 3600000; // 1 hour - } - - if (!newConfiguration.inactivity) { - newConfiguration.inactivity = undefined; // disabled - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/StorageConfiguration.spec.ts b/themes/matrix/server/src/lib/configuration/schema/StorageConfiguration.spec.ts deleted file mode 100644 index 9d02a11b6..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/StorageConfiguration.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import Assert = require("assert"); -import { StorageConfiguration, complete } from "./StorageConfiguration"; - -describe("configuration/schema/StorageConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: StorageConfiguration = {}; - const newConfiguration = complete(configuration); - - Assert.deepEqual(newConfiguration, { - local: { - in_memory: true - } - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/StorageConfiguration.ts b/themes/matrix/server/src/lib/configuration/schema/StorageConfiguration.ts deleted file mode 100644 index 47e356ef4..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/StorageConfiguration.ts +++ /dev/null @@ -1,30 +0,0 @@ -export interface MongoStorageConfiguration { - url: string; - database: string; - auth?: { - username: string; - password: string; - }; -} - -export interface LocalStorageConfiguration { - path?: string; - in_memory?: boolean; -} - -export interface StorageConfiguration { - local?: LocalStorageConfiguration; - mongo?: MongoStorageConfiguration; -} - -export function complete(configuration: StorageConfiguration): StorageConfiguration { - const newConfiguration: StorageConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.local && !newConfiguration.mongo) { - newConfiguration.local = { - in_memory: true - }; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/TotpConfiguration.ts b/themes/matrix/server/src/lib/configuration/schema/TotpConfiguration.ts deleted file mode 100644 index 683135639..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/TotpConfiguration.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface TotpConfiguration { - issuer: string; -} - -export function complete(configuration: TotpConfiguration): TotpConfiguration { - const newConfiguration: TotpConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.issuer) { - newConfiguration.issuer = "authelia.com"; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts b/themes/matrix/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts deleted file mode 100644 index 8008b4833..000000000 --- a/themes/matrix/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts +++ /dev/null @@ -1,9 +0,0 @@ - -export interface UserInfo { - username: string; - password_hash: string; - email: string; - groups?: string[]; -} - -export type UserDatabaseConfiguration = UserInfo[]; \ No newline at end of file diff --git a/themes/matrix/server/src/lib/connectors/mongo/IMongoClient.d.ts b/themes/matrix/server/src/lib/connectors/mongo/IMongoClient.d.ts deleted file mode 100644 index 36cb4b8bf..000000000 --- a/themes/matrix/server/src/lib/connectors/mongo/IMongoClient.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import MongoDB = require("mongodb"); -import Bluebird = require("bluebird"); - -export interface IMongoClient { - collection(name: string): Bluebird -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/connectors/mongo/MongoClient.spec.ts b/themes/matrix/server/src/lib/connectors/mongo/MongoClient.spec.ts deleted file mode 100644 index ca0c68593..000000000 --- a/themes/matrix/server/src/lib/connectors/mongo/MongoClient.spec.ts +++ /dev/null @@ -1,119 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); -import MongoDB = require("mongodb"); -import Sinon = require("sinon"); - -import { MongoClient } from "./MongoClient"; -import { GlobalLoggerStub } from "../../logging/GlobalLoggerStub.spec"; -import { MongoStorageConfiguration } from "../../configuration/schema/StorageConfiguration"; - -describe("connectors/mongo/MongoClient", function () { - let MongoClientStub: any; - let mongoClientStub: any; - let mongoDatabaseStub: any; - let logger: GlobalLoggerStub = new GlobalLoggerStub(); - - const configuration: MongoStorageConfiguration = { - url: "mongo://url", - database: "databasename" - }; - - describe("connection", () => { - before(() => { - mongoClientStub = { - db: Sinon.stub() - }; - mongoDatabaseStub = { - on: Sinon.stub(), - collection: Sinon.stub() - } - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - undefined, mongoClientStub); - mongoClientStub.db.returns( - mongoDatabaseStub); - }); - - after(() => { - MongoClientStub.restore(); - }); - - it("should use credentials from configuration", () => { - configuration.auth = { - username: "authelia", - password: "authelia_pass" - }; - - const client = new MongoClient(configuration, logger); - return client.collection("test") - .then(() => { - Assert(MongoClientStub.calledWith("mongo://url", { - auth: { - user: "authelia", - password: "authelia_pass" - } - })) - }); - }); - }); - - describe("collection", () => { - before(function() { - mongoClientStub = { - db: Sinon.stub() - }; - mongoDatabaseStub = { - on: Sinon.stub(), - collection: Sinon.stub() - } - }); - - describe("Connection to mongo is ok", function() { - before(function () { - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - undefined, mongoClientStub); - mongoClientStub.db.returns( - mongoDatabaseStub); - }); - - after(function () { - MongoClientStub.restore(); - }); - - it("should create a collection", function () { - const COLLECTION_NAME = "mycollection"; - const client = new MongoClient(configuration, logger); - - mongoDatabaseStub.collection.returns("COL"); - return client.collection(COLLECTION_NAME) - .then((collection) => mongoDatabaseStub.collection.calledWith(COLLECTION_NAME)); - }); - }); - - describe("Connection to mongo is broken", function() { - before(function () { - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - new Error("Failed connection"), undefined); - }); - - after(function () { - MongoClientStub.restore(); - }); - - it("should fail creating the collection", function() { - const COLLECTION_NAME = "mycollection"; - const client = new MongoClient(configuration, logger); - - mongoDatabaseStub.collection.returns("COL"); - return client.collection(COLLECTION_NAME) - .then((collection) => Bluebird.reject(new Error("should not be here."))) - .catch((err) => Bluebird.resolve()); - }); - }) - }); -}); diff --git a/themes/matrix/server/src/lib/connectors/mongo/MongoClient.ts b/themes/matrix/server/src/lib/connectors/mongo/MongoClient.ts deleted file mode 100644 index d15731e97..000000000 --- a/themes/matrix/server/src/lib/connectors/mongo/MongoClient.ts +++ /dev/null @@ -1,76 +0,0 @@ - -import MongoDB = require("mongodb"); -import { IMongoClient } from "./IMongoClient"; -import Bluebird = require("bluebird"); -import { AUTHENTICATION_FAILED } from "../../../../../shared/UserMessages"; -import { IGlobalLogger } from "../../logging/IGlobalLogger"; -import { MongoStorageConfiguration } from "../../configuration/schema/StorageConfiguration"; - -export class MongoClient implements IMongoClient { - private configuration: MongoStorageConfiguration; - - private database: MongoDB.Db; - private client: MongoDB.MongoClient; - private logger: IGlobalLogger; - - constructor( - configuration: MongoStorageConfiguration, - logger: IGlobalLogger) { - - this.configuration = configuration; - this.logger = logger; - } - - connect(): Bluebird { - const that = this; - const options: MongoDB.MongoClientOptions = {}; - if (that.configuration.auth) { - options["auth"] = { - user: that.configuration.auth.username, - password: that.configuration.auth.password - }; - } - - return new Bluebird((resolve, reject) => { - MongoDB.MongoClient.connect( - this.configuration.url, - options, - function(err, client) { - if (err) { - reject(err); - return; - } - resolve(client); - }); - }) - .then(function (client: MongoDB.MongoClient) { - that.database = client.db(that.configuration.database); - that.database.on("close", () => { - that.logger.info("[MongoClient] Lost connection."); - }); - that.database.on("reconnect", () => { - that.logger.info("[MongoClient] Reconnected."); - }); - that.client = client; - }); - } - - close(): Bluebird { - if (this.client) { - this.client.close(); - this.database = undefined; - this.client = undefined; - } - return Bluebird.resolve(); - } - - collection(name: string): Bluebird { - if (!this.client) { - const that = this; - return this.connect() - .then(() => Bluebird.resolve(that.database.collection(name))); - } - - return Bluebird.resolve(this.database.collection(name)); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/connectors/mongo/MongoClientStub.spec.ts b/themes/matrix/server/src/lib/connectors/mongo/MongoClientStub.spec.ts deleted file mode 100644 index 1cfd48e32..000000000 --- a/themes/matrix/server/src/lib/connectors/mongo/MongoClientStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import MongoDB = require("mongodb"); -import Bluebird = require("bluebird"); -import { IMongoClient } from "../../../../src/lib/connectors/mongo/IMongoClient"; - -export class MongoClientStub implements IMongoClient { - public collectionStub: Sinon.SinonStub; - - constructor() { - this.collectionStub = Sinon.stub(); - } - - collection(name: string): Bluebird { - return this.collectionStub(name); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/logging/GlobalLogger.ts b/themes/matrix/server/src/lib/logging/GlobalLogger.ts deleted file mode 100644 index 4da7acf49..000000000 --- a/themes/matrix/server/src/lib/logging/GlobalLogger.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { IGlobalLogger } from "./IGlobalLogger"; -import Util = require("util"); -import Express = require("express"); -import Winston = require("winston"); - -declare module "express" { - interface Request { - id: string; - } -} - -export class GlobalLogger implements IGlobalLogger { - private winston: typeof Winston; - constructor(winston: typeof Winston) { - this.winston = winston; - } - - private buildMessage(message: string, ...args: any[]): string { - return Util.format("date='%s' message='%s'", new Date(), - Util.format(message, ...args)); - } - - info(message: string, ...args: any[]): void { - this.winston.info(this.buildMessage(message, ...args)); - } - - debug(message: string, ...args: any[]): void { - this.winston.debug(this.buildMessage(message, ...args)); - } - - error(message: string, ...args: any[]): void { - this.winston.debug(this.buildMessage(message, ...args)); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/logging/GlobalLoggerStub.spec.ts b/themes/matrix/server/src/lib/logging/GlobalLoggerStub.spec.ts deleted file mode 100644 index d4bb13710..000000000 --- a/themes/matrix/server/src/lib/logging/GlobalLoggerStub.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import Sinon = require("sinon"); -import { GlobalLogger } from "./GlobalLogger"; -import Winston = require("winston"); -import Express = require("express"); -import { IGlobalLogger } from "./IGlobalLogger"; - -export class GlobalLoggerStub implements IGlobalLogger { - infoStub: Sinon.SinonStub; - debugStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - private globalLogger: IGlobalLogger; - - constructor(enableLogging?: boolean) { - this.infoStub = Sinon.stub(); - this.debugStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - if (enableLogging) - this.globalLogger = new GlobalLogger(Winston); - } - - info(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.infoStub(message, ...args); - } - - debug(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.debugStub(message, ...args); - } - - error(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.errorStub(message, ...args); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/logging/IGlobalLogger.ts b/themes/matrix/server/src/lib/logging/IGlobalLogger.ts deleted file mode 100644 index 548515ec7..000000000 --- a/themes/matrix/server/src/lib/logging/IGlobalLogger.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface IGlobalLogger { - info(message: string, ...args: any[]): void; - debug(message: string, ...args: any[]): void; - error(message: string, ...args: any[]): void; -} diff --git a/themes/matrix/server/src/lib/logging/IRequestLogger.ts b/themes/matrix/server/src/lib/logging/IRequestLogger.ts deleted file mode 100644 index 126a601fe..000000000 --- a/themes/matrix/server/src/lib/logging/IRequestLogger.ts +++ /dev/null @@ -1,7 +0,0 @@ -import Express = require("express"); - -export interface IRequestLogger { - info(req: Express.Request, message: string, ...args: any[]): void; - debug(req: Express.Request, message: string, ...args: any[]): void; - error(req: Express.Request, message: string, ...args: any[]): void; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/logging/RequestLogger.ts b/themes/matrix/server/src/lib/logging/RequestLogger.ts deleted file mode 100644 index c45c66018..000000000 --- a/themes/matrix/server/src/lib/logging/RequestLogger.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { IRequestLogger } from "./IRequestLogger"; -import Util = require("util"); -import Express = require("express"); -import Winston = require("winston"); - -declare module "express" { - interface Request { - id: string; - } -} - -export class RequestLogger implements IRequestLogger { - private winston: typeof Winston; - - constructor(winston: typeof Winston) { - this.winston = winston; - } - - private formatHeader(req: Express.Request) { - const clientIP = req.ip; // The IP of the original client going through the proxy chain. - return Util.format("date='%s' method='%s', path='%s' requestId='%s' sessionId='%s' ip='%s'", - new Date(), req.method, req.path, req.id, req.sessionID, clientIP); - } - - private formatBody(message: string) { - return Util.format("message='%s'", message); - } - - private formatMessage(req: Express.Request, message: string) { - return Util.format("%s %s", this.formatHeader(req), - this.formatBody(message)); - } - - info(req: Express.Request, message: string, ...args: any[]): void { - this.winston.info(this.formatMessage(req, message), ...args); - } - - debug(req: Express.Request, message: string, ...args: any[]): void { - this.winston.debug(this.formatMessage(req, message), ...args); - } - - error(req: Express.Request, message: string, ...args: any[]): void { - this.winston.error(this.formatMessage(req, message), ...args); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/logging/RequestLoggerStub.spec.ts b/themes/matrix/server/src/lib/logging/RequestLoggerStub.spec.ts deleted file mode 100644 index b0e375210..000000000 --- a/themes/matrix/server/src/lib/logging/RequestLoggerStub.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { IRequestLogger } from "./IRequestLogger"; -import Sinon = require("sinon"); -import { RequestLogger } from "./RequestLogger"; -import Winston = require("winston"); -import Express = require("express"); - -export class RequestLoggerStub implements IRequestLogger { - infoStub: Sinon.SinonStub; - debugStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - private requestLogger: RequestLogger; - - constructor(enableLogging?: boolean) { - this.infoStub = Sinon.stub(); - this.debugStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - if (enableLogging) - this.requestLogger = new RequestLogger(Winston); - } - - info(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.infoStub(req, message, ...args); - } - - debug(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.debugStub(req, message, ...args); - } - - error(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.errorStub(req, message, ...args); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/notifiers/AbstractEmailNotifier.ts b/themes/matrix/server/src/lib/notifiers/AbstractEmailNotifier.ts deleted file mode 100644 index 198e4e5db..000000000 --- a/themes/matrix/server/src/lib/notifiers/AbstractEmailNotifier.ts +++ /dev/null @@ -1,23 +0,0 @@ - -import { INotifier } from "../notifiers/INotifier"; -import { Identity } from "../../../types/Identity"; - -import Fs = require("fs"); -import Path = require("path"); -import Ejs = require("ejs"); -import BluebirdPromise = require("bluebird"); - -const email_template = Fs.readFileSync(Path.join(__dirname, "../../resources/email-template.ejs"), "UTF-8"); - -export abstract class AbstractEmailNotifier implements INotifier { - notify(to: string, subject: string, link: string): BluebirdPromise { - const d = { - url: link, - button_title: "Continue", - title: subject - }; - return this.sendEmail(to, subject, Ejs.render(email_template, d)); - } - - abstract sendEmail(to: string, subject: string, content: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/notifiers/EmailNotifier.spec.ts b/themes/matrix/server/src/lib/notifiers/EmailNotifier.spec.ts deleted file mode 100644 index 8211bbc02..000000000 --- a/themes/matrix/server/src/lib/notifiers/EmailNotifier.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as sinon from "sinon"; -import * as Assert from "assert"; -import BluebirdPromise = require("bluebird"); - -import { MailSenderStub } from "./MailSenderStub.spec"; -import EmailNotifier = require("./EmailNotifier"); - - -describe("notifiers/EmailNotifier", function () { - it("should send an email to given user", function () { - const mailSender = new MailSenderStub(); - const options = { - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }; - - mailSender.sendStub.returns(BluebirdPromise.resolve()); - const sender = new EmailNotifier.EmailNotifier(options, mailSender); - const subject = "subject"; - const url = "http://test.com"; - - return sender.notify("user@example.com", subject, url) - .then(function () { - Assert.equal(mailSender.sendStub.getCall(0).args[0].to, "user@example.com"); - Assert.equal(mailSender.sendStub.getCall(0).args[0].subject, "subject"); - return BluebirdPromise.resolve(); - }); - }); - - it("should fail while sending an email", function () { - const mailSender = new MailSenderStub(); - const options = { - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }; - - mailSender.sendStub.returns(BluebirdPromise.reject(new Error("Failed to send mail"))); - const sender = new EmailNotifier.EmailNotifier(options, mailSender); - const subject = "subject"; - const url = "http://test.com"; - - return sender.notify("user@example.com", subject, url) - .then(function () { - return BluebirdPromise.reject(new Error()); - }, function() { - Assert.equal(mailSender.sendStub.getCall(0).args[0].from, "admin@example.com"); - return BluebirdPromise.resolve(); - }); - }); -}); diff --git a/themes/matrix/server/src/lib/notifiers/EmailNotifier.ts b/themes/matrix/server/src/lib/notifiers/EmailNotifier.ts deleted file mode 100644 index 4df7c861e..000000000 --- a/themes/matrix/server/src/lib/notifiers/EmailNotifier.ts +++ /dev/null @@ -1,27 +0,0 @@ - -import * as BluebirdPromise from "bluebird"; - -import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier"; -import { EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; -import { IMailSender } from "./IMailSender"; - -export class EmailNotifier extends AbstractEmailNotifier { - private mailSender: IMailSender; - private sender: string; - - constructor(options: EmailNotifierConfiguration, mailSender: IMailSender) { - super(); - this.mailSender = mailSender; - this.sender = options.sender; - } - - sendEmail(to: string, subject: string, content: string) { - const mailOptions = { - from: this.sender, - to: to, - subject: subject, - html: content - }; - return this.mailSender.send(mailOptions); - } -} diff --git a/themes/matrix/server/src/lib/notifiers/FileSystemNotifier.ts b/themes/matrix/server/src/lib/notifiers/FileSystemNotifier.ts deleted file mode 100644 index 23f6242c4..000000000 --- a/themes/matrix/server/src/lib/notifiers/FileSystemNotifier.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as BluebirdPromise from "bluebird"; -import * as util from "util"; -import * as Fs from "fs"; -import { INotifier } from "./INotifier"; -import { Identity } from "../../../types/Identity"; - -import { FileSystemNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class FileSystemNotifier implements INotifier { - private filename: string; - - constructor(options: FileSystemNotifierConfiguration) { - this.filename = options.filename; - } - - notify(to: string, subject: string, link: string): BluebirdPromise { - const content = util.format("Date: %s\nEmail: %s\nSubject: %s\nLink: %s", - new Date().toString(), to, subject, link); - const writeFilePromised: any = BluebirdPromise.promisify(Fs.writeFile); - return writeFilePromised(this.filename, content); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/notifiers/IMailSender.ts b/themes/matrix/server/src/lib/notifiers/IMailSender.ts deleted file mode 100644 index 34ac464a8..000000000 --- a/themes/matrix/server/src/lib/notifiers/IMailSender.ts +++ /dev/null @@ -1,6 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); - -export interface IMailSender { - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/notifiers/IMailSenderBuilder.ts b/themes/matrix/server/src/lib/notifiers/IMailSenderBuilder.ts deleted file mode 100644 index 36d4dcdf7..000000000 --- a/themes/matrix/server/src/lib/notifiers/IMailSenderBuilder.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export interface IMailSenderBuilder { - buildEmail(options: EmailNotifierConfiguration): IMailSender; - buildSmtp(options: SmtpNotifierConfiguration): IMailSender; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/notifiers/INotifier.ts b/themes/matrix/server/src/lib/notifiers/INotifier.ts deleted file mode 100644 index b9a6b138c..000000000 --- a/themes/matrix/server/src/lib/notifiers/INotifier.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as BluebirdPromise from "bluebird"; - -export interface INotifier { - notify(to: string, subject: string, link: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/notifiers/MailSender.ts b/themes/matrix/server/src/lib/notifiers/MailSender.ts deleted file mode 100644 index 536a88e63..000000000 --- a/themes/matrix/server/src/lib/notifiers/MailSender.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import Nodemailer = require("nodemailer"); -import NodemailerDirectTransport = require("nodemailer-direct-transport"); -import NodemailerSmtpTransport = require("nodemailer-smtp-transport"); -import BluebirdPromise = require("bluebird"); - -export class MailSender implements IMailSender { - private transporter: Nodemailer.Transporter; - - constructor(options: NodemailerDirectTransport.DirectOptions | - NodemailerSmtpTransport.SmtpOptions, nodemailer: typeof Nodemailer) { - this.transporter = nodemailer.createTransport(options); - } - - verify(): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - that.transporter.verify(function (error: Error, success: any) { - if (error) { - reject(new Error("Unable to connect to SMTP server. \ - Please check the service is running and your credentials are correct.")); - return; - } - resolve(); - }); - }); - } - - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - that.transporter.sendMail(mailOptions, (error: Error, - data: Nodemailer.SentMessageInfo) => { - if (error) { - reject(new Error("Error while sending email: " + error.message)); - return; - } - resolve(); - }); - }); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/notifiers/MailSenderBuilder.spec.ts b/themes/matrix/server/src/lib/notifiers/MailSenderBuilder.spec.ts deleted file mode 100644 index 41e0db426..000000000 --- a/themes/matrix/server/src/lib/notifiers/MailSenderBuilder.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ - -import { MailSenderBuilder } from ".//MailSenderBuilder"; -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); -import Assert = require("assert"); - -describe("notifiers/MailSenderBuilder", function() { - let createTransportStub: Sinon.SinonStub; - beforeEach(function() { - createTransportStub = Sinon.stub(Nodemailer, "createTransport"); - }); - - afterEach(function() { - createTransportStub.restore(); - }); - - it("should create a email mail sender", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildEmail({ - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }); - Assert.equal(createTransportStub.getCall(0).args[0].auth.user, "user_gmail"); - Assert.equal(createTransportStub.getCall(0).args[0].auth.pass, "pass_gmail"); - Assert.equal(createTransportStub.getCall(0).args[0].service, "gmail"); - }); - - describe("build smtp mail sender", function() { - it("should create a smtp mail sender with authenticated user", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildSmtp({ - host: "mail.example.com", - password: "password", - port: 25, - secure: true, - username: "user", - sender: "admin@example.com" - }); - Assert.deepStrictEqual(createTransportStub.getCall(0).args[0], { - host: "mail.example.com", - auth: { - pass: "password", - user: "user" - }, - port: 25, - secure: true, - }); - }); - - it("should create a smtp mail sender with anonymous user", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildSmtp({ - host: "mail.example.com", - port: 25, - secure: true, - sender: "admin@example.com" - }); - Assert.deepStrictEqual(createTransportStub.getCall(0).args[0], { - host: "mail.example.com", - port: 25, - secure: true, - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/notifiers/MailSenderBuilder.ts b/themes/matrix/server/src/lib/notifiers/MailSenderBuilder.ts deleted file mode 100644 index 1d06be52f..000000000 --- a/themes/matrix/server/src/lib/notifiers/MailSenderBuilder.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import { IMailSenderBuilder } from "./IMailSenderBuilder"; -import { MailSender } from "./MailSender"; -import Nodemailer = require("nodemailer"); -import NodemailerSmtpTransport = require("nodemailer-smtp-transport"); -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class MailSenderBuilder implements IMailSenderBuilder { - private nodemailer: typeof Nodemailer; - - constructor(nodemailer: typeof Nodemailer) { - this.nodemailer = nodemailer; - } - - buildEmail(options: EmailNotifierConfiguration): IMailSender { - const emailOptions = { - service: options.service, - auth: { - user: options.username, - pass: options.password - } - }; - return new MailSender(emailOptions, this.nodemailer); - } - - buildSmtp(options: SmtpNotifierConfiguration): IMailSender { - const smtpOptions: NodemailerSmtpTransport.SmtpOptions = { - host: options.host, - port: options.port, - secure: options.secure, // upgrade later with STARTTLS - }; - - if (options.username && options.password) { - smtpOptions.auth = { - user: options.username, - pass: options.password - }; - } - - return new MailSender(smtpOptions, this.nodemailer); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts b/themes/matrix/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts deleted file mode 100644 index 5b76f6e56..000000000 --- a/themes/matrix/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { IMailSenderBuilder } from "../../../src/lib/notifiers/IMailSenderBuilder"; -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); -import { IMailSender } from "../../../src/lib/notifiers/IMailSender"; -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../../../src/lib/configuration/schema/NotifierConfiguration"; - -export class MailSenderBuilderStub implements IMailSenderBuilder { - buildEmailStub: Sinon.SinonStub; - buildSmtpStub: Sinon.SinonStub; - - constructor() { - this.buildEmailStub = Sinon.stub(); - this.buildSmtpStub = Sinon.stub(); - } - - buildEmail(options: EmailNotifierConfiguration): IMailSender { - return this.buildEmailStub(options); - } - - buildSmtp(options: SmtpNotifierConfiguration): IMailSender { - return this.buildSmtpStub(options); - } - -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/notifiers/MailSenderStub.spec.ts b/themes/matrix/server/src/lib/notifiers/MailSenderStub.spec.ts deleted file mode 100644 index d57c458fc..000000000 --- a/themes/matrix/server/src/lib/notifiers/MailSenderStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { IMailSender } from "../../../src/lib/notifiers/IMailSender"; -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); - -export class MailSenderStub implements IMailSender { - sendStub: Sinon.SinonStub; - - constructor() { - this.sendStub = Sinon.stub(); - } - - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise { - return this.sendStub(mailOptions); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/notifiers/NotifierFactory.spec.ts b/themes/matrix/server/src/lib/notifiers/NotifierFactory.spec.ts deleted file mode 100644 index f15e7667f..000000000 --- a/themes/matrix/server/src/lib/notifiers/NotifierFactory.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ - -import * as sinon from "sinon"; -import * as BluebirdPromise from "bluebird"; -import * as assert from "assert"; - -import { NotifierFactory } from "./NotifierFactory"; -import { EmailNotifier } from "./EmailNotifier"; -import { SmtpNotifier } from "./SmtpNotifier"; -import { MailSenderBuilderStub } from "./MailSenderBuilderStub.spec"; - - -describe("notifiers/NotifierFactory", function () { - let mailSenderBuilderStub: MailSenderBuilderStub; - it("should build a Email Notifier", function () { - const options = { - email: { - username: "abc", - password: "password", - sender: "admin@example.com", - service: "gmail" - } - }; - mailSenderBuilderStub = new MailSenderBuilderStub(); - assert(NotifierFactory.build(options, mailSenderBuilderStub) instanceof EmailNotifier); - }); - - it("should build a SMTP Notifier", function () { - const options = { - smtp: { - username: "user", - password: "pass", - secure: true, - host: "localhost", - port: 25, - sender: "admin@example.com" - } - }; - - mailSenderBuilderStub = new MailSenderBuilderStub(); - assert(NotifierFactory.build(options, mailSenderBuilderStub) instanceof SmtpNotifier); - }); -}); diff --git a/themes/matrix/server/src/lib/notifiers/NotifierFactory.ts b/themes/matrix/server/src/lib/notifiers/NotifierFactory.ts deleted file mode 100644 index a89155feb..000000000 --- a/themes/matrix/server/src/lib/notifiers/NotifierFactory.ts +++ /dev/null @@ -1,33 +0,0 @@ - -import { NotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; -import Nodemailer = require("nodemailer"); -import { INotifier } from "./INotifier"; - -import { FileSystemNotifier } from "./FileSystemNotifier"; -import { EmailNotifier } from "./EmailNotifier"; -import { SmtpNotifier } from "./SmtpNotifier"; -import { IMailSender } from "./IMailSender"; -import { IMailSenderBuilder } from "./IMailSenderBuilder"; - -export class NotifierFactory { - static build(options: NotifierConfiguration, mailSenderBuilder: IMailSenderBuilder): INotifier { - if ("email" in options) { - const mailSender = mailSenderBuilder.buildEmail(options.email); - return new EmailNotifier(options.email, mailSender); - } - else if ("smtp" in options) { - const mailSender = mailSenderBuilder.buildSmtp(options.smtp); - return new SmtpNotifier(options.smtp, mailSender); - } - else if ("filesystem" in options) { - return new FileSystemNotifier(options.filesystem); - } - else { - throw new Error("No available notifier option detected."); - } - } -} - - - - diff --git a/themes/matrix/server/src/lib/notifiers/NotifierStub.spec.ts b/themes/matrix/server/src/lib/notifiers/NotifierStub.spec.ts deleted file mode 100644 index f99231b50..000000000 --- a/themes/matrix/server/src/lib/notifiers/NotifierStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); - -import { INotifier } from "./INotifier"; - -export class NotifierStub implements INotifier { - notifyStub: Sinon.SinonStub; - - constructor() { - this.notifyStub = Sinon.stub(); - } - - notify(to: string, subject: string, link: string): BluebirdPromise { - return this.notifyStub(to, subject, link); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/notifiers/SmtpNotifier.ts b/themes/matrix/server/src/lib/notifiers/SmtpNotifier.ts deleted file mode 100644 index f93a6d4a0..000000000 --- a/themes/matrix/server/src/lib/notifiers/SmtpNotifier.ts +++ /dev/null @@ -1,30 +0,0 @@ - - -import * as BluebirdPromise from "bluebird"; - -import { IMailSender } from "./IMailSender"; -import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier"; -import { SmtpNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class SmtpNotifier extends AbstractEmailNotifier { - private mailSender: IMailSender; - private sender: string; - - constructor(options: SmtpNotifierConfiguration, - mailSender: IMailSender) { - super(); - this.mailSender = mailSender; - this.sender = options.sender; - } - - sendEmail(to: string, subject: string, content: string) { - const mailOptions = { - from: this.sender, - to: to, - subject: subject, - html: content - }; - const that = this; - return this.mailSender.send(mailOptions); - } -} diff --git a/themes/matrix/server/src/lib/regulation/IRegulator.ts b/themes/matrix/server/src/lib/regulation/IRegulator.ts deleted file mode 100644 index c49425b24..000000000 --- a/themes/matrix/server/src/lib/regulation/IRegulator.ts +++ /dev/null @@ -1,6 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -export interface IRegulator { - mark(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise; - regulate(userId: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/regulation/Regulator.spec.ts b/themes/matrix/server/src/lib/regulation/Regulator.spec.ts deleted file mode 100644 index f9c6e6086..000000000 --- a/themes/matrix/server/src/lib/regulation/Regulator.spec.ts +++ /dev/null @@ -1,186 +0,0 @@ - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -import { Regulator } from "./Regulator"; -import MockDate = require("mockdate"); -import exceptions = require("../Exceptions"); -import { UserDataStoreStub } from "../storage/UserDataStoreStub.spec"; - -describe("regulation/Regulator", function () { - const USER1 = "USER1"; - const USER2 = "USER2"; - let userDataStoreStub: UserDataStoreStub; - - beforeEach(function () { - userDataStoreStub = new UserDataStoreStub(); - const dataStore: { [userId: string]: { userId: string, date: Date, isAuthenticationSuccessful: boolean }[] } = { - [USER1]: [], - [USER2]: [] - }; - - userDataStoreStub.saveAuthenticationTraceStub.callsFake(function (userId, isAuthenticationSuccessful) { - dataStore[userId].unshift({ - userId: userId, - date: new Date(), - isAuthenticationSuccessful: isAuthenticationSuccessful, - }); - return BluebirdPromise.resolve(); - }); - - userDataStoreStub.retrieveLatestAuthenticationTracesStub.callsFake(function (userId, count) { - const ret = (dataStore[userId].length <= count) ? dataStore[userId] : dataStore[userId].slice(0, 3); - return BluebirdPromise.resolve(ret); - }); - }); - - afterEach(function () { - MockDate.reset(); - }); - - function markAuthenticationAt(regulator: Regulator, user: string, time: string, success: boolean) { - MockDate.set(time); - return regulator.mark(user, success); - } - - it("should mark 2 authentication and regulate (accept)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 10, 10); - - return regulator.mark(USER1, false) - .then(function () { - return regulator.mark(USER1, true); - }) - .then(function () { - return regulator.regulate(USER1); - }); - }); - - it("should mark 3 authentications and regulate (reject)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 10, 10); - - return regulator.mark(USER1, false) - .then(function () { - return regulator.mark(USER1, false); - }) - .then(function () { - return regulator.mark(USER1, false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { return BluebirdPromise.reject(new Error("should not be here!")); }) - .catch(exceptions.AuthenticationRegulationError, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should mark 1 failed, 1 successful and 1 failed authentications within minimum time and regulate (accept)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 60, 30); - - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:10", true); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:20", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:30", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:39", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here!")); - }, - function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should regulate user if number of failures is greater than 3 in allowed time lapse", function () { - function markAuthentications(regulator: Regulator, user: string) { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:45", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:01:05", false); - }) - .then(function () { - return regulator.regulate(user); - }); - } - - const regulator1 = new Regulator(userDataStoreStub, 3, 60, 60); - const regulator2 = new Regulator(userDataStoreStub, 3, 2 * 60, 60); - - const p1 = markAuthentications(regulator1, USER1); - const p2 = markAuthentications(regulator2, USER2); - - return BluebirdPromise.join(p1, p2) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here...")); - }, function () { - Assert(p1.isFulfilled()); - Assert(p2.isRejected()); - }); - }); - - it("should user wait after regulation to authenticate again", function () { - function markAuthentications(regulator: Regulator, user: string) { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:10", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:15", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:25", false); - }) - .then(function () { - MockDate.set("1/2/2000 00:00:54"); - return regulator.regulate(user); - }) - .then(function () { - return BluebirdPromise.reject(new Error("should fail at this time")); - }, function () { - MockDate.set("1/2/2000 00:00:56"); - return regulator.regulate(user); - }); - } - - const regulator = new Regulator(userDataStoreStub, 4, 30, 30); - return markAuthentications(regulator, USER1); - }); - - it("should disable regulation when max_retries is set to 0", function () { - const maxRetries = 0; - const regulator = new Regulator(userDataStoreStub, maxRetries, 60, 30); - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:10", false); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:15", false); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:25", false); - }) - .then(function () { - MockDate.set("1/2/2000 00:00:26"); - return regulator.regulate(USER1); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/regulation/Regulator.ts b/themes/matrix/server/src/lib/regulation/Regulator.ts deleted file mode 100644 index 1037a6a17..000000000 --- a/themes/matrix/server/src/lib/regulation/Regulator.ts +++ /dev/null @@ -1,55 +0,0 @@ - -import * as BluebirdPromise from "bluebird"; -import exceptions = require("../Exceptions"); -import { IUserDataStore } from "../storage/IUserDataStore"; -import { AuthenticationTraceDocument } from "../storage/AuthenticationTraceDocument"; -import { IRegulator } from "./IRegulator"; - -export class Regulator implements IRegulator { - private userDataStore: IUserDataStore; - private banTime: number; - private findTime: number; - private maxRetries: number; - - constructor(userDataStore: any, maxRetries: number, findTime: number, banTime: number) { - this.userDataStore = userDataStore; - this.banTime = banTime; - this.findTime = findTime; - this.maxRetries = maxRetries; - } - - // Mark authentication - mark(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - return this.userDataStore.saveAuthenticationTrace(userId, isAuthenticationSuccessful); - } - - regulate(userId: string): BluebirdPromise { - const that = this; - - if (that.maxRetries <= 0) return BluebirdPromise.resolve(); - - return this.userDataStore.retrieveLatestAuthenticationTraces(userId, that.maxRetries) - .then((docs: AuthenticationTraceDocument[]) => { - // less than the max authorized number of authentication in time range, thus authorizing access - if (docs.length < that.maxRetries) return BluebirdPromise.resolve(); - - const numberOfFailedAuth = docs - .map(function (d: AuthenticationTraceDocument) { return d.isAuthenticationSuccessful == false ? 1 : 0; }) - .reduce(function (acc, v) { return acc + v; }, 0); - - if (numberOfFailedAuth < this.maxRetries) return BluebirdPromise.resolve(); - - const newestDocument = docs[0]; - const oldestDocument = docs[that.maxRetries - 1]; - - const authenticationsTimeRangeInSeconds = (newestDocument.date.getTime() - oldestDocument.date.getTime()) / 1000; - const tooManyAuthInTimelapse = (authenticationsTimeRangeInSeconds < this.findTime); - const stillInBannedTimeRange = (new Date(new Date().getTime() - this.banTime * 1000) < newestDocument.date); - - if (tooManyAuthInTimelapse && stillInBannedTimeRange) - throw new exceptions.AuthenticationRegulationError("Max number of authentication. Please retry in few minutes."); - - return BluebirdPromise.resolve(); - }); - } -} diff --git a/themes/matrix/server/src/lib/regulation/RegulatorStub.spec.ts b/themes/matrix/server/src/lib/regulation/RegulatorStub.spec.ts deleted file mode 100644 index ca8a00fb1..000000000 --- a/themes/matrix/server/src/lib/regulation/RegulatorStub.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); -import { IRegulator } from "./IRegulator"; - - -export class RegulatorStub implements IRegulator { - markStub: Sinon.SinonStub; - regulateStub: Sinon.SinonStub; - - constructor() { - this.markStub = Sinon.stub(); - this.regulateStub = Sinon.stub(); - } - - mark(userId: string, isAuthenticationSuccessful: boolean): Bluebird { - return this.markStub(userId, isAuthenticationSuccessful); - } - - regulate(userId: string): Bluebird { - return this.regulateStub(userId); - } -} diff --git a/themes/matrix/server/src/lib/routes/error/401/get.spec.ts b/themes/matrix/server/src/lib/routes/error/401/get.spec.ts deleted file mode 100644 index 9fdac9c3c..000000000 --- a/themes/matrix/server/src/lib/routes/error/401/get.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get401 from "./get"; -import { ServerVariables } from "../../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../../ServerVariablesMockBuilder.spec"; - -describe("routes/error/401/get", function () { - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let req: any; - let res: any; - let renderSpy: Sinon.SinonSpy; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - - renderSpy = Sinon.spy(); - req = { - headers: {} - }; - res = { - render: renderSpy - }; - }); - - it("should set redirection url to the default redirection url", function () { - vars.config.default_redirection_url = "http://default-redirection"; - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: "http://default-redirection" - })); - }); - }); - - it("should set redirection url to the referer", function () { - req.headers["referer"] = "http://redirection"; - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: "http://redirection" - })); - }); - }); - - it("should render without redirecting the user", function () { - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: undefined - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/error/401/get.ts b/themes/matrix/server/src/lib/routes/error/401/get.ts deleted file mode 100644 index ca4a3963d..000000000 --- a/themes/matrix/server/src/lib/routes/error/401/get.ts +++ /dev/null @@ -1,15 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import redirector from "../redirector"; -import { ServerVariables } from "../../../ServerVariables"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - const redirectionUrl = redirector(req, vars); - res.render("errors/401", { - redirection_url: redirectionUrl - }); - return BluebirdPromise.resolve(); - }; -} diff --git a/themes/matrix/server/src/lib/routes/error/403/get.spec.ts b/themes/matrix/server/src/lib/routes/error/403/get.spec.ts deleted file mode 100644 index 22eb84853..000000000 --- a/themes/matrix/server/src/lib/routes/error/403/get.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get403 from "./get"; -import { ServerVariables } from "../../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../../ServerVariablesMockBuilder.spec"; - -describe("routes/error/403/get", function () { - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let req: any; - let res: any; - let renderSpy: Sinon.SinonSpy; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - - renderSpy = Sinon.spy(); - req = { - headers: {} - }; - res = { - render: renderSpy - }; - }); - - it("should set redirection url to the default redirection url", function () { - vars.config.default_redirection_url = "http://default-redirection"; - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: "http://default-redirection" - })); - }); - }); - - it("should set redirection url to the referer", function () { - req.headers["referer"] = "http://redirection"; - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: "http://redirection" - })); - }); - }); - - it("should render without redirecting the user", function () { - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: undefined - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/error/403/get.ts b/themes/matrix/server/src/lib/routes/error/403/get.ts deleted file mode 100644 index 3ab0319e5..000000000 --- a/themes/matrix/server/src/lib/routes/error/403/get.ts +++ /dev/null @@ -1,15 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import redirector from "../redirector"; -import { ServerVariables } from "../../../ServerVariables"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - const redirectionUrl = redirector(req, vars); - res.render("errors/403", { - redirection_url: redirectionUrl - }); - return BluebirdPromise.resolve(); - }; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/error/404/get.spec.ts b/themes/matrix/server/src/lib/routes/error/404/get.spec.ts deleted file mode 100644 index 73e4e6cef..000000000 --- a/themes/matrix/server/src/lib/routes/error/404/get.spec.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get404 from "./get"; - -describe("routes/error/404/get", function () { - it("should render the page", function () { - const req = {} as Express.Request; - const res = { - render: Sinon.stub() - }; - - return Get404(req, res as any) - .then(function () { - Assert(res.render.calledOnce); - Assert(res.render.calledWith("errors/404")); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/error/404/get.ts b/themes/matrix/server/src/lib/routes/error/404/get.ts deleted file mode 100644 index 6693b6fc8..000000000 --- a/themes/matrix/server/src/lib/routes/error/404/get.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); - -export default function (req: express.Request, res: express.Response): BluebirdPromise { - res.render("errors/404"); - return BluebirdPromise.resolve(); -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/error/redirector.ts b/themes/matrix/server/src/lib/routes/error/redirector.ts deleted file mode 100644 index b1a3ccc11..000000000 --- a/themes/matrix/server/src/lib/routes/error/redirector.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Express = require("express"); -import { ServerVariables } from "../../ServerVariables"; - -export default function (req: Express.Request, vars: ServerVariables): string { - let redirectionUrl: string; - - if (req.headers && req.headers["referer"]) - redirectionUrl = "" + req.headers["referer"]; - else if (vars.config.default_redirection_url) - redirectionUrl = vars.config.default_redirection_url; - - return redirectionUrl; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/firstfactor/get.ts b/themes/matrix/server/src/lib/routes/firstfactor/get.ts deleted file mode 100644 index d94f656c8..000000000 --- a/themes/matrix/server/src/lib/routes/firstfactor/get.ts +++ /dev/null @@ -1,72 +0,0 @@ - -import express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import Util = require("util"); -import { ServerVariables } from "../../ServerVariables"; -import { SafeRedirector } from "../../utils/SafeRedirection"; -import { Level } from "../../authentication/Level"; - -function getRedirectParam( - req: express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -function redirectToSecondFactorPage( - req: express.Request, - res: express.Response) { - - const redirectUrl = getRedirectParam(req); - if (!redirectUrl) - res.redirect(Endpoints.SECOND_FACTOR_GET); - else - res.redirect( - Util.format("%s?%s=%s", - Endpoints.SECOND_FACTOR_GET, - Constants.REDIRECT_QUERY_PARAM, - redirectUrl)); -} - -function redirectToService( - req: express.Request, - res: express.Response, - redirector: SafeRedirector) { - const redirectUrl = getRedirectParam(req); - if (!redirectUrl) { - res.redirect(Endpoints.LOGGED_IN); - } else { - redirector.redirectOrElse(res, redirectUrl, Endpoints.LOGGED_IN); - } -} - -function renderFirstFactor( - res: express.Response) { - - res.render("firstfactor", { - first_factor_post_endpoint: Endpoints.FIRST_FACTOR_POST, - reset_password_request_endpoint: Endpoints.RESET_PASSWORD_REQUEST_GET - }); -} - -export default function ( - vars: ServerVariables) { - - const redirector = new SafeRedirector(vars.config.session.domain); - return function (req: express.Request, res: express.Response): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (authSession.authentication_level == Level.ONE_FACTOR) { - redirectToSecondFactorPage(req, res); - } else if (authSession.authentication_level == Level.TWO_FACTOR) { - redirectToService(req, res, redirector); - } else { - renderFirstFactor(res); - } - resolve(); - }); - }; -} diff --git a/themes/matrix/server/src/lib/routes/firstfactor/post.spec.ts b/themes/matrix/server/src/lib/routes/firstfactor/post.spec.ts deleted file mode 100644 index e1d078cdd..000000000 --- a/themes/matrix/server/src/lib/routes/firstfactor/post.spec.ts +++ /dev/null @@ -1,136 +0,0 @@ - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import FirstFactorPost = require("./post"); -import exceptions = require("../../Exceptions"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import Endpoints = require("../../../../../shared/api"); -import AuthenticationRegulatorMock = require("../../regulation/RegulatorStub.spec"); -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; - -describe("routes/firstfactor/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let emails: string[]; - let groups: string[]; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let authSession: AuthenticationSession; - - beforeEach(function () { - emails = ["test_ok@example.com"]; - groups = ["group1", "group2" ]; - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - mocks.authorizer.authorizationMock.returns(true); - mocks.regulator.regulateStub.returns(BluebirdPromise.resolve()); - mocks.regulator.markStub.returns(BluebirdPromise.resolve()); - - req = { - originalUrl: "/api/firstfactor", - body: { - username: "username", - password: "password" - }, - query: { - redirect: "http://redirect.url" - }, - session: { - cookie: {} - }, - headers: { - host: "home.example.com" - } - }; - - res = ExpressMock.ResponseMock(); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - it("should reply with 204 if success", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal("username", authSession.userid); - Assert(res.send.calledOnce); - }); - }); - - describe("keep me logged in", () => { - beforeEach(() => { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - req.body.keepMeLoggedIn = "true"; - return FirstFactorPost.default(vars)(req as any, res as any); - }); - - it("should set keep_me_logged_in session variable to true", function () { - Assert.equal(authSession.keep_me_logged_in, true); - }); - - it("should set cookie maxAge to one year", function () { - Assert.equal(req.session.cookie.maxAge, 365 * 24 * 60 * 60 * 1000); - }); - }); - - it("should retrieve email from LDAP", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve([{ mail: ["test@example.com"] }])); - return FirstFactorPost.default(vars)(req as any, res as any); - }); - - it("should set first email address as user session variable", function () { - const emails = ["test_ok@example.com"]; - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal("test_ok@example.com", authSession.email); - }); - }); - - it("should return error message when LDAP authenticator throws", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.reject(new exceptions.LdapBindError("Bad credentials"))); - - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.equal(mocks.regulator.markStub.getCall(0).args[0], "username"); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); - - it("should return error message when regulator rejects authentication", function () { - const err = new exceptions.AuthenticationRegulationError("Authentication regulation..."); - mocks.regulator.regulateStub.returns(BluebirdPromise.reject(err)); - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); -}); - - diff --git a/themes/matrix/server/src/lib/routes/firstfactor/post.ts b/themes/matrix/server/src/lib/routes/firstfactor/post.ts deleted file mode 100644 index 565681d6a..000000000 --- a/themes/matrix/server/src/lib/routes/firstfactor/post.ts +++ /dev/null @@ -1,101 +0,0 @@ - -import Exceptions = require("../../Exceptions"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import Endpoint = require("../../../../../shared/api"); -import ErrorReplies = require("../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import UserMessages = require("../../../../../shared/UserMessages"); -import { ServerVariables } from "../../ServerVariables"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import { GroupsAndEmails } from "../../authentication/backends/GroupsAndEmails"; -import { Level as AuthenticationLevel } from "../../authentication/Level"; -import { Level as AuthorizationLevel } from "../../authorization/Level"; -import { URLDecomposer } from "../../utils/URLDecomposer"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response) - : BluebirdPromise { - const username: string = req.body.username; - const password: string = req.body.password; - const keepMeLoggedIn: boolean = req.body.keepMeLoggedIn && - req.body.keepMeLoggedIn === "true"; - let authSession: AuthenticationSession; - - if (keepMeLoggedIn) { - // Stay connected for 1 year. - vars.logger.debug(req, "User requested to stay logged in for one year."); - req.session.cookie.maxAge = 365 * 24 * 60 * 60 * 1000; - } - - return BluebirdPromise.resolve() - .then(function () { - if (!username || !password) { - return BluebirdPromise.reject(new Error("No username or password.")); - } - vars.logger.info(req, "Starting authentication of user \"%s\"", username); - authSession = AuthenticationSessionHandler.get(req, vars.logger); - return vars.regulator.regulate(username); - }) - .then(function () { - vars.logger.info(req, "No regulation applied."); - return vars.usersDatabase.checkUserPassword(username, password); - }) - .then(function (groupsAndEmails: GroupsAndEmails) { - vars.logger.info(req, - "LDAP binding successful. Retrieved information about user are %s", - JSON.stringify(groupsAndEmails)); - authSession.userid = username; - authSession.keep_me_logged_in = keepMeLoggedIn; - authSession.authentication_level = AuthenticationLevel.ONE_FACTOR; - const redirectUrl: string = req.query[Constants.REDIRECT_QUERY_PARAM] !== "undefined" - // Fuck, don't know why it is a string! - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : ""; - - const emails: string[] = groupsAndEmails.emails; - const groups: string[] = groupsAndEmails.groups; - const decomposition = URLDecomposer.fromUrl(redirectUrl); - const authorizationLevel = (decomposition) - ? vars.authorizer.authorization( - {domain: decomposition.domain, resource: decomposition.path}, - {user: username, groups: groups}) - : AuthorizationLevel.TWO_FACTOR; - - if (emails.length > 0) - authSession.email = emails[0]; - authSession.groups = groups; - - vars.logger.debug(req, "Mark successful authentication to regulator."); - vars.regulator.mark(username, true); - - if (authorizationLevel <= AuthorizationLevel.ONE_FACTOR) { - let newRedirectionUrl: string = redirectUrl; - if (!newRedirectionUrl) - newRedirectionUrl = Endpoint.LOGGED_IN; - res.send({ - redirect: newRedirectionUrl - }); - vars.logger.debug(req, "Redirect to '%s'", redirectUrl); - } - else { - let newRedirectUrl = Endpoint.SECOND_FACTOR_GET; - if (redirectUrl) { - newRedirectUrl += "?" + Constants.REDIRECT_QUERY_PARAM + "=" - + redirectUrl; - } - vars.logger.debug(req, "Redirect to '%s'", newRedirectUrl); - res.send({ - redirect: newRedirectUrl - }); - } - return BluebirdPromise.resolve(); - }) - .catch(Exceptions.LdapBindError, function (err: Error) { - vars.regulator.mark(username, false); - return ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.OPERATION_FAILED)(err); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.OPERATION_FAILED)); - }; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/loggedin/get.ts b/themes/matrix/server/src/lib/routes/loggedin/get.ts deleted file mode 100644 index 283a041b1..000000000 --- a/themes/matrix/server/src/lib/routes/loggedin/get.ts +++ /dev/null @@ -1,23 +0,0 @@ -import Express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; -import ErrorReplies = require("../../ErrorReplies"); - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - res.render("already-logged-in", { - logout_endpoint: Endpoints.LOGOUT_GET, - username: authSession.userid, - redirection_url: vars.config.default_redirection_url - }); - resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - } - - return handler; -} diff --git a/themes/matrix/server/src/lib/routes/logout/get.ts b/themes/matrix/server/src/lib/routes/logout/get.ts deleted file mode 100644 index 4d5112146..000000000 --- a/themes/matrix/server/src/lib/routes/logout/get.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import express = require("express"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import { ServerVariables } from "../../ServerVariables"; - -function getRedirectParam(req: express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -export default function (vars: ServerVariables) { - return function(req: express.Request, res: express.Response) { - const redirect_param = getRedirectParam(req); - const redirect_url = redirect_param || "/"; - AuthenticationSessionHandler.reset(req); - res.redirect(redirect_url); - }; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/password-reset/constants.ts b/themes/matrix/server/src/lib/routes/password-reset/constants.ts deleted file mode 100644 index 5c639e92a..000000000 --- a/themes/matrix/server/src/lib/routes/password-reset/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const CHALLENGE = "reset-password"; \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/password-reset/form/post.spec.ts b/themes/matrix/server/src/lib/routes/password-reset/form/post.spec.ts deleted file mode 100644 index ed029c906..000000000 --- a/themes/matrix/server/src/lib/routes/password-reset/form/post.spec.ts +++ /dev/null @@ -1,122 +0,0 @@ - -import PasswordResetFormPost = require("./post"); -import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../types/AuthenticationSession"; -import { UserDataStore } from "../../../storage/UserDataStore"; -import Sinon = require("sinon"); -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../ServerVariables"; -import { Level } from "../../../authentication/Level"; - -describe("routes/password-reset/form/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = { - originalUrl: "/api/password-reset", - body: { - userid: "user" - }, - session: {}, - headers: { - host: "localhost" - } - }; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - - mocks.config.authentication_backend.ldap = { - url: "ldap://ldapjs", - mail_attribute: "mail", - user: "user", - password: "password", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "user", - group_name_attribute: "cn", - groups_filter: "groups" - }; - - res = ExpressMock.ResponseMock(); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - authSession.userid = "user"; - authSession.email = "user@example.com"; - authSession.authentication_level = Level.ONE_FACTOR; - }); - - describe("test reset password post", () => { - it("should update the password and reset auth_session for reauthentication", function () { - req.body = {}; - req.body.password = "new-password"; - - mocks.usersDatabase.updatePasswordStub.returns(BluebirdPromise.resolve()); - - authSession.identity_check = { - userid: "user", - challenge: "reset-password" - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }).then(function (_authSession) { - Assert.equal(res.status.getCall(0).args[0], 204); - Assert.equal(_authSession.authentication_level, Level.NOT_AUTHENTICATED); - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if identity_challenge does not exist", function () { - authSession.identity_check = { - userid: "user", - challenge: undefined - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "An error occurred during password reset. Your password has not been changed." - }); - }); - }); - - it("should fail when ldap fails", function () { - req.body = {}; - req.body.password = "new-password"; - - mocks.usersDatabase.updatePasswordStub - .returns(BluebirdPromise.reject("Internal error with LDAP")); - - authSession.identity_check = { - challenge: "reset-password", - userid: "user" - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "An error occurred during password reset. Your password has not been changed." - }); - return BluebirdPromise.resolve(); - }); - }); - }); -}); diff --git a/themes/matrix/server/src/lib/routes/password-reset/form/post.ts b/themes/matrix/server/src/lib/routes/password-reset/form/post.ts deleted file mode 100644 index fccd7471b..000000000 --- a/themes/matrix/server/src/lib/routes/password-reset/form/post.ts +++ /dev/null @@ -1,50 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); -import exceptions = require("../../../Exceptions"); -import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../types/AuthenticationSession"; -import ErrorReplies = require("../../../ErrorReplies"); -import UserMessages = require("../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../ServerVariables"; - -import Constants = require("./../constants"); - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const newPassword = objectPath.get(req, "body.password"); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.identity_check) { - reject(new Error("No identity check initiated")); - return; - } - - vars.logger.info(req, "User %s wants to reset his/her password.", - authSession.identity_check.userid); - vars.logger.debug(req, "Challenge %s", authSession.identity_check.challenge); - - if (authSession.identity_check.challenge != Constants.CHALLENGE) { - reject(new Error("Bad challenge.")); - return; - } - resolve(); - }) - .then(function () { - return vars.usersDatabase.updatePassword(authSession.identity_check.userid, newPassword); - }) - .then(function () { - vars.logger.info(req, "Password reset for user '%s'", - authSession.identity_check.userid); - AuthenticationSessionHandler.reset(req); - res.status(204); - res.send(); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.RESET_PASSWORD_FAILED)); - }; -} diff --git a/themes/matrix/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts b/themes/matrix/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts deleted file mode 100644 index ac6a41754..000000000 --- a/themes/matrix/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts +++ /dev/null @@ -1,92 +0,0 @@ - -import PasswordResetHandler - from "./PasswordResetHandler"; -import { UserDataStore } from "../../../storage/UserDataStore"; -import Sinon = require("sinon"); -import winston = require("winston"); -import assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../ServerVariables"; - -describe("routes/password-reset/identity/PasswordResetHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = { - originalUrl: "/non-api/xxx", - query: { - userid: "user" - }, - session: { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false - } - }, - headers: { - host: "localhost" - } - }; - - const options = { - inMemoryOnly: true - }; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - mocks.userDataStore.saveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - res = ExpressMock.ResponseMock(); - }); - - describe("test reset password identity pre check", () => { - it("should fail when no userid is provided", function () { - req.query.userid = undefined; - const handler = new PasswordResetHandler(vars.logger, - vars.usersDatabase); - return handler.preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject("It should fail"); - }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if ldap fail", function () { - mocks.usersDatabase.getEmailsStub - .returns(BluebirdPromise.reject("Internal error")); - new PasswordResetHandler(vars.logger, vars.usersDatabase) - .preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should returns identity when ldap replies", function () { - mocks.usersDatabase.getEmailsStub - .returns(BluebirdPromise.resolve(["test@example.com"])); - return new PasswordResetHandler(vars.logger, vars.usersDatabase) - .preValidationInit(req as any); - }); - }); -}); diff --git a/themes/matrix/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts b/themes/matrix/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts deleted file mode 100644 index 42ae92cda..000000000 --- a/themes/matrix/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts +++ /dev/null @@ -1,69 +0,0 @@ -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); - -import exceptions = require("../../../Exceptions"); -import { Identity } from "../../../../../types/Identity"; -import { IdentityValidable } from "../../../IdentityValidable"; -import { PRE_VALIDATION_TEMPLATE } from "../../../IdentityCheckPreValidationTemplate"; -import Constants = require("../constants"); -import { IRequestLogger } from "../../../logging/IRequestLogger"; -import { IUsersDatabase } from "../../../authentication/backends/IUsersDatabase"; - -export const TEMPLATE_NAME = "password-reset-form"; - -export default class PasswordResetHandler implements IdentityValidable { - private logger: IRequestLogger; - private usersDatabase: IUsersDatabase; - - constructor(logger: IRequestLogger, usersDatabase: IUsersDatabase) { - this.logger = logger; - this.usersDatabase = usersDatabase; - } - - challenge(): string { - return Constants.CHALLENGE; - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - const userid: string = - objectPath.get(req, "query.userid"); - return BluebirdPromise.resolve() - .then(function () { - that.logger.debug(req, "User '%s' requested a password reset", userid); - if (!userid) - return BluebirdPromise.reject( - new exceptions.AccessDeniedError("No user id provided")); - - return that.usersDatabase.getEmails(userid); - }) - .then(function (emails: string[]) { - if (!emails && emails.length <= 0) throw new Error("No email found"); - const identity = { - email: emails[0], - userid: userid - }; - return BluebirdPromise.resolve(identity); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject(new exceptions.IdentityError(err.message)); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return BluebirdPromise.resolve(); - } - - postValidationResponse(req: express.Request, res: express.Response) { - res.render(TEMPLATE_NAME); - } - - mailSubject(): string { - return "Reset your password"; - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/password-reset/request/get.ts b/themes/matrix/server/src/lib/routes/password-reset/request/get.ts deleted file mode 100644 index 8f3ae2b4b..000000000 --- a/themes/matrix/server/src/lib/routes/password-reset/request/get.ts +++ /dev/null @@ -1,13 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); -import exceptions = require("../../../Exceptions"); - -import Constants = require("./../constants"); - -const TEMPLATE_NAME = "password-reset-request"; - -export default function (req: express.Request, res: express.Response) { - res.render(TEMPLATE_NAME); -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/secondfactor/get.spec.ts b/themes/matrix/server/src/lib/routes/secondfactor/get.spec.ts deleted file mode 100644 index 6c77e1f69..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/get.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import SecondFactorGet from "./get"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; -import Sinon = require("sinon"); -import ExpressMock = require("../../stubs/express.spec"); -import Assert = require("assert"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); - -describe("routes/secondfactor/get", function () { - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false - } - }; - }); - - describe("test rendering", function () { - it("should render second factor page", function () { - req.session.auth.second_factor = false; - return SecondFactorGet(vars)(req as any, res as any) - .then(function () { - Assert(res.render.calledWith("secondfactor")); - return BluebirdPromise.resolve(); - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/secondfactor/get.ts b/themes/matrix/server/src/lib/routes/secondfactor/get.ts deleted file mode 100644 index 9f6deb4c6..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/get.ts +++ /dev/null @@ -1,28 +0,0 @@ - -import Express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; - -const TEMPLATE_NAME = "secondfactor"; - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response) - : BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - - res.render(TEMPLATE_NAME, { - username: authSession.userid, - totp_identity_start_endpoint: - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - u2f_identity_start_endpoint: - Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET - }); - resolve(); - }); - } - return handler; -} diff --git a/themes/matrix/server/src/lib/routes/secondfactor/redirect.spec.ts b/themes/matrix/server/src/lib/routes/secondfactor/redirect.spec.ts deleted file mode 100644 index ea66e6dca..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/redirect.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import Redirect from "./redirect"; -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariablesMockBuilder, ServerVariablesMock } -from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; -import Assert = require("assert"); - -describe("routes/secondfactor/redirect", function() { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - }); - - it("should redirect to default_redirection_url", function() { - vars.config.default_redirection_url = "http://default_redirection_url"; - Redirect(vars)(req as any, res as any) - .then(function() { - Assert(res.json.calledWith({ - redirect: "http://default_redirection_url" - })); - }); - }); - - it("should redirect to /", function() { - Redirect(vars)(req as any, res as any) - .then(function() { - Assert(res.json.calledWith({ - redirect: "/" - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/secondfactor/redirect.ts b/themes/matrix/server/src/lib/routes/secondfactor/redirect.ts deleted file mode 100644 index 5d84d9ebf..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/redirect.ts +++ /dev/null @@ -1,30 +0,0 @@ - -import express = require("express"); -import objectPath = require("object-path"); -import Endpoints = require("../../../../../shared/api"); -import { ServerVariables } from "../../ServerVariables"; -import BluebirdPromise = require("bluebird"); -import ErrorReplies = require("../../ErrorReplies"); -import UserMessages = require("../../../../../shared/UserMessages"); -import { RedirectionMessage } from "../../../../../shared/RedirectionMessage"; -import Constants = require("../../../../../shared/constants"); - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response) - : BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - let redirectUrl: string = "/"; - if (vars.config.default_redirection_url) { - redirectUrl = vars.config.default_redirection_url; - } - vars.logger.debug(req, "Request redirection to \"%s\".", redirectUrl); - res.json({ - redirect: redirectUrl - } as RedirectionMessage); - return resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - }; -} diff --git a/themes/matrix/server/src/lib/routes/secondfactor/totp/constants.ts b/themes/matrix/server/src/lib/routes/secondfactor/totp/constants.ts deleted file mode 100644 index 7b5a1dcfe..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/totp/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export const CHALLENGE = "totp-register"; -export const TEMPLATE_NAME = "totp-register"; - diff --git a/themes/matrix/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts b/themes/matrix/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts deleted file mode 100644 index 78b8ea3ea..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts +++ /dev/null @@ -1,116 +0,0 @@ -import Sinon = require("sinon"); -import RegistrationHandler from "./RegistrationHandler"; -import { Identity } from "../../../../../../types/Identity"; -import { UserDataStore } from "../../../../storage/UserDataStore"; -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; -import Assert = require("assert"); - -describe("routes/secondfactor/totp/identity/RegistrationHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - req.session = { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false, - identity_check: { - userid: "user", - challenge: "totp-register" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.saveTOTPSecretStub - .returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - }); - - describe("test totp registration pre validation", function () { - it("should fail if first_factor has not been passed", function () { - req.session.auth.first_factor = false; - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("It should fail")); - }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if userid is missing", function (done) { - req.session.auth.first_factor = false; - req.session.auth.userid = undefined; - - new RegistrationHandler(vars.logger, vars.userDataStore, vars.totpHandler, - vars.config.totp) - .preValidationInit(req as any) - .catch(function (err: Error) { - done(); - }); - }); - - it("should fail if email is missing", function (done) { - req.session.auth.first_factor = false; - req.session.auth.email = undefined; - - new RegistrationHandler(vars.logger, vars.userDataStore, vars.totpHandler, - vars.config.totp) - .preValidationInit(req as any) - .catch(function (err: Error) { - done(); - }); - }); - - it("should succeed if first factor passed, userid and email are provided", - function () { - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .preValidationInit(req as any); - }); - }); - - describe("test totp registration post validation", function () { - it("should generate a secret using userId as label and issuer defined in config", function () { - vars.config.totp = { - issuer: "issuer" - }; - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .postValidationResponse(req as any, res as any) - .then(function() { - Assert(mocks.totpHandler.generateStub.calledWithExactly("user", "issuer")); - }); - }); - }); -}); diff --git a/themes/matrix/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts b/themes/matrix/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts deleted file mode 100644 index b39b6d045..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts +++ /dev/null @@ -1,112 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); - -import { Identity } from "../../../../../../types/Identity"; -import { IdentityValidable } from "../../../../IdentityValidable"; -import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate"; -import Constants = require("../constants"); -import Endpoints = require("../../../../../../../shared/api"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import FirstFactorValidator = require("../../../../FirstFactorValidator"); -import { IRequestLogger } from "../../../../logging/IRequestLogger"; -import { IUserDataStore } from "../../../../storage/IUserDataStore"; -import { ITotpHandler } from "../../../../authentication/totp/ITotpHandler"; -import { TOTPSecret } from "../../../../../../types/TOTPSecret"; -import { TotpConfiguration } from "../../../../configuration/schema/TotpConfiguration"; - - -export default class RegistrationHandler implements IdentityValidable { - private logger: IRequestLogger; - private userDataStore: IUserDataStore; - private totp: ITotpHandler; - private configuration: TotpConfiguration; - - constructor(logger: IRequestLogger, - userDataStore: IUserDataStore, - totp: ITotpHandler, configuration: TotpConfiguration) { - this.logger = logger; - this.userDataStore = userDataStore; - this.totp = totp; - this.configuration = configuration; - } - - challenge(): string { - return Constants.CHALLENGE; - } - - private retrieveIdentity(req: express.Request): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - const userid = authSession.userid; - const email = authSession.email; - - if (!(userid && email)) { - return reject(new Error("User ID or email is missing")); - } - - const identity = { - email: email, - userid: userid - }; - return resolve(identity); - }); - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - return FirstFactorValidator.validate(req, this.logger) - .then(function () { - return that.retrieveIdentity(req); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return FirstFactorValidator.validate(req, this.logger); - } - - postValidationResponse(req: express.Request, res: express.Response) - : BluebirdPromise { - const that = this; - let secret: TOTPSecret; - let userId: string; - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - userId = authSession.userid; - - if (authSession.identity_check.challenge != Constants.CHALLENGE - || !userId) - return reject(new Error("Bad challenge.")); - - resolve(); - }) - .then(function () { - secret = that.totp.generate(userId, - that.configuration.issuer); - that.logger.debug(req, "Save the TOTP secret in DB"); - return that.userDataStore.saveTOTPSecret(userId, secret); - }) - .then(function () { - AuthenticationSessionHandler.reset(req); - - res.render(Constants.TEMPLATE_NAME, { - base32_secret: secret.base32, - otpauth_url: secret.otpauth_url, - login_endpoint: Endpoints.FIRST_FACTOR_GET - }); - }) - .catch(ErrorReplies.replyWithError200(req, res, that.logger, UserMessages.OPERATION_FAILED)); - } - - mailSubject(): string { - return "Set up Authelia's one-time password"; - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts b/themes/matrix/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts deleted file mode 100644 index 70a20d39d..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import Assert = require("assert"); -import Exceptions = require("../../../../Exceptions"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import SignPost = require("./post"); -import { ServerVariables } from "../../../../ServerVariables"; - -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { Level } from "../../../../authentication/Level"; - -describe("routes/secondfactor/totp/sign/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let authSession: AuthenticationSession; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - const app_get = Sinon.stub(); - req = { - originalUrl: "/api/totp-register", - app: {}, - body: { - token: "abc" - }, - session: {}, - query: { - redirect: "http://redirect" - } - }; - res = ExpressMock.ResponseMock(); - - const doc = { - userid: "user", - secret: { - base32: "ABCDEF" - } - }; - mocks.userDataStore.retrieveTOTPSecretStub.returns(BluebirdPromise.resolve(doc)); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - authSession.userid = "user"; - authSession.authentication_level = Level.ONE_FACTOR; - }); - - - it("should send status code 200 when totp is valid", function () { - mocks.totpHandler.validateStub.returns(true); - return SignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(authSession.authentication_level, Level.TWO_FACTOR); - return BluebirdPromise.resolve(); - }); - }); - - it("should send error message when totp is not valid", function () { - mocks.totpHandler.validateStub.returns(false); - return SignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.notEqual(authSession.authentication_level, Level.TWO_FACTOR); - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); -}); - diff --git a/themes/matrix/server/src/lib/routes/secondfactor/totp/sign/post.ts b/themes/matrix/server/src/lib/routes/secondfactor/totp/sign/post.ts deleted file mode 100644 index 34a276d12..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/totp/sign/post.ts +++ /dev/null @@ -1,42 +0,0 @@ -import Bluebird = require("bluebird"); -import Express = require("express"); - -import { TOTPSecretDocument } from "../../../../storage/TOTPSecretDocument"; -import Endpoints = require("../../../../../../../shared/api"); -import Redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; -import { Level } from "../../../../authentication/Level"; - -const UNAUTHORIZED_MESSAGE = "Unauthorized access"; - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response): Bluebird { - let authSession: AuthenticationSession; - const token = req.body.token; - - return new Bluebird(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - vars.logger.info(req, "Initiate TOTP validation for user \"%s\".", authSession.userid); - resolve(); - }) - .then(function () { - return vars.userDataStore.retrieveTOTPSecret(authSession.userid); - }) - .then(function (doc: TOTPSecretDocument) { - if (!vars.totpHandler.validate(token, doc.secret.base32)) - return Bluebird.reject(new Error("Invalid TOTP token.")); - - vars.logger.debug(req, "TOTP validation succeeded."); - authSession.authentication_level = Level.TWO_FACTOR; - Redirect(vars)(req, res); - return Bluebird.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/matrix/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts b/themes/matrix/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts deleted file mode 100644 index 7f16c0ee8..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts +++ /dev/null @@ -1,11 +0,0 @@ - -import util = require("util"); -import express = require("express"); - -function extract_app_id(req: express.Request): string { - return util.format("https://%s", req.headers.host); -} - -export = { - extract_app_id: extract_app_id -}; \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts b/themes/matrix/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts deleted file mode 100644 index a54bfbfe6..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); - -import { Identity } from "../../../../../../types/Identity"; -import RegistrationHandler from "./RegistrationHandler"; -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -describe("routes/secondfactor/u2f/identity/RegistrationHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - req.app = {}; - req.session = { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = Sinon.spy(); - res.json = Sinon.spy(); - res.status = Sinon.spy(); - }); - - describe("test u2f registration check", test_registration_check); - - function test_registration_check() { - it("should fail if first_factor has not been passed", function () { - req.session.auth.first_factor = false; - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if userid is missing", function () { - req.session.auth.first_factor = false; - req.session.auth.userid = undefined; - - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if email is missing", function () { - req.session.auth.first_factor = false; - req.session.auth.email = undefined; - - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should succeed if first factor passed, userid and email are provided", function () { - req.session.auth.first_factor = true; - req.session.auth.email = "admin@example.com"; - req.session.auth.userid = "user"; - return new RegistrationHandler(vars.logger).preValidationInit(req as any); - }); - } -}); diff --git a/themes/matrix/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts b/themes/matrix/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts deleted file mode 100644 index bc4713c77..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts +++ /dev/null @@ -1,73 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import objectPath = require("object-path"); - -import { IdentityValidable } from "../../../../IdentityValidable"; -import { Identity } from "../../../../../../types/Identity"; -import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate"; -import FirstFactorValidator = require("../../../../FirstFactorValidator"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { IRequestLogger } from "../../../../logging/IRequestLogger"; - -const CHALLENGE = "u2f-register"; -const MAIL_SUBJECT = "Register your security key with Authelia"; - -const POST_VALIDATION_TEMPLATE_NAME = "u2f-register"; - - -export default class RegistrationHandler implements IdentityValidable { - private logger: IRequestLogger; - - constructor(logger: IRequestLogger) { - this.logger = logger; - } - - challenge(): string { - return CHALLENGE; - } - - private retrieveIdentity(req: express.Request): BluebirdPromise { - const that = this; - return new BluebirdPromise(function(resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - const userid = authSession.userid; - const email = authSession.email; - - if (!(userid && email)) { - return reject(new Error("User ID or email is missing")); - } - - const identity = { - email: email, - userid: userid - }; - return resolve(identity); - }); - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - return FirstFactorValidator.validate(req, this.logger) - .then(function () { - return that.retrieveIdentity(req); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return FirstFactorValidator.validate(req, this.logger); - } - - postValidationResponse(req: express.Request, res: express.Response) { - res.render(POST_VALIDATION_TEMPLATE_NAME); - } - - mailSubject(): string { - return MAIL_SUBJECT; - } -} - diff --git a/themes/matrix/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts b/themes/matrix/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts deleted file mode 100644 index de3347a21..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts +++ /dev/null @@ -1,146 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import assert = require("assert"); -import U2FRegisterPost = require("./post"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - - -describe("routes/secondfactor/u2f/register/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - describe("test registration", test_registration); - - - function test_registration() { - it("should save u2f meta and return status code 200", function () { - const expectedStatus = { - keyHandle: "keyHandle", - publicKey: "pbk", - certificate: "cert" - }; - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve(expectedStatus)); - - authSession.register_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { - assert.equal("user", mocks.userDataStore.saveU2FRegistrationStub.getCall(0).args[0]); - assert.equal(authSession.identity_check, undefined); - }); - }); - - it("should return error message on finishRegistration error", function () { - mocks.u2f.checkRegistrationStub.returns({ errorCode: 500 }); - - authSession.register_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when register_request is not provided", function () { - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve()); - authSession.register_request = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when no auth request has been initiated", function () { - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve()); - authSession.register_request = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when identity has not been verified", function () { - authSession.identity_check = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - } -}); - diff --git a/themes/matrix/server/src/lib/routes/secondfactor/u2f/register/post.ts b/themes/matrix/server/src/lib/routes/secondfactor/u2f/register/post.ts deleted file mode 100644 index 7296ccbe5..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/u2f/register/post.ts +++ /dev/null @@ -1,64 +0,0 @@ - -import { UserDataStore } from "../../../../storage/UserDataStore"; -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import U2f = require("u2f"); -import { U2FRegistration } from "../../../../../../types/U2FRegistration"; -import redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; - - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appid = u2f_common.extract_app_id(req); - const registrationResponse: U2f.RegistrationData = req.body; - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - const registrationRequest = authSession.register_request; - - if (!registrationRequest) { - return reject(new Error("No registration request")); - } - - if (!authSession.identity_check - || authSession.identity_check.challenge != "u2f-register") { - return reject(new Error("Bad challenge for registration request")); - } - - vars.logger.info(req, "Finishing registration"); - vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest)); - vars.logger.debug(req, "RegistrationResponse = %s", JSON.stringify(registrationResponse)); - - return resolve(vars.u2f.checkRegistration(registrationRequest, registrationResponse)); - }) - .then(function (u2fResult: U2f.RegistrationResult | U2f.Error): BluebirdPromise { - if (objectPath.has(u2fResult, "errorCode")) - return BluebirdPromise.reject(new Error("Error while registering.")); - - const registrationResult: U2f.RegistrationResult = u2fResult as U2f.RegistrationResult; - vars.logger.info(req, "Store registration and reply"); - vars.logger.debug(req, "RegistrationResult = %s", JSON.stringify(registrationResult)); - const registration: U2FRegistration = { - keyHandle: registrationResult.keyHandle, - publicKey: registrationResult.publicKey - }; - return vars.userDataStore.saveU2FRegistration(authSession.userid, appid, registration); - }) - .then(function () { - authSession.identity_check = undefined; - redirect(vars)(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/matrix/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts b/themes/matrix/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts deleted file mode 100644 index a207c9109..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts +++ /dev/null @@ -1,86 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import U2FRegisterRequestGet = require("./get"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -describe("routes/secondfactor/u2f/register_request/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - describe("test registration request", () => { - it("should send back the registration request and save it in the session", function () { - const expectedRequest = { - test: "abc" - }; - mocks.u2f.requestStub.returns(BluebirdPromise.resolve(expectedRequest)); - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.deepEqual(expectedRequest, res.json.getCall(0).args[0]); - }); - }); - - it("should return internal error on registration request", function () { - res.send = sinon.spy(); - const user_key_container = {}; - mocks.u2f.requestStub.returns(BluebirdPromise.reject("Internal error")); - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); - - it("should return forbidden if identity has not been verified", function () { - req.session.auth.identity_check = undefined; - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(403, res.status.getCall(0).args[0]); - }); - }); - }); -}); - diff --git a/themes/matrix/server/src/lib/routes/secondfactor/u2f/register_request/get.ts b/themes/matrix/server/src/lib/routes/secondfactor/u2f/register_request/get.ts deleted file mode 100644 index f611af933..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/u2f/register_request/get.ts +++ /dev/null @@ -1,43 +0,0 @@ - -import { UserDataStore } from "../../../../storage/UserDataStore"; - -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import U2f = require("u2f"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appid: string = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.identity_check - || authSession.identity_check.challenge != "u2f-register") { - res.status(403); - res.send(); - return reject(new Error("Bad challenge.")); - } - - vars.logger.info(req, "Starting registration for appId '%s'", appid); - return resolve(vars.u2f.request(appid)); - }) - .then(function (registrationRequest: U2f.Request) { - vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest)); - authSession.register_request = registrationRequest; - res.json(registrationRequest); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - - return handler; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts b/themes/matrix/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts deleted file mode 100644 index 9b137e66d..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts +++ /dev/null @@ -1,101 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import U2FSignPost = require("./post"); -import { ServerVariables } from "../../../../ServerVariables"; -import winston = require("winston"); - -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import ExpressMock = require("../../../../stubs/express.spec"); -import U2FMock = require("../../../../stubs/u2f.spec"); -import U2f = require("u2f"); -import { Level } from "../../../../authentication/Level"; - -describe("routes/secondfactor/u2f/sign/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.app = {}; - req.originalUrl = "/api/xxxx"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req.session = { - auth: { - userid: "user", - authentication_level: Level.ONE_FACTOR, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - it("should return status code 204", function () { - const expectedStatus = { - keyHandle: "keyHandle", - publicKey: "pbk", - certificate: "cert" - }; - mocks.u2f.checkSignatureStub.returns(expectedStatus); - - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({ - registration: { - publicKey: "PUBKEY" - } - })); - - req.session.auth.sign_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FSignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(req.session.auth.authentication_level, Level.TWO_FACTOR); - }); - }); - - it("should return unauthorized error on registration request internal error", function () { - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({ - registration: { - publicKey: "PUBKEY" - } - })); - mocks.u2f.checkSignatureStub.returns({ errorCode: 500 }); - - req.session.auth.sign_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FSignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], - { error: "Operation failed." }); - }); - }); -}); - diff --git a/themes/matrix/server/src/lib/routes/secondfactor/u2f/sign/post.ts b/themes/matrix/server/src/lib/routes/secondfactor/u2f/sign/post.ts deleted file mode 100644 index 7ee711c2c..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/u2f/sign/post.ts +++ /dev/null @@ -1,57 +0,0 @@ - -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import { UserDataStore } from "../../../../storage/UserDataStore"; -import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocument"; -import { Winston } from "../../../../../../types/Dependencies"; -import U2f = require("u2f"); -import exceptions = require("../../../../Exceptions"); -import redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import { Level } from "../../../../authentication/Level"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appId = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.sign_request) { - const err = new Error("No sign request"); - ErrorReplies.replyWithError401(req, res, vars.logger)(err); - return reject(err); - } - resolve(); - }) - .then(function () { - const userid = authSession.userid; - return vars.userDataStore.retrieveU2FRegistration(userid, appId); - }) - .then(function (doc: U2FRegistrationDocument): BluebirdPromise { - const signRequest = authSession.sign_request; - const signData: U2f.SignatureData = req.body; - vars.logger.info(req, "Finish authentication"); - return BluebirdPromise.resolve(vars.u2f.checkSignature(signRequest, signData, doc.registration.publicKey)); - }) - .then(function (result: U2f.SignatureResult | U2f.Error): BluebirdPromise { - if (objectPath.has(result, "errorCode")) - return BluebirdPromise.reject(new Error("Error while signing")); - vars.logger.info(req, "Successful authentication"); - authSession.authentication_level = Level.TWO_FACTOR; - redirect(vars)(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - - return handler; -} - diff --git a/themes/matrix/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts b/themes/matrix/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts deleted file mode 100644 index dd52b27e0..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import assert = require("assert"); -import U2FSignRequestGet = require("./get"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { Request } from "u2f"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -import { SignMessage } from "../../../../../../../shared/SignMessage"; - -describe("routes/secondfactor/u2f/sign_request/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - it("should send back the sign request and save it in the session", function () { - const expectedRequest: Request = { - version: "U2F_V2", - appId: 'app', - challenge: 'challenge!' - }; - mocks.u2f.requestStub.returns(expectedRequest); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({ - registration: { - keyHandle: "KeyHandle" - } - })); - - return U2FSignRequestGet.default(vars)(req as any, res as any) - .then(() => { - assert.deepEqual(expectedRequest, req.session.auth.sign_request); - assert.deepEqual(expectedRequest, res.json.getCall(0).args[0]); - }); - }); -}); - diff --git a/themes/matrix/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts b/themes/matrix/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts deleted file mode 100644 index 9e93dde06..000000000 --- a/themes/matrix/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts +++ /dev/null @@ -1,42 +0,0 @@ - -import u2f_common = require("../../../secondfactor/u2f/U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocument"; -import exceptions = require("../../../../Exceptions"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appId = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - resolve(); - }) - .then(function () { - return vars.userDataStore.retrieveU2FRegistration(authSession.userid, appId); - }) - .then(function (doc: U2FRegistrationDocument): BluebirdPromise { - if (!doc) - return BluebirdPromise.reject(new exceptions.AccessDeniedError("No U2F registration document found.")); - - const appId: string = u2f_common.extract_app_id(req); - vars.logger.info(req, "Start authentication of app '%s'", appId); - vars.logger.debug(req, "AppId = %s, keyHandle = %s", appId, JSON.stringify(doc.registration.keyHandle)); - - const request = vars.u2f.request(appId, doc.registration.keyHandle); - authSession.sign_request = request; - res.json(request); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/matrix/server/src/lib/routes/verify/access_control.ts b/themes/matrix/server/src/lib/routes/verify/access_control.ts deleted file mode 100644 index 136239aeb..000000000 --- a/themes/matrix/server/src/lib/routes/verify/access_control.ts +++ /dev/null @@ -1,51 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import Util = require("util"); - -import Exceptions = require("../../Exceptions"); - -import { Level as AuthorizationLevel } from "../../authorization/Level"; -import { Level as AuthenticationLevel } from "../../authentication/Level"; -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; - -function isAuthorized( - authorization: AuthorizationLevel, - authentication: AuthenticationLevel): boolean { - - if (authorization == AuthorizationLevel.BYPASS) { - return true; - } else if (authorization == AuthorizationLevel.ONE_FACTOR && - authentication >= AuthenticationLevel.ONE_FACTOR) { - return true; - } else if (authorization == AuthorizationLevel.TWO_FACTOR && - authentication >= AuthenticationLevel.TWO_FACTOR) { - return true; - } - return false; -} - -export default function ( - req: Express.Request, - vars: ServerVariables, - domain: string, resource: string, - user: string, groups: string[], - authenticationLevel: AuthenticationLevel) { - - return new BluebirdPromise(function (resolve, reject) { - const authorizationLevel = vars.authorizer - .authorization({domain, resource}, {user, groups}); - - if (!isAuthorized(authorizationLevel, authenticationLevel)) { - if (authorizationLevel == AuthorizationLevel.DENY) { - reject(new Exceptions.NotAuthorizedError( - Util.format("User %s is not authorized to access %s%s", user, domain, resource))); - return; - } - reject(new Exceptions.NotAuthenticatedError(Util.format( - "User '%s' is not sufficiently authorized to access %s%s.", user, domain, resource))); - return; - } - resolve(); - }); -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/verify/get.spec.ts b/themes/matrix/server/src/lib/routes/verify/get.spec.ts deleted file mode 100644 index 67cf19fb6..000000000 --- a/themes/matrix/server/src/lib/routes/verify/get.spec.ts +++ /dev/null @@ -1,320 +0,0 @@ - -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import Express = require("express"); -import Sinon = require("sinon"); -import winston = require("winston"); - -import VerifyGet = require("./get"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariables } from "../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../ServerVariablesMockBuilder.spec"; -import { Level } from "../../authentication/Level"; -import { Level as AuthorizationLevel } from "../../authorization/Level"; - -describe("routes/verify/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - req.originalUrl = "/api/xxxx"; - req.query = { - redirect: "undefined" - }; - AuthenticationSessionHandler.reset(req as any); - req.headers["x-original-url"] = "https://secret.example.com/"; - const s = ServerVariablesMockBuilder.build(false); - mocks = s.mocks; - vars = s.variables; - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - describe("with session cookie", function () { - it("should be already authenticated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser"); - Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup"); - Assert.equal(204, res.status.getCall(0).args[0]); - }); - }); - - function test_session(_authSession: AuthenticationSession, status_code: number) { - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert.equal(status_code, res.status.getCall(0).args[0]); - }); - } - - function test_non_authenticated_401(authSession: AuthenticationSession) { - return test_session(authSession, 401); - } - - function test_unauthorized_403(authSession: AuthenticationSession) { - return test_session(authSession, 403); - } - - function test_authorized(authSession: AuthenticationSession) { - return test_session(authSession, 204); - } - - describe("given user tries to access a 2-factor endpoint", function () { - before(function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - }); - - describe("given different cases of session", function () { - it("should not be authenticated when second factor is missing", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: "user", - authentication_level: Level.ONE_FACTOR, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when userid is missing", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: undefined, - authentication_level: Level.TWO_FACTOR, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when level is insufficient", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: "user", - authentication_level: Level.NOT_AUTHENTICATED, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when session has not be initiated", function () { - return test_non_authenticated_401(undefined); - }); - - it("should not be authenticated when domain is not allowed for user", function () { - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - req.headers["x-original-url"] = "https://test.example.com/"; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.DENY); - - return test_unauthorized_403({ - keep_me_logged_in: false, - authentication_level: Level.TWO_FACTOR, - userid: "user", - groups: ["group1", "group2"], - email: undefined, - last_activity_datetime: new Date().getTime() - }); - }); - }); - }); - - describe("given user tries to access a single factor endpoint", function () { - beforeEach(function () { - req.headers["x-original-url"] = "https://redirect.url/"; - }); - - it("should be authenticated when first factor is validated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - authSession.authentication_level = Level.ONE_FACTOR; - authSession.userid = "user1"; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWith(204)); - Assert(res.send.calledOnce); - }); - }); - - it("should be rejected with 401 when not authenticated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - authSession.authentication_level = Level.NOT_AUTHENTICATED; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWith(401)); - }); - }); - }); - - describe("inactivity period", function () { - it("should update last inactivity period on requests on /api/verify", function () { - mocks.config.session.inactivity = 200000; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - const currentTime = new Date().getTime() - 1000; - AuthenticationSessionHandler.reset(req as any); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - authSession.last_activity_datetime = currentTime; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }) - .then(function (authSession) { - Assert(authSession.last_activity_datetime > currentTime); - }); - }); - - it("should reset session when max inactivity period has been reached", function () { - mocks.config.session.inactivity = 1; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - const currentTime = new Date().getTime() - 1000; - AuthenticationSessionHandler.reset(req as any); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - authSession.last_activity_datetime = currentTime; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }) - .then(function (authSession) { - Assert.equal(authSession.authentication_level, Level.NOT_AUTHENTICATED); - Assert.equal(authSession.userid, undefined); - }); - }); - }); - }); - - describe("response type 401 | 302", function() { - it("should return error code 401", function() { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should redirect to provided redirection url", function() { - const REDIRECT_URL = "http://redirection_url.com"; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - req.query["rd"] = REDIRECT_URL; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.redirect.calledWithExactly(REDIRECT_URL)); - }); - }); - }); - - describe("with basic auth", function () { - it("should authenticate correctly", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.returns({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "john"); - Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup"); - Assert.equal(204, res.status.getCall(0).args[0]); - }); - }); - - it("should fail when endpoint is protected by two factors", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.config.access_control.rules = [{ - domain: "secret.example.com", - policy: "two_factor" - }]; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when base64 token is not valid", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic i_m*not_a_base64*token"; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when base64 token has not format user:psswd", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzOmJhZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when bad user password is provided", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when resource is restricted", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - }); -}); - diff --git a/themes/matrix/server/src/lib/routes/verify/get.ts b/themes/matrix/server/src/lib/routes/verify/get.ts deleted file mode 100644 index f73861696..000000000 --- a/themes/matrix/server/src/lib/routes/verify/get.ts +++ /dev/null @@ -1,91 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Express = require("express"); -import Exceptions = require("../../Exceptions"); -import ErrorReplies = require("../../ErrorReplies"); -import { ServerVariables } from "../../ServerVariables"; -import GetWithSessionCookieMethod from "./get_session_cookie"; -import GetWithBasicAuthMethod from "./get_basic_auth"; -import Constants = require("../../../../../shared/constants"); -import ObjectPath = require("object-path"); - -import { AuthenticationSessionHandler } - from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; - -const REMOTE_USER = "Remote-User"; -const REMOTE_GROUPS = "Remote-Groups"; - - -function verifyWithSelectedMethod(req: Express.Request, res: Express.Response, - vars: ServerVariables, authSession: AuthenticationSession) - : () => BluebirdPromise<{ username: string, groups: string[] }> { - return function () { - const authorization: string = "" + req.headers["proxy-authorization"]; - if (authorization && authorization.startsWith("Basic ")) - return GetWithBasicAuthMethod(req, res, vars, authorization); - - return GetWithSessionCookieMethod(req, res, vars, authSession); - }; -} - -function setRedirectHeader(req: Express.Request, res: Express.Response) { - return function () { - const originalUrl = ObjectPath.get( - req, "headers.x-original-url"); - res.set("Redirect", originalUrl); - return BluebirdPromise.resolve(); - }; -} - -function setUserAndGroupsHeaders(res: Express.Response) { - return function (u: { username: string, groups: string[] }) { - res.setHeader(REMOTE_USER, u.username); - res.setHeader(REMOTE_GROUPS, u.groups.join(",")); - return BluebirdPromise.resolve(); - }; -} - -function replyWith200(res: Express.Response) { - return function () { - res.status(204); - res.send(); - }; -} - -function getRedirectParam(req: Express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -export default function (vars: ServerVariables) { - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - let authSession: AuthenticationSession; - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - resolve(); - }) - .then(setRedirectHeader(req, res)) - .then(verifyWithSelectedMethod(req, res, vars, authSession)) - .then(setUserAndGroupsHeaders(res)) - .then(replyWith200(res)) - // The user is authenticated but has restricted access -> 403 - .catch(Exceptions.NotAuthorizedError, - ErrorReplies.replyWithError403(req, res, vars.logger)) - .catch(Exceptions.NotAuthenticatedError, - ErrorReplies.replyWithError401(req, res, vars.logger)) - // The user is not yet authenticated -> 401 - .catch((err) => { - const redirectUrl = getRedirectParam(req); - if (redirectUrl) { - ErrorReplies.redirectTo(redirectUrl, req, res, vars.logger)(err); - } - else { - ErrorReplies.replyWithError401(req, res, vars.logger)(err); - } - }); - }; -} - diff --git a/themes/matrix/server/src/lib/routes/verify/get_basic_auth.ts b/themes/matrix/server/src/lib/routes/verify/get_basic_auth.ts deleted file mode 100644 index af23c76c9..000000000 --- a/themes/matrix/server/src/lib/routes/verify/get_basic_auth.ts +++ /dev/null @@ -1,55 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import ObjectPath = require("object-path"); -import { ServerVariables } from "../../ServerVariables"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; -import AccessControl from "./access_control"; -import { URLDecomposer } from "../../utils/URLDecomposer"; -import { Level } from "../../authentication/Level"; - -export default function (req: Express.Request, res: Express.Response, - vars: ServerVariables, authorizationHeader: string) - : BluebirdPromise<{ username: string, groups: string[] }> { - let username: string; - const uri = ObjectPath.get(req, "headers.x-original-url"); - const urlDecomposition = URLDecomposer.fromUrl(uri); - - return BluebirdPromise.resolve() - .then(() => { - const base64Re = new RegExp("^Basic ((?:[A-Za-z0-9+/]{4})*" + - "(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)$"); - const isTokenValidBase64 = base64Re.test(authorizationHeader); - - if (!isTokenValidBase64) { - return BluebirdPromise.reject(new Error("No valid base64 token found in the header")); - } - - const tokenMatches = authorizationHeader.match(base64Re); - const base64Token = tokenMatches[1]; - const decodedToken = Buffer.from(base64Token, "base64").toString(); - const splittedToken = decodedToken.split(":"); - - if (splittedToken.length != 2) { - return BluebirdPromise.reject(new Error( - "The authorization token is invalid. Expecting 'userid:password'")); - } - - username = splittedToken[0]; - const password = splittedToken[1]; - return vars.usersDatabase.checkUserPassword(username, password); - }) - .then(function (groupsAndEmails) { - return AccessControl(req, vars, urlDecomposition.domain, urlDecomposition.path, - username, groupsAndEmails.groups, Level.ONE_FACTOR) - .then(() => BluebirdPromise.resolve({ - username: username, - groups: groupsAndEmails.groups - })); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject( - new Error("Unable to authenticate the user with basic auth. Cause: " - + err.message)); - }); -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/routes/verify/get_session_cookie.ts b/themes/matrix/server/src/lib/routes/verify/get_session_cookie.ts deleted file mode 100644 index 070344812..000000000 --- a/themes/matrix/server/src/lib/routes/verify/get_session_cookie.ts +++ /dev/null @@ -1,78 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import Util = require("util"); -import ObjectPath = require("object-path"); - -import Exceptions = require("../../Exceptions"); -import { Configuration } from "../../configuration/schema/Configuration"; -import { ServerVariables } from "../../ServerVariables"; -import { IRequestLogger } from "../../logging/IRequestLogger"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; -import { AuthenticationSessionHandler } - from "../../AuthenticationSessionHandler"; -import AccessControl from "./access_control"; -import { URLDecomposer } from "../../utils/URLDecomposer"; - -function verify_inactivity(req: Express.Request, - authSession: AuthenticationSession, - configuration: Configuration, logger: IRequestLogger) - : BluebirdPromise { - - // If inactivity is not specified, then inactivity timeout does not apply - if (!configuration.session.inactivity || authSession.keep_me_logged_in) { - return BluebirdPromise.resolve(); - } - - const lastActivityTime = authSession.last_activity_datetime; - const currentTime = new Date().getTime(); - authSession.last_activity_datetime = currentTime; - - const inactivityPeriodMs = currentTime - lastActivityTime; - logger.debug(req, "Inactivity period was %s s and max period was %s.", - inactivityPeriodMs / 1000, configuration.session.inactivity / 1000); - if (inactivityPeriodMs < configuration.session.inactivity) { - return BluebirdPromise.resolve(); - } - - logger.debug(req, "Session has been reset after too long inactivity period."); - AuthenticationSessionHandler.reset(req); - return BluebirdPromise.reject(new Error("Inactivity period exceeded.")); -} - -export default function (req: Express.Request, res: Express.Response, - vars: ServerVariables, authSession: AuthenticationSession) - : BluebirdPromise<{ username: string, groups: string[] }> { - - return BluebirdPromise.resolve() - .then(() => { - const username = authSession.userid; - const groups = authSession.groups; - - if (!authSession.userid) { - return BluebirdPromise.reject(new Exceptions.AccessDeniedError( - "userid is missing")); - } - - const originalUrl = ObjectPath.get( - req, "headers.x-original-url"); - const originalUri = - ObjectPath.get(req, "headers.x-original-uri"); - - const d = URLDecomposer.fromUrl(originalUrl); - vars.logger.debug(req, "domain=%s, path=%s, user=%s, groups=%s", d.domain, - d.path, username, groups.join(",")); - return AccessControl(req, vars, d.domain, d.path, username, groups, - authSession.authentication_level); - }) - .then(() => { - return verify_inactivity(req, authSession, - vars.config, vars.logger); - }) - .then(() => { - return BluebirdPromise.resolve({ - username: authSession.userid, - groups: authSession.groups - }); - }); -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/AuthenticationTraceDocument.d.ts b/themes/matrix/server/src/lib/storage/AuthenticationTraceDocument.d.ts deleted file mode 100644 index 69818c055..000000000 --- a/themes/matrix/server/src/lib/storage/AuthenticationTraceDocument.d.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export interface AuthenticationTraceDocument { - userId: string; - date: Date; - isAuthenticationSuccessful: boolean; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/CollectionFactoryFactory.ts b/themes/matrix/server/src/lib/storage/CollectionFactoryFactory.ts deleted file mode 100644 index 92b29abfa..000000000 --- a/themes/matrix/server/src/lib/storage/CollectionFactoryFactory.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ICollectionFactory } from "./ICollectionFactory"; -import { NedbCollectionFactory } from "./nedb/NedbCollectionFactory"; -import { MongoCollectionFactory } from "./mongo/MongoCollectionFactory"; -import { IMongoClient } from "../connectors/mongo/IMongoClient"; - - -export class CollectionFactoryFactory { - static createNedb(options: Nedb.DataStoreOptions): ICollectionFactory { - return new NedbCollectionFactory(options); - } - - static createMongo(client: IMongoClient): ICollectionFactory { - return new MongoCollectionFactory(client); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/CollectionFactoryStub.spec.ts b/themes/matrix/server/src/lib/storage/CollectionFactoryStub.spec.ts deleted file mode 100644 index 17f8bb021..000000000 --- a/themes/matrix/server/src/lib/storage/CollectionFactoryStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import { ICollection } from "./ICollection"; -import { ICollectionFactory } from "./ICollectionFactory"; - -export class CollectionFactoryStub implements ICollectionFactory { - buildStub: Sinon.SinonStub; - - constructor() { - this.buildStub = Sinon.stub(); - } - - build(collectionName: string): ICollection { - return this.buildStub(collectionName); - } -} diff --git a/themes/matrix/server/src/lib/storage/CollectionStub.spec.ts b/themes/matrix/server/src/lib/storage/CollectionStub.spec.ts deleted file mode 100644 index 42895d672..000000000 --- a/themes/matrix/server/src/lib/storage/CollectionStub.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import { ICollection } from "./ICollection"; - -export class CollectionStub implements ICollection { - findStub: Sinon.SinonStub; - findOneStub: Sinon.SinonStub; - updateStub: Sinon.SinonStub; - removeStub: Sinon.SinonStub; - insertStub: Sinon.SinonStub; - - constructor() { - this.findStub = Sinon.stub(); - this.findOneStub = Sinon.stub(); - this.updateStub = Sinon.stub(); - this.removeStub = Sinon.stub(); - this.insertStub = Sinon.stub(); - } - - find(filter: any, sortKeys: any, count: number): BluebirdPromise { - return this.findStub(filter, sortKeys, count); - } - - findOne(filter: any): BluebirdPromise { - return this.findOneStub(filter); - } - - update(filter: any, document: any, options: any): BluebirdPromise { - return this.updateStub(filter, document, options); - } - - remove(filter: any): BluebirdPromise { - return this.removeStub(filter); - } - - insert(document: any): BluebirdPromise { - return this.insertStub(document); - } -} diff --git a/themes/matrix/server/src/lib/storage/ICollection.d.ts b/themes/matrix/server/src/lib/storage/ICollection.d.ts deleted file mode 100644 index caa6c2a87..000000000 --- a/themes/matrix/server/src/lib/storage/ICollection.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* istanbul ignore next */ -import BluebirdPromise = require("bluebird"); - -/* istanbul ignore next */ -export interface ICollection { - find(query: any, sortKeys: any, count: number): BluebirdPromise; - findOne(query: any): BluebirdPromise; - update(query: any, updateQuery: any, options?: any): BluebirdPromise; - remove(query: any): BluebirdPromise; - insert(document: any): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/ICollectionFactory.d.ts b/themes/matrix/server/src/lib/storage/ICollectionFactory.d.ts deleted file mode 100644 index 39eb42c77..000000000 --- a/themes/matrix/server/src/lib/storage/ICollectionFactory.d.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import { ICollection } from "./ICollection"; - -export interface ICollectionFactory { - build(collectionName: string): ICollection; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/IUserDataStore.d.ts b/themes/matrix/server/src/lib/storage/IUserDataStore.d.ts deleted file mode 100644 index 81df482aa..000000000 --- a/themes/matrix/server/src/lib/storage/IUserDataStore.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; - -export interface IUserDataStore { - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise; - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise; - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise; - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise; - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise; - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise; - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise; - retrieveTOTPSecret(userId: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/IdentityValidationDocument.d.ts b/themes/matrix/server/src/lib/storage/IdentityValidationDocument.d.ts deleted file mode 100644 index e7fd7b3f4..000000000 --- a/themes/matrix/server/src/lib/storage/IdentityValidationDocument.d.ts +++ /dev/null @@ -1,7 +0,0 @@ - -export interface IdentityValidationDocument { - userId: string; - token: string; - challenge: string; - maxDate: Date; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/TOTPSecretDocument.d.ts b/themes/matrix/server/src/lib/storage/TOTPSecretDocument.d.ts deleted file mode 100644 index a6c0bf9e6..000000000 --- a/themes/matrix/server/src/lib/storage/TOTPSecretDocument.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { TOTPSecret } from "../../../types/TOTPSecret"; - -export interface TOTPSecretDocument { - userid: string; - secret: TOTPSecret; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/U2FRegistrationDocument.d.ts b/themes/matrix/server/src/lib/storage/U2FRegistrationDocument.d.ts deleted file mode 100644 index efec6cb1d..000000000 --- a/themes/matrix/server/src/lib/storage/U2FRegistrationDocument.d.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import { U2FRegistration } from "../../../types/U2FRegistration"; - -export interface U2FRegistrationDocument { - userId: string; - appId: string; - registration: U2FRegistration; -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/UserDataStore.spec.ts b/themes/matrix/server/src/lib/storage/UserDataStore.spec.ts deleted file mode 100644 index 66fb85461..000000000 --- a/themes/matrix/server/src/lib/storage/UserDataStore.spec.ts +++ /dev/null @@ -1,264 +0,0 @@ - -import * as Assert from "assert"; -import * as Sinon from "sinon"; -import * as MockDate from "mockdate"; -import BluebirdPromise = require("bluebird"); - -import { UserDataStore } from "./UserDataStore"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { CollectionStub } from "./CollectionStub.spec"; -import { CollectionFactoryStub } from "./CollectionFactoryStub.spec"; - -describe("storage/UserDataStore", function () { - let factory: CollectionFactoryStub; - let collection: CollectionStub; - let userId: string; - let appId: string; - let totpSecret: TOTPSecret; - let u2fRegistration: U2FRegistration; - - beforeEach(function () { - factory = new CollectionFactoryStub(); - collection = new CollectionStub(); - - userId = "user"; - appId = "https://myappId"; - - totpSecret = { - ascii: "abc", - base32: "ABCDKZLEFZGREJK", - otpauth_url: "totp://test", - google_auth_qr: "dummy", - hex: "dummy", - qr_code_ascii: "dummy", - qr_code_base32: "dummy", - qr_code_hex: "dummy" - }; - - u2fRegistration = { - keyHandle: "KEY_HANDLE", - publicKey: "publickey" - }; - }); - - it("should correctly creates collections", function () { - new UserDataStore(factory); - - Assert.equal(4, factory.buildStub.callCount); - Assert(factory.buildStub.calledWith("authentication_traces")); - Assert(factory.buildStub.calledWith("identity_validation_tokens")); - Assert(factory.buildStub.calledWith("u2f_registrations")); - Assert(factory.buildStub.calledWith("totp_secrets")); - }); - - describe("TOTP secrets collection", function () { - it("should save a totp secret", function () { - factory.buildStub.returns(collection); - collection.updateStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveTOTPSecret(userId, totpSecret) - .then(function (doc) { - Assert(collection.updateStub.calledOnce); - Assert(collection.updateStub.calledWith({ userId: userId }, { - userId: userId, - secret: totpSecret - }, { upsert: true })); - return BluebirdPromise.resolve(); - }); - }); - - it("should retrieve a totp secret", function () { - factory.buildStub.returns(collection); - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveTOTPSecret(userId) - .then(function (doc) { - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ userId: userId })); - return BluebirdPromise.resolve(); - }); - }); - }); - - describe("U2F secrets collection", function () { - it("should save a U2F secret", function () { - factory.buildStub.returns(collection); - collection.updateStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveU2FRegistration(userId, appId, u2fRegistration) - .then(function (doc) { - Assert(collection.updateStub.calledOnce); - Assert(collection.updateStub.calledWith({ - userId: userId, - appId: appId - }, { - userId: userId, - appId: appId, - registration: u2fRegistration - }, { upsert: true })); - return BluebirdPromise.resolve(); - }); - }); - - it("should retrieve a U2F secret", function () { - factory.buildStub.returns(collection); - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveU2FRegistration(userId, appId) - .then(function (doc) { - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - userId: userId, - appId: appId - })); - return BluebirdPromise.resolve(); - }); - }); - }); - - - describe("Regulator traces collection", function () { - it("should save a trace", function () { - factory.buildStub.returns(collection); - collection.insertStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveAuthenticationTrace(userId, true) - .then(function (doc) { - Assert(collection.insertStub.calledOnce); - Assert(collection.insertStub.calledWith({ - userId: userId, - date: Sinon.match.date, - isAuthenticationSuccessful: true - })); - return BluebirdPromise.resolve(); - }); - }); - - function should_retrieve_latest_authentication_traces(count: number) { - factory.buildStub.returns(collection); - collection.findStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveLatestAuthenticationTraces(userId, count) - .then(function (doc: AuthenticationTraceDocument[]) { - Assert(collection.findStub.calledOnce); - Assert(collection.findStub.calledWith({ - userId: userId, - }, { date: -1 }, count)); - return BluebirdPromise.resolve(); - }); - } - - it("should retrieve 3 latest failed authentication traces", function () { - should_retrieve_latest_authentication_traces(3); - }); - }); - - - describe("Identity validation collection", function () { - it("should save a identity validation token", function () { - factory.buildStub.returns(collection); - collection.insertStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - const maxAge = 400; - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - return dataStore.produceIdentityValidationToken(userId, token, challenge, maxAge) - .then(function (doc) { - Assert(collection.insertStub.calledOnce); - Assert(collection.insertStub.calledWith({ - userId: userId, - token: token, - challenge: challenge, - maxDate: Sinon.match.date - })); - return BluebirdPromise.resolve(); - }); - }); - - it("should consume an identity token successfully", function () { - factory.buildStub.returns(collection); - - MockDate.set(100); - - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve({ - userId: "USER", - token: token, - challenge: challenge, - maxDate: new Date() - })); - collection.removeStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - MockDate.set(80); - - return dataStore.consumeIdentityValidationToken(token, challenge) - .then(function (doc) { - MockDate.reset(); - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - token: token, - challenge: challenge - })); - - Assert(collection.removeStub.calledOnce); - Assert(collection.removeStub.calledWith({ - token: token, - challenge: challenge - })); - return BluebirdPromise.resolve(); - }); - }); - - it("should consume an expired identity token", function () { - factory.buildStub.returns(collection); - - MockDate.set(0); - - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve({ - userId: "USER", - token: token, - challenge: challenge, - maxDate: new Date() - })); - - const dataStore = new UserDataStore(factory); - - MockDate.set(80000); - - return dataStore.consumeIdentityValidationToken(token, challenge) - .then(function () { return BluebirdPromise.reject(new Error("should not be here")); }) - .catch(function () { - MockDate.reset(); - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - token: token, - challenge: challenge - })); - return BluebirdPromise.resolve(); - }); - }); - }); -}); diff --git a/themes/matrix/server/src/lib/storage/UserDataStore.ts b/themes/matrix/server/src/lib/storage/UserDataStore.ts deleted file mode 100644 index 27b0cddbd..000000000 --- a/themes/matrix/server/src/lib/storage/UserDataStore.ts +++ /dev/null @@ -1,143 +0,0 @@ -import * as BluebirdPromise from "bluebird"; -import * as path from "path"; -import { IUserDataStore } from "./IUserDataStore"; -import { ICollection } from "./ICollection"; -import { ICollectionFactory } from "./ICollectionFactory"; -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; - -// Constants - -const IDENTITY_VALIDATION_TOKENS_COLLECTION_NAME = "identity_validation_tokens"; -const AUTHENTICATION_TRACES_COLLECTION_NAME = "authentication_traces"; - -const U2F_REGISTRATIONS_COLLECTION_NAME = "u2f_registrations"; -const TOTP_SECRETS_COLLECTION_NAME = "totp_secrets"; - - -export interface U2FRegistrationKey { - userId: string; - appId: string; -} - -// Source - -export class UserDataStore implements IUserDataStore { - private u2fSecretCollection: ICollection; - private identityCheckTokensCollection: ICollection; - private authenticationTracesCollection: ICollection; - private totpSecretCollection: ICollection; - - private collectionFactory: ICollectionFactory; - - constructor(collectionFactory: ICollectionFactory) { - this.collectionFactory = collectionFactory; - - this.u2fSecretCollection = this.collectionFactory.build(U2F_REGISTRATIONS_COLLECTION_NAME); - this.identityCheckTokensCollection = this.collectionFactory.build(IDENTITY_VALIDATION_TOKENS_COLLECTION_NAME); - this.authenticationTracesCollection = this.collectionFactory.build(AUTHENTICATION_TRACES_COLLECTION_NAME); - this.totpSecretCollection = this.collectionFactory.build(TOTP_SECRETS_COLLECTION_NAME); - } - - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise { - const newDocument: U2FRegistrationDocument = { - userId: userId, - appId: appId, - registration: registration - }; - - const filter: U2FRegistrationKey = { - userId: userId, - appId: appId - }; - - return this.u2fSecretCollection.update(filter, newDocument, { upsert: true }); - } - - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise { - const filter: U2FRegistrationKey = { - userId: userId, - appId: appId - }; - return this.u2fSecretCollection.findOne(filter); - } - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - const newDocument: AuthenticationTraceDocument = { - userId: userId, - date: new Date(), - isAuthenticationSuccessful: isAuthenticationSuccessful, - }; - - return this.authenticationTracesCollection.insert(newDocument); - } - - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise { - const q = { - userId: userId - }; - - return this.authenticationTracesCollection.find(q, { date: -1 }, count); - } - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise { - const newDocument: IdentityValidationDocument = { - userId: userId, - token: token, - challenge: challenge, - maxDate: new Date(new Date().getTime() + maxAge) - }; - - return this.identityCheckTokensCollection.insert(newDocument); - } - - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise { - const that = this; - const filter = { - token: token, - challenge: challenge - }; - - let identityValidationDocument: IdentityValidationDocument; - - return this.identityCheckTokensCollection.findOne(filter) - .then(function (doc: IdentityValidationDocument) { - if (!doc) { - return BluebirdPromise.reject(new Error("Registration token does not exist")); - } - - identityValidationDocument = doc; - const current_date = new Date(); - if (current_date > doc.maxDate) - return BluebirdPromise.reject(new Error("Registration token is not valid anymore")); - - return that.identityCheckTokensCollection.remove(filter); - }) - .then(() => { - return BluebirdPromise.resolve(identityValidationDocument); - }); - } - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise { - const doc = { - userId: userId, - secret: secret - }; - - const filter = { - userId: userId - }; - return this.totpSecretCollection.update(filter, doc, { upsert: true }); - } - - retrieveTOTPSecret(userId: string): BluebirdPromise { - const filter = { - userId: userId - }; - return this.totpSecretCollection.findOne(filter); - } -} diff --git a/themes/matrix/server/src/lib/storage/UserDataStoreStub.spec.ts b/themes/matrix/server/src/lib/storage/UserDataStoreStub.spec.ts deleted file mode 100644 index 5ea27a2de..000000000 --- a/themes/matrix/server/src/lib/storage/UserDataStoreStub.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); - -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; -import { IUserDataStore } from "./IUserDataStore"; - -export class UserDataStoreStub implements IUserDataStore { - saveU2FRegistrationStub: Sinon.SinonStub; - retrieveU2FRegistrationStub: Sinon.SinonStub; - saveAuthenticationTraceStub: Sinon.SinonStub; - retrieveLatestAuthenticationTracesStub: Sinon.SinonStub; - produceIdentityValidationTokenStub: Sinon.SinonStub; - consumeIdentityValidationTokenStub: Sinon.SinonStub; - saveTOTPSecretStub: Sinon.SinonStub; - retrieveTOTPSecretStub: Sinon.SinonStub; - - constructor() { - this.saveU2FRegistrationStub = Sinon.stub(); - this.retrieveU2FRegistrationStub = Sinon.stub(); - this.saveAuthenticationTraceStub = Sinon.stub(); - this.retrieveLatestAuthenticationTracesStub = Sinon.stub(); - this.produceIdentityValidationTokenStub = Sinon.stub(); - this.consumeIdentityValidationTokenStub = Sinon.stub(); - this.saveTOTPSecretStub = Sinon.stub(); - this.retrieveTOTPSecretStub = Sinon.stub(); - } - - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise { - return this.saveU2FRegistrationStub(userId, appId, registration); - } - - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise { - return this.retrieveU2FRegistrationStub(userId, appId); - } - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - return this.saveAuthenticationTraceStub(userId, isAuthenticationSuccessful); - } - - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise { - return this.retrieveLatestAuthenticationTracesStub(userId, count); - } - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise { - return this.produceIdentityValidationTokenStub(userId, token, challenge, maxAge); - } - - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise { - return this.consumeIdentityValidationTokenStub(token, challenge); - } - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise { - return this.saveTOTPSecretStub(userId, secret); - } - - retrieveTOTPSecret(userId: string): BluebirdPromise { - return this.retrieveTOTPSecretStub(userId); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/mongo/MongoCollection.spec.ts b/themes/matrix/server/src/lib/storage/mongo/MongoCollection.spec.ts deleted file mode 100644 index 74a773a1e..000000000 --- a/themes/matrix/server/src/lib/storage/mongo/MongoCollection.spec.ts +++ /dev/null @@ -1,110 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import MongoDB = require("mongodb"); -import BluebirdPromise = require("bluebird"); -import { MongoClientStub } from "../../connectors/mongo/MongoClientStub.spec"; -import { MongoCollection } from "./MongoCollection"; - -describe("storage/mongo/MongoCollection", function () { - let mongoCollectionStub: any; - let mongoClientStub: MongoClientStub; - let findStub: Sinon.SinonStub; - let findOneStub: Sinon.SinonStub; - let insertOneStub: Sinon.SinonStub; - let updateStub: Sinon.SinonStub; - let removeStub: Sinon.SinonStub; - let countStub: Sinon.SinonStub; - const COLLECTION_NAME = "collection"; - - before(function () { - mongoClientStub = new MongoClientStub(); - mongoCollectionStub = Sinon.createStubInstance(require("mongodb").Collection as any); - findStub = mongoCollectionStub.find as Sinon.SinonStub; - findOneStub = mongoCollectionStub.findOne as Sinon.SinonStub; - insertOneStub = mongoCollectionStub.insertOne as Sinon.SinonStub; - updateStub = mongoCollectionStub.update as Sinon.SinonStub; - removeStub = mongoCollectionStub.remove as Sinon.SinonStub; - countStub = mongoCollectionStub.count as Sinon.SinonStub; - mongoClientStub.collectionStub.returns( - BluebirdPromise.resolve(mongoCollectionStub) - ); - }); - - describe("find", function () { - it("should find a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - findStub.returns({ - sort: Sinon.stub().returns({ - limit: Sinon.stub().returns({ - toArray: Sinon.stub().returns(BluebirdPromise.resolve([])) - }) - }) - }); - - return collection.find({ key: "KEY" }) - .then(function () { - Assert(findStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("findOne", function () { - it("should find one document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - findOneStub.returns(BluebirdPromise.resolve({})); - - return collection.findOne({ key: "KEY" }) - .then(function () { - Assert(findOneStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("insert", function () { - it("should insert a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - insertOneStub.returns(BluebirdPromise.resolve({})); - - return collection.insert({ key: "KEY" }) - .then(function () { - Assert(insertOneStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("update", function () { - it("should update a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - updateStub.returns(BluebirdPromise.resolve({})); - - return collection.update({ key: "KEY" }, { key: "KEY", value: 1 }) - .then(function () { - Assert(updateStub.calledWith({ key: "KEY" }, { key: "KEY", value: 1 })); - }); - }); - }); - - describe("remove", function () { - it("should remove a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - removeStub.returns(BluebirdPromise.resolve({})); - - return collection.remove({ key: "KEY" }) - .then(function () { - Assert(removeStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("count", function () { - it("should count documents in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - countStub.returns(BluebirdPromise.resolve({})); - - return collection.count({ key: "KEY" }) - .then(function () { - Assert(countStub.calledWith({ key: "KEY" })); - }); - }); - }); -}); diff --git a/themes/matrix/server/src/lib/storage/mongo/MongoCollection.ts b/themes/matrix/server/src/lib/storage/mongo/MongoCollection.ts deleted file mode 100644 index 9771389f1..000000000 --- a/themes/matrix/server/src/lib/storage/mongo/MongoCollection.ts +++ /dev/null @@ -1,50 +0,0 @@ -import Bluebird = require("bluebird"); -import { ICollection } from "../ICollection"; -import MongoDB = require("mongodb"); -import { IMongoClient } from "../../connectors/mongo/IMongoClient"; - - -export class MongoCollection implements ICollection { - private mongoClient: IMongoClient; - private collectionName: string; - - constructor(collectionName: string, mongoClient: IMongoClient) { - this.collectionName = collectionName; - this.mongoClient = mongoClient; - } - - private collection(): Bluebird { - return this.mongoClient.collection(this.collectionName); - } - - find(query: any, sortKeys?: any, count?: number): Bluebird { - return this.collection() - .then((collection) => collection.find(query).sort(sortKeys).limit(count)) - .then((query) => query.toArray()); - } - - findOne(query: any): Bluebird { - return this.collection() - .then((collection) => collection.findOne(query)); - } - - update(query: any, updateQuery: any, options?: any): Bluebird { - return this.collection() - .then((collection) => collection.update(query, updateQuery, options)); - } - - remove(query: any): Bluebird { - return this.collection() - .then((collection) => collection.remove(query)); - } - - insert(document: any): Bluebird { - return this.collection() - .then((collection) => collection.insertOne(document)); - } - - count(query: any): Bluebird { - return this.collection() - .then((collection) => collection.count(query)); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts b/themes/matrix/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts deleted file mode 100644 index bd959cacb..000000000 --- a/themes/matrix/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import { MongoClientStub } from "../../connectors/mongo/MongoClientStub.spec"; -import { MongoCollectionFactory } from "./MongoCollectionFactory"; - -describe("storage/mongo/MongoCollectionFactory", function () { - let mongoClient: MongoClientStub; - - before(function() { - mongoClient = new MongoClientStub(); - }); - - describe("create", function () { - it("should create a collection", function () { - const COLLECTION_NAME = "COLLECTION_NAME"; - - const factory = new MongoCollectionFactory(mongoClient); - Assert(factory.build(COLLECTION_NAME)); - }); - }); -}); diff --git a/themes/matrix/server/src/lib/storage/mongo/MongoCollectionFactory.ts b/themes/matrix/server/src/lib/storage/mongo/MongoCollectionFactory.ts deleted file mode 100644 index 14a8262c9..000000000 --- a/themes/matrix/server/src/lib/storage/mongo/MongoCollectionFactory.ts +++ /dev/null @@ -1,19 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ICollection } from "../ICollection"; -import { ICollectionFactory } from "../ICollectionFactory"; -import { MongoCollection } from "./MongoCollection"; -import path = require("path"); -import MongoDB = require("mongodb"); -import { IMongoClient } from "../../connectors/mongo/IMongoClient"; - -export class MongoCollectionFactory implements ICollectionFactory { - private mongoClient: IMongoClient; - - constructor(mongoClient: IMongoClient) { - this.mongoClient = mongoClient; - } - - build(collectionName: string): ICollection { - return new MongoCollection(collectionName, this.mongoClient); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/nedb/NedbCollection.spec.ts b/themes/matrix/server/src/lib/storage/nedb/NedbCollection.spec.ts deleted file mode 100644 index a69962b67..000000000 --- a/themes/matrix/server/src/lib/storage/nedb/NedbCollection.spec.ts +++ /dev/null @@ -1,136 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); - -import { NedbCollection } from "./NedbCollection"; - -describe("storage/nedb/NedbCollection", function () { - describe("insert", function () { - it("should insert one entry", function () { - const nedbOptions = { - inMemoryOnly: true - }; - const collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - - return collection.count({}).then(function (count: number) { - Assert.equal(1, count); - }); - }); - - it("should insert three entries", function () { - const nedbOptions = { - inMemoryOnly: true - }; - const collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - collection.insert({ key: "hello" }); - collection.insert({ key: "hey" }); - - return collection.count({}).then(function (count: number) { - Assert.equal(3, count); - }); - }); - }); - - describe("find", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "hello" }); - collection.insert({ key: "hey" }); - collection.insert({ key: "coucou", value: 2 }); - }); - - it("should find one hello", function () { - return collection.find({ key: "hello" }, { key: 1 }) - .then(function (docs: { key: string }[]) { - Assert.equal(1, docs.length); - Assert(docs[0].key == "hello"); - }); - }); - - it("should find two coucou", function () { - return collection.find({ key: "coucou" }, { value: 1 }) - .then(function (docs: { value: number }[]) { - Assert.equal(2, docs.length); - }); - }); - }); - - describe("findOne", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - }); - - it("should find two coucou", function () { - const doc = { key: "coucou", value: 1 }; - return collection.count(doc) - .then(function (count: number) { - Assert.equal(4, count); - return collection.findOne(doc); - }); - }); - }); - - describe("update", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - }); - - it("should update the value", function () { - return collection.update({ key: "coucou" }, { key: "coucou", value: 2 }, { multi: true }) - .then(function () { - return collection.find({ key: "coucou" }); - }) - .then(function (docs: { key: string, value: number }[]) { - Assert.equal(1, docs.length); - Assert.equal(2, docs[0].value); - }); - }); - }); - - describe("update", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - collection.insert({ key: "hello" }); - }); - - it("should update the value", function () { - return collection.remove({ key: "coucou" }) - .then(function () { - return collection.count({}); - }) - .then(function (count: number) { - Assert.equal(1, count); - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/nedb/NedbCollection.ts b/themes/matrix/server/src/lib/storage/nedb/NedbCollection.ts deleted file mode 100644 index 88a93ad05..000000000 --- a/themes/matrix/server/src/lib/storage/nedb/NedbCollection.ts +++ /dev/null @@ -1,47 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ICollection } from "../ICollection"; -import Nedb = require("nedb"); - -declare module "nedb" { - export class NedbAsync extends Nedb { - constructor(pathOrOptions?: string | Nedb.DataStoreOptions); - updateAsync(query: any, updateQuery: any, options?: Nedb.UpdateOptions): BluebirdPromise; - findOneAsync(query: any): BluebirdPromise; - insertAsync(newDoc: T): BluebirdPromise; - removeAsync(query: any): BluebirdPromise; - countAsync(query: any): BluebirdPromise; - } -} - -export class NedbCollection implements ICollection { - private collection: Nedb.NedbAsync; - - constructor(options: Nedb.DataStoreOptions) { - this.collection = BluebirdPromise.promisifyAll(new Nedb(options)) as Nedb.NedbAsync; - } - - find(query: any, sortKeys?: any, count?: number): BluebirdPromise { - const q = this.collection.find(query).sort(sortKeys).limit(count); - return BluebirdPromise.promisify(q.exec, { context: q })(); - } - - findOne(query: any): BluebirdPromise { - return this.collection.findOneAsync(query); - } - - update(query: any, updateQuery: any, options?: any): BluebirdPromise { - return this.collection.updateAsync(query, updateQuery, options); - } - - remove(query: any): BluebirdPromise { - return this.collection.removeAsync(query); - } - - insert(document: any): BluebirdPromise { - return this.collection.insertAsync(document); - } - - count(query: any): BluebirdPromise { - return this.collection.countAsync(query); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts b/themes/matrix/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts deleted file mode 100644 index da90c661f..000000000 --- a/themes/matrix/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); - -import { NedbCollectionFactory } from "./NedbCollectionFactory"; - -describe("storage/nedb/NedbCollectionFactory", function() { - it("should create a nedb collection", function() { - const nedbOptions = { - inMemoryOnly: true - }; - const factory = new NedbCollectionFactory(nedbOptions); - - const collection = factory.build("mycollection"); - Assert(collection); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/storage/nedb/NedbCollectionFactory.ts b/themes/matrix/server/src/lib/storage/nedb/NedbCollectionFactory.ts deleted file mode 100644 index 49c4dc853..000000000 --- a/themes/matrix/server/src/lib/storage/nedb/NedbCollectionFactory.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ICollection } from "../ICollection"; -import { ICollectionFactory } from "../ICollectionFactory"; -import { NedbCollection } from "./NedbCollection"; -import path = require("path"); -import Nedb = require("nedb"); - -export interface NedbOptions { - inMemoryOnly?: boolean; - directory?: string; -} - -export class NedbCollectionFactory implements ICollectionFactory { - private options: Nedb.DataStoreOptions; - - constructor(options: Nedb.DataStoreOptions) { - this.options = options; - } - - build(collectionName: string): ICollection { - const datastoreOptions: Nedb.DataStoreOptions = { - inMemoryOnly: this.options.inMemoryOnly || false, - autoload: true, - filename: (this.options.filename) ? path.resolve(this.options.filename, collectionName) : undefined - }; - - return new NedbCollection(datastoreOptions); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/stubs/express.spec.ts b/themes/matrix/server/src/lib/stubs/express.spec.ts deleted file mode 100644 index 48f15d7e1..000000000 --- a/themes/matrix/server/src/lib/stubs/express.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ - -import sinon = require("sinon"); -import express = require("express"); - -export interface RequestMock { - app?: any; - body?: any; - session?: any; - headers?: any; - get?: any; - query?: any; - originalUrl: string; -} - -export interface ResponseMock { - send: sinon.SinonStub | sinon.SinonSpy; - sendStatus: sinon.SinonStub; - sendFile: sinon.SinonStub; - sendfile: sinon.SinonStub; - status: sinon.SinonStub | sinon.SinonSpy; - json: sinon.SinonStub | sinon.SinonSpy; - links: sinon.SinonStub; - jsonp: sinon.SinonStub; - download: sinon.SinonStub; - contentType: sinon.SinonStub; - type: sinon.SinonStub; - format: sinon.SinonStub; - attachment: sinon.SinonStub; - set: sinon.SinonStub; - header: sinon.SinonStub; - headersSent: boolean; - get: sinon.SinonStub; - clearCookie: sinon.SinonStub; - cookie: sinon.SinonStub; - location: sinon.SinonStub; - redirect: sinon.SinonStub | sinon.SinonSpy; - render: sinon.SinonStub | sinon.SinonSpy; - locals: sinon.SinonStub; - charset: string; - vary: sinon.SinonStub; - app: any; - write: sinon.SinonStub; - writeContinue: sinon.SinonStub; - writeHead: sinon.SinonStub; - statusCode: number; - statusMessage: string; - setHeader: sinon.SinonStub; - setTimeout: sinon.SinonStub; - sendDate: boolean; - getHeader: sinon.SinonStub; -} - -export function RequestMock(): RequestMock { - return { - originalUrl: "/non-api/xxx", - app: { - get: sinon.stub() - }, - headers: { - "x-forwarded-for": "127.0.0.1" - }, - session: {} - }; -} -export function ResponseMock(): ResponseMock { - return { - send: sinon.stub(), - status: sinon.stub(), - json: sinon.stub(), - sendStatus: sinon.stub(), - links: sinon.stub(), - jsonp: sinon.stub(), - sendFile: sinon.stub(), - sendfile: sinon.stub(), - download: sinon.stub(), - contentType: sinon.stub(), - type: sinon.stub(), - format: sinon.stub(), - attachment: sinon.stub(), - set: sinon.stub(), - header: sinon.stub(), - headersSent: true, - get: sinon.stub(), - clearCookie: sinon.stub(), - cookie: sinon.stub(), - location: sinon.stub(), - redirect: sinon.stub(), - render: sinon.stub(), - locals: sinon.stub(), - charset: "utf-8", - vary: sinon.stub(), - app: sinon.stub(), - write: sinon.stub(), - writeContinue: sinon.stub(), - writeHead: sinon.stub(), - statusCode: 200, - statusMessage: "message", - setHeader: sinon.stub(), - setTimeout: sinon.stub(), - sendDate: true, - getHeader: sinon.stub() - }; -} diff --git a/themes/matrix/server/src/lib/stubs/ldapjs.spec.ts b/themes/matrix/server/src/lib/stubs/ldapjs.spec.ts deleted file mode 100644 index 045c0e11b..000000000 --- a/themes/matrix/server/src/lib/stubs/ldapjs.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ - -import Sinon = require("sinon"); - -export class LdapjsMock { - createClientStub: sinon.SinonStub; - - constructor() { - this.createClientStub = Sinon.stub(); - } - - createClient(params: any) { - return this.createClientStub(params); - } -} - -export class LdapjsClientMock { - bindStub: sinon.SinonStub; - unbindStub: sinon.SinonStub; - searchStub: sinon.SinonStub; - modifyStub: sinon.SinonStub; - onStub: sinon.SinonStub; - - constructor() { - this.bindStub = Sinon.stub(); - this.unbindStub = Sinon.stub(); - this.searchStub = Sinon.stub(); - this.modifyStub = Sinon.stub(); - this.onStub = Sinon.stub(); - } - - bind() { - return this.bindStub(); - } - - unbind() { - return this.unbindStub(); - } - - search() { - return this.searchStub(); - } - - modify() { - return this.modifyStub(); - } - - on() { - return this.onStub(); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/stubs/speakeasy.spec.ts b/themes/matrix/server/src/lib/stubs/speakeasy.spec.ts deleted file mode 100644 index 023614dcd..000000000 --- a/themes/matrix/server/src/lib/stubs/speakeasy.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import sinon = require("sinon"); - -export = { - totp: sinon.stub(), - generateSecret: sinon.stub() -}; diff --git a/themes/matrix/server/src/lib/stubs/u2f.spec.ts b/themes/matrix/server/src/lib/stubs/u2f.spec.ts deleted file mode 100644 index 234b28c11..000000000 --- a/themes/matrix/server/src/lib/stubs/u2f.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ - -import sinon = require("sinon"); - -export interface U2FMock { - request: sinon.SinonStub; - checkSignature: sinon.SinonStub; - checkRegistration: sinon.SinonStub; -} - -export function U2FMock(): U2FMock { - return { - request: sinon.stub(), - checkSignature: sinon.stub(), - checkRegistration: sinon.stub() - }; -} diff --git a/themes/matrix/server/src/lib/utils/HashGenerator.spec.ts b/themes/matrix/server/src/lib/utils/HashGenerator.spec.ts deleted file mode 100644 index f19619a65..000000000 --- a/themes/matrix/server/src/lib/utils/HashGenerator.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Assert = require("assert"); -import { HashGenerator } from "./HashGenerator"; - -describe("utils/HashGenerator", function () { - it("should compute correct ssha512 (password)", function () { - return HashGenerator.ssha512("password", 500000, "jgiCMRyGXzoqpxS3") - .then(function (hash: string) { - Assert.equal(hash, "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"); - }); - }); - - it("should compute correct ssha512 (test)", function () { - return HashGenerator.ssha512("test", 500000, "abcdefghijklmnop") - .then(function (hash: string) { - Assert.equal(hash, "{CRYPT}$6$rounds=500000$abcdefghijklmnop$sTlNGf0VO/HTQIOXemmaBbV28HUch/qhWOA1/4dsDj6CDQYhUgXbYSPL6gccAsWMr2zD5fFWwhKmPdG.yxphs."); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/utils/HashGenerator.ts b/themes/matrix/server/src/lib/utils/HashGenerator.ts deleted file mode 100644 index e67de32b7..000000000 --- a/themes/matrix/server/src/lib/utils/HashGenerator.ts +++ /dev/null @@ -1,23 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import RandomString = require("randomstring"); -import Util = require("util"); -const crypt = require("crypt3"); - -export class HashGenerator { - static ssha512( - password: string, - rounds: number = 500000, - salt?: string): BluebirdPromise { - const saltSize = 16; - // $6 means SHA512 - const _salt = Util.format("$6$rounds=%d$%s", rounds, - (salt) ? salt : RandomString.generate(16)); - - const cryptAsync = BluebirdPromise.promisify(crypt); - - return cryptAsync(password, _salt) - .then(function (hash: string) { - return BluebirdPromise.resolve(Util.format("{CRYPT}%s", hash)); - }); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/utils/ObjectCloner.ts b/themes/matrix/server/src/lib/utils/ObjectCloner.ts deleted file mode 100644 index 3e125d749..000000000 --- a/themes/matrix/server/src/lib/utils/ObjectCloner.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export class ObjectCloner { - static clone(obj: any): any { - return JSON.parse(JSON.stringify(obj)); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/utils/SafeRedirection.spec.ts b/themes/matrix/server/src/lib/utils/SafeRedirection.spec.ts deleted file mode 100644 index 4126949fd..000000000 --- a/themes/matrix/server/src/lib/utils/SafeRedirection.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import { SafeRedirector } from "./SafeRedirection"; - -describe("web_server/middlewares/SafeRedirection", () => { - describe("Url is in protected domain", () => { - before(() => { - this.redirector = new SafeRedirector("example.com"); - this.res = {redirect: Sinon.stub()}; - }); - - it("should redirect to provided url", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.example.com:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://mysubdomain.example.com:8080/abc")); - }); - - it("should redirect to default url when wrong domain", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.domain.rtf:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://authelia.example.com")); - }); - - it("should redirect to default url when not terminating by domain", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.example.com.rtf:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://authelia.example.com")); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/utils/SafeRedirection.ts b/themes/matrix/server/src/lib/utils/SafeRedirection.ts deleted file mode 100644 index 9e6a32e0c..000000000 --- a/themes/matrix/server/src/lib/utils/SafeRedirection.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Express = require("express"); -import { DomainExtractor } from "../../../../shared/DomainExtractor"; -import { BelongToDomain } from "../../../../shared/BelongToDomain"; - - -export class SafeRedirector { - private domain: string; - - constructor(domain: string) { - this.domain = domain; - } - - redirectOrElse( - res: Express.Response, - url: string, - defaultUrl: string): void { - if (BelongToDomain(url, this.domain)) { - res.redirect(url); - } - res.redirect(defaultUrl); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/utils/URLDecomposer.spec.ts b/themes/matrix/server/src/lib/utils/URLDecomposer.spec.ts deleted file mode 100644 index cbb038738..000000000 --- a/themes/matrix/server/src/lib/utils/URLDecomposer.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { URLDecomposer } from "./URLDecomposer"; -import Assert = require("assert"); - -describe("utils/URLDecomposer", function () { - describe("test fromUrl", function () { - it("should return domain from https url", function () { - const d = URLDecomposer.fromUrl("https://www.example.com/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return domain from http url", function () { - const d = URLDecomposer.fromUrl("http://www.example.com/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return domain when url contains port", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return default path when no path provided", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/"); - }); - - it("should return default path when provided", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080/"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/"); - }); - - it("should return undefined when does not match", function () { - const d = URLDecomposer.fromUrl("https:///abc/test"); - Assert.equal(d, undefined); - }); - - it("should return undefined when does not match", function () { - const d = URLDecomposer.fromUrl("https:///abc/test"); - Assert.equal(d, undefined); - }); - }); -}); \ No newline at end of file diff --git a/themes/matrix/server/src/lib/utils/URLDecomposer.ts b/themes/matrix/server/src/lib/utils/URLDecomposer.ts deleted file mode 100644 index 9bdf2e9d2..000000000 --- a/themes/matrix/server/src/lib/utils/URLDecomposer.ts +++ /dev/null @@ -1,15 +0,0 @@ -export class URLDecomposer { - static fromUrl(url: string): {domain: string, path: string} { - if (!url) return; - const match = url.match(/https?:\/\/([a-z0-9_.-]+)(:[0-9]+)?(.*)/); - - if (!match) return; - - if (match[1] && !match[3]) { - return {domain: match[1], path: "/"}; - } else if (match[1] && match[3]) { - return {domain: match[1], path: match[3]}; - } - return; - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/web_server/Configurator.ts b/themes/matrix/server/src/lib/web_server/Configurator.ts deleted file mode 100644 index 6e404874a..000000000 --- a/themes/matrix/server/src/lib/web_server/Configurator.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Configuration } from "../configuration/schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; -import { SessionConfigurationBuilder } from - "../configuration/SessionConfigurationBuilder"; -import Path = require("path"); -import Express = require("express"); -import * as BodyParser from "body-parser"; -import { RestApi } from "./RestApi"; -import { WithHeadersLogged } from "./middlewares/WithHeadersLogged"; -import { ServerVariables } from "../ServerVariables"; -import Helmet = require("helmet"); - -const addRequestId = require("express-request-id")(); - -// Constants -const TRUST_PROXY = "trust proxy"; -const X_POWERED_BY = "x-powered-by"; -const VIEWS = "views"; -const VIEW_ENGINE = "view engine"; -const PUG = "pug"; - -export class Configurator { - static configure(config: Configuration, - app: Express.Application, - vars: ServerVariables, - deps: GlobalDependencies): void { - const viewsDirectory = Path.resolve(__dirname, "../../views"); - const publicHtmlDirectory = Path.resolve(__dirname, "../../public_html"); - - const expressSessionOptions = SessionConfigurationBuilder.build(config, deps); - - app.use(Express.static(publicHtmlDirectory)); - app.use(BodyParser.urlencoded({ extended: false })); - app.use(BodyParser.json()); - app.use(deps.session(expressSessionOptions)); - app.use(addRequestId); - app.use(WithHeadersLogged.middleware(vars.logger)); - app.disable(X_POWERED_BY); - app.enable(TRUST_PROXY); - app.use(Helmet()); - - app.set(VIEWS, viewsDirectory); - app.set(VIEW_ENGINE, PUG); - - RestApi.setup(app, vars); - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/web_server/RestApi.ts b/themes/matrix/server/src/lib/web_server/RestApi.ts deleted file mode 100644 index 9144a15b9..000000000 --- a/themes/matrix/server/src/lib/web_server/RestApi.ts +++ /dev/null @@ -1,125 +0,0 @@ -import Express = require("express"); - -import FirstFactorGet = require("../routes/firstfactor/get"); -import SecondFactorGet = require("../routes/secondfactor/get"); - -import FirstFactorPost = require("../routes/firstfactor/post"); -import LogoutGet = require("../routes/logout/get"); -import VerifyGet = require("../routes/verify/get"); -import TOTPSignGet = require("../routes/secondfactor/totp/sign/post"); - -import IdentityCheckMiddleware = require("../IdentityCheckMiddleware"); - -import TOTPRegistrationIdentityHandler from "../routes/secondfactor/totp/identity/RegistrationHandler"; -import U2FRegistrationIdentityHandler from "../routes/secondfactor/u2f/identity/RegistrationHandler"; -import ResetPasswordIdentityHandler from "../routes/password-reset/identity/PasswordResetHandler"; - -import U2FSignPost = require("../routes/secondfactor/u2f/sign/post"); -import U2FSignRequestGet = require("../routes/secondfactor/u2f/sign_request/get"); - -import U2FRegisterPost = require("../routes/secondfactor/u2f/register/post"); -import U2FRegisterRequestGet = require("../routes/secondfactor/u2f/register_request/get"); - -import ResetPasswordFormPost = require("../routes/password-reset/form/post"); -import ResetPasswordRequestPost = require("../routes/password-reset/request/get"); - -import Error401Get = require("../routes/error/401/get"); -import Error403Get = require("../routes/error/403/get"); -import Error404Get = require("../routes/error/404/get"); - -import LoggedIn = require("../routes/loggedin/get"); - -import { ServerVariables } from "../ServerVariables"; -import Endpoints = require("../../../../shared/api"); -import { RequireValidatedFirstFactor } from "./middlewares/RequireValidatedFirstFactor"; - -function setupTotp(app: Express.Application, vars: ServerVariables) { - app.post(Endpoints.SECOND_FACTOR_TOTP_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - TOTPSignGet.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - app.get(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - IdentityCheckMiddleware.register(app, - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET, - new TOTPRegistrationIdentityHandler(vars.logger, - vars.userDataStore, vars.totpHandler, vars.config.totp), - vars); -} - -function setupU2f(app: Express.Application, vars: ServerVariables) { - app.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FSignRequestGet.default(vars)); - - app.post(Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FSignPost.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FRegisterRequestGet.default(vars)); - - app.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FRegisterPost.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - app.get(Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - IdentityCheckMiddleware.register(app, - Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, - Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, - new U2FRegistrationIdentityHandler(vars.logger), vars); -} - -function setupResetPassword(app: Express.Application, vars: ServerVariables) { - IdentityCheckMiddleware.register(app, - Endpoints.RESET_PASSWORD_IDENTITY_START_GET, - Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET, - new ResetPasswordIdentityHandler(vars.logger, vars.usersDatabase), - vars); - - app.get(Endpoints.RESET_PASSWORD_REQUEST_GET, - ResetPasswordRequestPost.default); - app.post(Endpoints.RESET_PASSWORD_FORM_POST, - ResetPasswordFormPost.default(vars)); -} - -function setupErrors(app: Express.Application, vars: ServerVariables) { - app.get(Endpoints.ERROR_401_GET, Error401Get.default(vars)); - app.get(Endpoints.ERROR_403_GET, Error403Get.default(vars)); - app.get(Endpoints.ERROR_404_GET, Error404Get.default); -} - -export class RestApi { - static setup(app: Express.Application, vars: ServerVariables): void { - app.get(Endpoints.FIRST_FACTOR_GET, FirstFactorGet.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - SecondFactorGet.default(vars)); - - app.get(Endpoints.LOGOUT_GET, LogoutGet.default(vars)); - - app.get(Endpoints.VERIFY_GET, VerifyGet.default(vars)); - app.post(Endpoints.FIRST_FACTOR_POST, FirstFactorPost.default(vars)); - - setupTotp(app, vars); - setupU2f(app, vars); - setupResetPassword(app, vars); - setupErrors(app, vars); - - app.get(Endpoints.LOGGED_IN, - RequireValidatedFirstFactor.middleware(vars.logger), - LoggedIn.default(vars)); - } -} diff --git a/themes/matrix/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts b/themes/matrix/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts deleted file mode 100644 index ecfd75765..000000000 --- a/themes/matrix/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts +++ /dev/null @@ -1,27 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import ErrorReplies = require("../../ErrorReplies"); -import { IRequestLogger } from "../../logging/IRequestLogger"; -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Exceptions = require("../../Exceptions"); -import { Level } from "../../authentication/Level"; - -export class RequireValidatedFirstFactor { - static middleware(logger: IRequestLogger) { - return function (req: Express.Request, res: Express.Response, - next: Express.NextFunction): BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, logger); - if (!authSession.userid || authSession.authentication_level < Level.ONE_FACTOR) - return reject( - new Exceptions.FirstFactorValidationError( - "First factor has not been validated yet.")); - - next(); - resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, logger)); - }; - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/lib/web_server/middlewares/WithHeadersLogged.ts b/themes/matrix/server/src/lib/web_server/middlewares/WithHeadersLogged.ts deleted file mode 100644 index 139db1141..000000000 --- a/themes/matrix/server/src/lib/web_server/middlewares/WithHeadersLogged.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Express = require("express"); -import { IRequestLogger } from "../../logging/IRequestLogger"; - -export class WithHeadersLogged { - static middleware(logger: IRequestLogger) { - return function (req: Express.Request, res: Express.Response, - next: Express.NextFunction): void { - logger.debug(req, "Headers = %s", JSON.stringify(req.headers)); - next(); - }; - } -} \ No newline at end of file diff --git a/themes/matrix/server/src/resources/email-template.ejs b/themes/matrix/server/src/resources/email-template.ejs old mode 100644 new mode 100755 diff --git a/themes/matrix/server/src/views/already-logged-in.pug b/themes/matrix/server/src/views/already-logged-in.pug old mode 100644 new mode 100755 diff --git a/themes/matrix/server/src/views/errors/.directory b/themes/matrix/server/src/views/errors/.directory old mode 100644 new mode 100755 diff --git a/themes/matrix/server/src/views/errors/401.pug b/themes/matrix/server/src/views/errors/401.pug old mode 100644 new mode 100755 diff --git a/themes/matrix/server/src/views/errors/403.pug b/themes/matrix/server/src/views/errors/403.pug old mode 100644 new mode 100755 diff --git a/themes/matrix/server/src/views/errors/404.pug b/themes/matrix/server/src/views/errors/404.pug old mode 100644 new mode 100755 diff --git a/themes/matrix/server/src/views/firstfactor.pug b/themes/matrix/server/src/views/firstfactor.pug old mode 100644 new mode 100755 diff --git a/themes/matrix/server/src/views/layout/layout.pug b/themes/matrix/server/src/views/layout/layout.pug old mode 100644 new mode 100755 diff --git a/themes/matrix/server/src/views/need-identity-validation.pug b/themes/matrix/server/src/views/need-identity-validation.pug old mode 100644 new mode 100755 diff --git a/themes/matrix/server/src/views/password-reset-form.pug b/themes/matrix/server/src/views/password-reset-form.pug old mode 100644 new mode 100755 diff --git a/themes/matrix/server/src/views/password-reset-request.pug b/themes/matrix/server/src/views/password-reset-request.pug old mode 100644 new mode 100755 diff --git a/themes/matrix/server/src/views/secondfactor.pug b/themes/matrix/server/src/views/secondfactor.pug old mode 100644 new mode 100755 diff --git a/themes/matrix/server/src/views/totp-register.pug b/themes/matrix/server/src/views/totp-register.pug old mode 100644 new mode 100755 diff --git a/themes/matrix/server/src/views/u2f-register.pug b/themes/matrix/server/src/views/u2f-register.pug old mode 100644 new mode 100755 diff --git a/themes/matrix/server/test/requests.ts b/themes/matrix/server/test/requests.ts deleted file mode 100644 index 93fa0de47..000000000 --- a/themes/matrix/server/test/requests.ts +++ /dev/null @@ -1,94 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import request = require("request"); -import assert = require("assert"); -import express = require("express"); -import nodemailer = require("nodemailer"); -import Endpoints = require("../../shared/api"); - -declare module "request" { - export interface RequestAPI { - getAsync(uri: string, options?: RequiredUriUrl): BluebirdPromise; - getAsync(uri: string): BluebirdPromise; - getAsync(options: RequiredUriUrl & CoreOptions): BluebirdPromise; - - postAsync(uri: string, options?: CoreOptions): BluebirdPromise; - postAsync(uri: string): BluebirdPromise; - postAsync(options: RequiredUriUrl & CoreOptions): BluebirdPromise; - } -} - -const requestAsync: typeof request = BluebirdPromise.promisifyAll(request) as typeof request; - -export = function (port: number) { - const PORT = port; - const BASE_URL = "http://localhost:" + PORT; - - function execute_totp(jar: request.CookieJar, token: string) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_TOTP_POST, - jar: jar, - form: { - token: token - } - }); - } - - function execute_u2f_authentication(jar: request.CookieJar) { - return requestAsync.getAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, - jar: jar - }) - .then(function (res: request.RequestResponse) { - assert.equal(res.statusCode, 200); - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - jar: jar, - form: { - } - }); - }); - } - - function execute_verification(jar: request.CookieJar) { - return requestAsync.getAsync({ url: BASE_URL + Endpoints.VERIFY_GET, jar: jar }); - } - - function execute_login(jar: request.CookieJar) { - return requestAsync.getAsync({ url: BASE_URL + Endpoints.FIRST_FACTOR_GET, jar: jar }); - } - - function execute_first_factor(jar: request.CookieJar) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.FIRST_FACTOR_POST, - jar: jar, - form: { - username: "test_ok", - password: "password" - } - }); - } - - function execute_failing_first_factor(jar: request.CookieJar) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.FIRST_FACTOR_POST, - jar: jar, - form: { - username: "test_nok", - password: "password" - } - }); - } - - return { - login: execute_login, - verify: execute_verification, - u2f_authentication: execute_u2f_authentication, - first_factor: execute_first_factor, - failing_first_factor: execute_failing_first_factor, - totp: execute_totp, - }; -}; - diff --git a/themes/matrix/server/tsconfig.json b/themes/matrix/server/tsconfig.json deleted file mode 100644 index ebe98c5ed..000000000 --- a/themes/matrix/server/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "moduleResolution": "node", - "noImplicitAny": true, - "sourceMap": true, - "removeComments": true, - "outDir": "../dist", - "baseUrl": ".", - "paths": { - "*": [ - "./types/*", - "../shared/types/*" - ] - } - }, - "exclude": [ - "src/**/*.spec.ts" - ] -} diff --git a/themes/matrix/server/tslint.json b/themes/matrix/server/tslint.json deleted file mode 100644 index c2c1b7501..000000000 --- a/themes/matrix/server/tslint.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "no-var-keyword": true, - "quotemark": [ - true, - "double", - "avoid-escape" - ], - "semicolon": [ - true, - "always", - "ignore-bound-class-methods" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-module", - "check-separator", - "check-type" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "onespace", - "index-signature": "onespace", - "parameter": "onespace", - "property-declaration": "onespace", - "variable-declaration": "onespace" - } - ], - "no-internal-module": true, - "no-trailing-whitespace": true, - "no-null-keyword": true, - "prefer-const": true, - "jsdoc-format": true - } -} diff --git a/themes/matrix/server/types/.directory b/themes/matrix/server/types/.directory deleted file mode 100644 index 1e65000ea..000000000 --- a/themes/matrix/server/types/.directory +++ /dev/null @@ -1,4 +0,0 @@ -[Dolphin] -Timestamp=2018,12,17,20,58,27 -Version=3 -ViewMode=1 diff --git a/themes/matrix/server/types/AuthenticationSession.ts b/themes/matrix/server/types/AuthenticationSession.ts deleted file mode 100644 index bbed0e715..000000000 --- a/themes/matrix/server/types/AuthenticationSession.ts +++ /dev/null @@ -1,18 +0,0 @@ -import U2f = require("u2f"); -import { Level } from "../src/lib/authentication/Level"; - -export interface AuthenticationSession { - userid: string; - authentication_level: Level; - keep_me_logged_in: boolean; - last_activity_datetime: number; - identity_check?: { - challenge: string; - userid: string; - }; - register_request?: U2f.Request; - sign_request?: U2f.Request; - email: string; - groups: string[]; - redirect?: string; -} \ No newline at end of file diff --git a/themes/matrix/server/types/Dependencies.ts b/themes/matrix/server/types/Dependencies.ts deleted file mode 100644 index f20404dbc..000000000 --- a/themes/matrix/server/types/Dependencies.ts +++ /dev/null @@ -1,29 +0,0 @@ -import winston = require("winston"); -import speakeasy = require("speakeasy"); -import nodemailer = require("nodemailer"); -import session = require("express-session"); -import nedb = require("nedb"); -import ldapjs = require("ldapjs"); -import u2f = require("u2f"); -import RedisSession = require("connect-redis"); -import Redis = require("redis"); - -export type Speakeasy = typeof speakeasy; -export type Winston = typeof winston; -export type Session = typeof session; -export type Nedb = typeof nedb; -export type Ldapjs = typeof ldapjs; -export type U2f = typeof u2f; -export type ConnectRedis = typeof RedisSession; -export type Redis = typeof Redis; - -export interface GlobalDependencies { - u2f: U2f; - ldapjs: Ldapjs; - session: Session; - Redis: Redis; - ConnectRedis: ConnectRedis; - winston: Winston; - speakeasy: Speakeasy; - nedb: Nedb; -} \ No newline at end of file diff --git a/themes/matrix/server/types/Identity.ts b/themes/matrix/server/types/Identity.ts deleted file mode 100644 index e985984e2..000000000 --- a/themes/matrix/server/types/Identity.ts +++ /dev/null @@ -1,6 +0,0 @@ - - -export interface Identity { - userid: string; - email: string; -} \ No newline at end of file diff --git a/themes/matrix/server/types/TOTPSecret.ts b/themes/matrix/server/types/TOTPSecret.ts deleted file mode 100644 index d6775f2f0..000000000 --- a/themes/matrix/server/types/TOTPSecret.ts +++ /dev/null @@ -1,11 +0,0 @@ - -export interface TOTPSecret { - ascii: string; - hex: string; - base32: string; - qr_code_ascii: string; - qr_code_hex: string; - qr_code_base32: string; - google_auth_qr: string; - otpauth_url: string; - } \ No newline at end of file diff --git a/themes/matrix/server/types/U2FRegistration.ts b/themes/matrix/server/types/U2FRegistration.ts deleted file mode 100644 index b6080af07..000000000 --- a/themes/matrix/server/types/U2FRegistration.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface U2FRegistration { - keyHandle: string; - publicKey: string; -} \ No newline at end of file diff --git a/themes/matrix/server/types/dovehash.d.ts b/themes/matrix/server/types/dovehash.d.ts deleted file mode 100644 index c354609c0..000000000 --- a/themes/matrix/server/types/dovehash.d.ts +++ /dev/null @@ -1,4 +0,0 @@ - -declare module "dovehash" { - function encode(algo: string, text: string): string; -} \ No newline at end of file diff --git a/themes/matrix/server/types/speakeasy.d.ts b/themes/matrix/server/types/speakeasy.d.ts deleted file mode 100644 index 6ea06948b..000000000 --- a/themes/matrix/server/types/speakeasy.d.ts +++ /dev/null @@ -1,96 +0,0 @@ -declare module "speakeasy" { - export = speakeasy - - interface SharedOptions { - encoding?: string - algorithm?: string - } - - interface DigestOptions extends SharedOptions { - secret: string - counter: number - } - - interface HOTPOptions extends SharedOptions { - secret: string - counter: number - digest?: Buffer - digits?: number - } - - interface HOTPVerifyOptions extends SharedOptions { - secret: string - token: string - counter: number - digits?: number - window?: number - } - - interface TOTPOptions extends SharedOptions { - secret: string - time?: number - step?: number - epoch?: number - counter?: number - digits?: number - } - - interface TOTPVerifyOptions extends SharedOptions { - secret: string - token: string - time?: number - step?: number - epoch?: number - counter?: number - digits?: number - window?: number - } - - interface GenerateSecretOptions { - length?: number - symbols?: boolean - otpauth_url?: boolean - name?: string - issuer?: string - } - - interface GeneratedSecret { - ascii: string - hex: string - base32: string - qr_code_ascii: string - qr_code_hex: string - qr_code_base32: string - google_auth_qr: string - otpauth_url: string - } - - interface OTPAuthURLOptions extends SharedOptions { - secret: string - label: string - type?: string - counter?: number - issuer?: string - digits?: number - period?: number - } - - interface Speakeasy { - digest: (options: DigestOptions) => Buffer - hotp: { - (options: HOTPOptions): string, - verifyDelta: (options: HOTPVerifyOptions) => boolean, - verify: (options: HOTPVerifyOptions) => boolean, - } - totp: { - (options: TOTPOptions): string - verifyDelta: (options: TOTPVerifyOptions) => boolean, - verify: (options: TOTPVerifyOptions) => boolean, - } - generateSecret: (options?: GenerateSecretOptions) => GeneratedSecret - generateSecretASCII: (length?: number, symbols?: boolean) => string - otpauthURL: (options: OTPAuthURLOptions) => string - } - - const speakeasy: Speakeasy -} \ No newline at end of file diff --git a/themes/squares/client/src/css/.directory b/themes/squares/client/src/css/.directory old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/css/00-bootstrap.min.css b/themes/squares/client/src/css/00-bootstrap.min.css old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/css/01-main.css b/themes/squares/client/src/css/01-main.css old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/css/02-login.css b/themes/squares/client/src/css/02-login.css old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/css/03-errors.css b/themes/squares/client/src/css/03-errors.css old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/css/03-password-reset-form.css b/themes/squares/client/src/css/03-password-reset-form.css old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/css/03-password-reset-request.css b/themes/squares/client/src/css/03-password-reset-request.css old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/css/03-totp-register.css b/themes/squares/client/src/css/03-totp-register.css old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/css/03-u2f-register.css b/themes/squares/client/src/css/03-u2f-register.css old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/LargeTriangles.svg b/themes/squares/client/src/img/LargeTriangles.svg old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/RandomizedPattern.svg b/themes/squares/client/src/img/RandomizedPattern.svg old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/background.jpg b/themes/squares/client/src/img/background.jpg old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/background.svg b/themes/squares/client/src/img/background.svg old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/icon.png b/themes/squares/client/src/img/icon.png old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/mail.png b/themes/squares/client/src/img/mail.png old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/matrix_circle_128x128.png b/themes/squares/client/src/img/matrix_circle_128x128.png old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/notifications/.directory b/themes/squares/client/src/img/notifications/.directory old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/notifications/error.png b/themes/squares/client/src/img/notifications/error.png old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/notifications/info.png b/themes/squares/client/src/img/notifications/info.png old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/notifications/success.png b/themes/squares/client/src/img/notifications/success.png old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/notifications/warning.png b/themes/squares/client/src/img/notifications/warning.png old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/padlock.png b/themes/squares/client/src/img/padlock.png old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/password_white.png b/themes/squares/client/src/img/password_white.png old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/pendrive.png b/themes/squares/client/src/img/pendrive.png old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/sharingan.png b/themes/squares/client/src/img/sharingan.png old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/stores/.directory b/themes/squares/client/src/img/stores/.directory old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/stores/applestore-badge.svg b/themes/squares/client/src/img/stores/applestore-badge.svg old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/stores/googleplay-badge.svg b/themes/squares/client/src/img/stores/googleplay-badge.svg old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/success.png b/themes/squares/client/src/img/success.png old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/user.png b/themes/squares/client/src/img/user.png old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/img/warning.png b/themes/squares/client/src/img/warning.png old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/index.ts b/themes/squares/client/src/index.ts deleted file mode 100644 index 802004a8f..000000000 --- a/themes/squares/client/src/index.ts +++ /dev/null @@ -1,34 +0,0 @@ - -import FirstFactorValidator = require("./lib/firstfactor/FirstFactorValidator"); - -import FirstFactor from "./lib/firstfactor/index"; -import SecondFactor from "./lib/secondfactor/index"; -import TOTPRegister from "./lib/totp-register/totp-register"; -import U2fRegister from "./lib/u2f-register/u2f-register"; -import ResetPasswordRequest from "./lib/reset-password/reset-password-request"; -import ResetPasswordForm from "./lib/reset-password/reset-password-form"; -import jslogger = require("js-logger"); -import jQuery = require("jquery"); -import Endpoints = require("../../shared/api"); - -jslogger.useDefaults(); -jslogger.setLevel(jslogger.INFO); - -(function () { - (window).jQuery = jQuery; - require("bootstrap"); - - jQuery('[data-toggle="tooltip"]').tooltip(); - if (window.location.pathname == Endpoints.FIRST_FACTOR_GET) - FirstFactor(window, jQuery, FirstFactorValidator, jslogger); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_GET) - SecondFactor(window, jQuery, (global as any).u2f); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET) - TOTPRegister(window, jQuery); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET) - U2fRegister(window, jQuery, (global as any).u2f); - else if (window.location.pathname == Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET) - ResetPasswordForm(window, jQuery); - else if (window.location.pathname == Endpoints.RESET_PASSWORD_REQUEST_GET) - ResetPasswordRequest(window, jQuery); -})(); diff --git a/themes/squares/client/src/lib/GetPromised.ts b/themes/squares/client/src/lib/GetPromised.ts deleted file mode 100644 index 779139654..000000000 --- a/themes/squares/client/src/lib/GetPromised.ts +++ /dev/null @@ -1,14 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -export default function ($: JQueryStatic, url: string, data: Object, fn: any, - dataType: string): BluebirdPromise { - return new BluebirdPromise((resolve, reject) => { - $.get(url, {}, undefined, dataType) - .done((data: any) => { - resolve(data); - }) - .fail((xhr: JQueryXHR, textStatus: string) => { - reject(textStatus); - }); - }); -} \ No newline at end of file diff --git a/themes/squares/client/src/lib/INotifier.ts b/themes/squares/client/src/lib/INotifier.ts deleted file mode 100644 index df947538f..000000000 --- a/themes/squares/client/src/lib/INotifier.ts +++ /dev/null @@ -1,14 +0,0 @@ - -declare type Handler = () => void; - -export interface Handlers { - onFadedIn: Handler; - onFadedOut: Handler; -} - -export interface INotifier { - success(msg: string, handlers?: Handlers): void; - error(msg: string, handlers?: Handlers): void; - warning(msg: string, handlers?: Handlers): void; - info(msg: string, handlers?: Handlers): void; -} \ No newline at end of file diff --git a/themes/squares/client/src/lib/Notifier.ts b/themes/squares/client/src/lib/Notifier.ts deleted file mode 100644 index c0252b9b9..000000000 --- a/themes/squares/client/src/lib/Notifier.ts +++ /dev/null @@ -1,83 +0,0 @@ - - -import util = require("util"); -import { INotifier, Handlers } from "./INotifier"; - -class NotificationEvent { - private element: JQuery; - private message: string; - private statusType: string; - private timeoutId: any; - - constructor(element: JQuery, msg: string, statusType: string) { - this.message = msg; - this.statusType = statusType; - this.element = element; - } - - private clearNotification() { - this.element.removeClass(this.statusType); - this.element.html(""); - } - - start(handlers?: Handlers) { - const that = this; - const FADE_TIME = 500; - const html = util.format('status %s\ - %s', this.statusType, this.statusType, this.message); - this.element.html(html); - this.element.addClass(this.statusType); - this.element.fadeIn(FADE_TIME, function () { - if (handlers) - handlers.onFadedIn(); - }); - - this.timeoutId = setTimeout(function () { - that.element.fadeOut(FADE_TIME, function () { - that.clearNotification(); - if (handlers) - handlers.onFadedOut(); - }); - }, 4000); - } - - interrupt() { - this.clearNotification(); - this.element.hide(); - clearTimeout(this.timeoutId); - } -} - -export class Notifier implements INotifier { - private element: JQuery; - private onGoingEvent: NotificationEvent; - - constructor(selector: string, $: JQueryStatic) { - this.element = $(selector); - this.onGoingEvent = undefined; - } - - private displayAndFadeout(msg: string, statusType: string, handlers?: Handlers): void { - if (this.onGoingEvent) - this.onGoingEvent.interrupt(); - - this.onGoingEvent = new NotificationEvent(this.element, msg, statusType); - this.onGoingEvent.start(handlers); - } - - success(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "success", handlers); - } - - error(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "error", handlers); - } - - warning(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "warning", handlers); - } - - info(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "info", handlers); - } -} \ No newline at end of file diff --git a/themes/squares/client/src/lib/QueryParametersRetriever.ts b/themes/squares/client/src/lib/QueryParametersRetriever.ts deleted file mode 100644 index a529adb6e..000000000 --- a/themes/squares/client/src/lib/QueryParametersRetriever.ts +++ /dev/null @@ -1,12 +0,0 @@ - -export class QueryParametersRetriever { - static get(name: string, url?: string): string { - if (!url) url = window.location.href; - name = name.replace(/[\[\]]/g, "\\$&"); - const regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), - results = regex.exec(url); - if (!results) return undefined; - if (!results[2]) return ""; - return decodeURIComponent(results[2].replace(/\+/g, " ")); - } -} \ No newline at end of file diff --git a/themes/squares/client/src/lib/SafeRedirect.ts b/themes/squares/client/src/lib/SafeRedirect.ts deleted file mode 100644 index 7e7684b8a..000000000 --- a/themes/squares/client/src/lib/SafeRedirect.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BelongToDomain } from "../../../shared/BelongToDomain"; - -export function SafeRedirect(url: string, cb: () => void): void { - const domain = window.location.hostname.split(".").slice(-2).join("."); - if (url.startsWith("/") || BelongToDomain(url, domain)) { - window.location.href = url; - return; - } - cb(); -} \ No newline at end of file diff --git a/themes/squares/client/src/lib/firstfactor/FirstFactorValidator.ts b/themes/squares/client/src/lib/firstfactor/FirstFactorValidator.ts deleted file mode 100644 index eaa496fdd..000000000 --- a/themes/squares/client/src/lib/firstfactor/FirstFactorValidator.ts +++ /dev/null @@ -1,46 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import Constants = require("../../../../shared/constants"); -import Util = require("util"); -import UserMessages = require("../../../../shared/UserMessages"); - -export function validate(username: string, password: string, - keepMeLoggedIn: boolean, redirectUrl: string, $: JQueryStatic) - : BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - let url: string; - if (redirectUrl != undefined) { - const redirectParam = Util.format("%s=%s", Constants.REDIRECT_QUERY_PARAM, redirectUrl); - url = Util.format("%s?%s", Endpoints.FIRST_FACTOR_POST, redirectParam); - } - else { - url = Util.format("%s", Endpoints.FIRST_FACTOR_POST); - } - - const data: any = { - username: username, - password: password, - }; - - if (keepMeLoggedIn) { - data.keepMeLoggedIn = "true"; - } - - $.ajax({ - method: "POST", - url: url, - data: data - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(body.redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(UserMessages.AUTHENTICATION_FAILED)); - }); - }); -} diff --git a/themes/squares/client/src/lib/firstfactor/UISelectors.ts b/themes/squares/client/src/lib/firstfactor/UISelectors.ts deleted file mode 100644 index 0e971b3c3..000000000 --- a/themes/squares/client/src/lib/firstfactor/UISelectors.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export const USERNAME_FIELD_ID = "#username"; -export const PASSWORD_FIELD_ID = "#password"; -export const SIGN_IN_BUTTON_ID = "#signin"; -export const KEEP_ME_LOGGED_IN_ID = "#keep_me_logged_in"; diff --git a/themes/squares/client/src/lib/firstfactor/index.ts b/themes/squares/client/src/lib/firstfactor/index.ts deleted file mode 100644 index 24affee2d..000000000 --- a/themes/squares/client/src/lib/firstfactor/index.ts +++ /dev/null @@ -1,49 +0,0 @@ -import FirstFactorValidator = require("./FirstFactorValidator"); -import JSLogger = require("js-logger"); -import UISelectors = require("./UISelectors"); -import { Notifier } from "../Notifier"; -import { QueryParametersRetriever } from "../QueryParametersRetriever"; -import Constants = require("../../../../shared/constants"); -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic, - firstFactorValidator: typeof FirstFactorValidator, jslogger: typeof JSLogger) { - - const notifier = new Notifier(".notification", $); - - function onFormSubmitted() { - const username: string = $(UISelectors.USERNAME_FIELD_ID).val() as string; - const password: string = $(UISelectors.PASSWORD_FIELD_ID).val() as string; - const keepMeLoggedIn: boolean = $(UISelectors.KEEP_ME_LOGGED_IN_ID).is(":checked"); - - $("form").css("opacity", 0.5); - $("input,button").attr("disabled", "true"); - $(UISelectors.SIGN_IN_BUTTON_ID).text("Please wait..."); - - const redirectUrl = QueryParametersRetriever.get(Constants.REDIRECT_QUERY_PARAM); - firstFactorValidator.validate(username, password, keepMeLoggedIn, redirectUrl, $) - .then(onFirstFactorSuccess, onFirstFactorFailure); - return false; - } - - function onFirstFactorSuccess(redirectUrl: string) { - SafeRedirect(redirectUrl, () => { - notifier.error("Cannot redirect to an external domain."); - }); - } - - function onFirstFactorFailure(err: Error) { - $("input,button").removeAttr("disabled"); - $("form").css("opacity", 1); - notifier.error(UserMessages.AUTHENTICATION_FAILED); - $(UISelectors.PASSWORD_FIELD_ID).select(); - $(UISelectors.SIGN_IN_BUTTON_ID).text("Sign in"); - } - - $(window.document).ready(function () { - $("form").on("submit", onFormSubmitted); - }); -} - diff --git a/themes/squares/client/src/lib/reset-password/constants.ts b/themes/squares/client/src/lib/reset-password/constants.ts deleted file mode 100644 index d48d4e67d..000000000 --- a/themes/squares/client/src/lib/reset-password/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const FORM_SELECTOR = ".form-signin"; \ No newline at end of file diff --git a/themes/squares/client/src/lib/reset-password/reset-password-form.ts b/themes/squares/client/src/lib/reset-password/reset-password-form.ts deleted file mode 100644 index b94279cde..000000000 --- a/themes/squares/client/src/lib/reset-password/reset-password-form.ts +++ /dev/null @@ -1,57 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); - -import Constants = require("./constants"); -import { Notifier } from "../Notifier"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function modifyPassword(newPassword: string) { - return new BluebirdPromise(function (resolve, reject) { - $.post(Endpoints.RESET_PASSWORD_FORM_POST, { - password: newPassword, - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(body); - }) - .fail(function (xhr, status) { - reject(status); - }); - }); - } - - function onFormSubmitted() { - const password1 = $("#password1").val() as string; - const password2 = $("#password2").val() as string; - - if (!password1 || !password2) { - notifier.warning(UserMessages.MISSING_PASSWORD); - return false; - } - - if (password1 != password2) { - notifier.warning(UserMessages.DIFFERENT_PASSWORDS); - return false; - } - - modifyPassword(password1) - .then(function () { - window.location.href = Endpoints.FIRST_FACTOR_GET; - }) - .error(function () { - notifier.error(UserMessages.RESET_PASSWORD_FAILED); - }); - return false; - } - - $(document).ready(function () { - $(Constants.FORM_SELECTOR).on("submit", onFormSubmitted); - }); -} diff --git a/themes/squares/client/src/lib/reset-password/reset-password-request.ts b/themes/squares/client/src/lib/reset-password/reset-password-request.ts deleted file mode 100644 index 846226d75..000000000 --- a/themes/squares/client/src/lib/reset-password/reset-password-request.ts +++ /dev/null @@ -1,56 +0,0 @@ - -import BluebirdPromise = require("bluebird"); - -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import Constants = require("./constants"); -import jslogger = require("js-logger"); -import { Notifier } from "../Notifier"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function requestPasswordReset(username: string) { - return new BluebirdPromise(function (resolve, reject) { - $.get(Endpoints.RESET_PASSWORD_IDENTITY_START_GET, { - userid: username, - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); - } - - function onFormSubmitted() { - const username = $("#username").val() as string; - - if (!username) { - notifier.warning(UserMessages.MISSING_USERNAME); - return; - } - - requestPasswordReset(username) - .then(function () { - notifier.success(UserMessages.MAIL_SENT); - setTimeout(function () { - window.location.replace(Endpoints.FIRST_FACTOR_GET); - }, 1000); - }) - .error(function () { - notifier.error(UserMessages.MAIL_NOT_SENT); - }); - return false; - } - - $(document).ready(function () { - $(Constants.FORM_SELECTOR).on("submit", onFormSubmitted); - }); -} - diff --git a/themes/squares/client/src/lib/secondfactor/TOTPValidator.ts b/themes/squares/client/src/lib/secondfactor/TOTPValidator.ts deleted file mode 100644 index 5394139a6..000000000 --- a/themes/squares/client/src/lib/secondfactor/TOTPValidator.ts +++ /dev/null @@ -1,28 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; - -export function validate(token: string, $: JQueryStatic): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.ajax({ - url: Endpoints.SECOND_FACTOR_TOTP_POST, - data: { - token: token, - }, - method: "POST", - dataType: "json" - } as JQueryAjaxSettings) - .done(function (body: RedirectionMessage | ErrorMessage) { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); -} \ No newline at end of file diff --git a/themes/squares/client/src/lib/secondfactor/U2FValidator.ts b/themes/squares/client/src/lib/secondfactor/U2FValidator.ts deleted file mode 100644 index 5812922f7..000000000 --- a/themes/squares/client/src/lib/secondfactor/U2FValidator.ts +++ /dev/null @@ -1,42 +0,0 @@ -import U2f = require("u2f"); -import U2fApi from "u2f-api"; -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { INotifier } from "../INotifier"; -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; -import GetPromised from "../GetPromised"; - -function finishU2fAuthentication(responseData: U2fApi.SignResponse, - $: JQueryStatic): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.ajax({ - url: Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - data: responseData, - method: "POST", - dataType: "json" - } as JQueryAjaxSettings) - .done(function (body: RedirectionMessage | ErrorMessage) { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); -} - -export function validate($: JQueryStatic): BluebirdPromise { - return GetPromised($, Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, {}, - undefined, "json") - .then(function (signRequest: U2f.Request) { - return U2fApi.sign(signRequest, 60); - }) - .then(function (signResponse: U2fApi.SignResponse) { - return finishU2fAuthentication(signResponse, $); - }); -} diff --git a/themes/squares/client/src/lib/secondfactor/constants.ts b/themes/squares/client/src/lib/secondfactor/constants.ts deleted file mode 100644 index 50bba7571..000000000 --- a/themes/squares/client/src/lib/secondfactor/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ - -export const TOTP_FORM_SELECTOR = ".form-signin.totp"; -export const TOTP_TOKEN_SELECTOR = ".form-signin #token"; diff --git a/themes/squares/client/src/lib/secondfactor/index.ts b/themes/squares/client/src/lib/secondfactor/index.ts deleted file mode 100644 index 279723dce..000000000 --- a/themes/squares/client/src/lib/secondfactor/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import TOTPValidator = require("./TOTPValidator"); -import U2FValidator = require("./U2FValidator"); -import ClientConstants = require("./constants"); -import { Notifier } from "../Notifier"; -import { QueryParametersRetriever } from "../QueryParametersRetriever"; -import UserMessages = require("../../../../shared/UserMessages"); -import SharedConstants = require("../../../../shared/constants"); -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function onAuthenticationSuccess(serverRedirectUrl: string) { - const queryRedirectUrl = QueryParametersRetriever.get(SharedConstants.REDIRECT_QUERY_PARAM); - if (queryRedirectUrl) { - SafeRedirect(queryRedirectUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - } else if (serverRedirectUrl) { - SafeRedirect(serverRedirectUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - } else { - notifier.success(UserMessages.AUTHENTICATION_SUCCEEDED); - } - } - - function onSecondFactorTotpSuccess(redirectUrl: string) { - onAuthenticationSuccess(redirectUrl); - } - - function onSecondFactorTotpFailure(err: Error) { - notifier.error(UserMessages.AUTHENTICATION_TOTP_FAILED); - } - - function onU2fAuthenticationSuccess(redirectUrl: string) { - onAuthenticationSuccess(redirectUrl); - } - - function onU2fAuthenticationFailure() { - // TODO(clems4ever): we should not display this error message until a device - // is registered. - // notifier.error(UserMessages.AUTHENTICATION_U2F_FAILED); - } - - function onTOTPFormSubmitted(): boolean { - const token = $(ClientConstants.TOTP_TOKEN_SELECTOR).val() as string; - TOTPValidator.validate(token, $) - .then(onSecondFactorTotpSuccess) - .catch(onSecondFactorTotpFailure); - return false; - } - - $(window.document).ready(function () { - $(ClientConstants.TOTP_FORM_SELECTOR).on("submit", onTOTPFormSubmitted); - U2FValidator.validate($) - .then(onU2fAuthenticationSuccess, onU2fAuthenticationFailure); - }); -} \ No newline at end of file diff --git a/themes/squares/client/src/lib/totp-register/totp-register.ts b/themes/squares/client/src/lib/totp-register/totp-register.ts deleted file mode 100644 index 6a9aa7ee0..000000000 --- a/themes/squares/client/src/lib/totp-register/totp-register.ts +++ /dev/null @@ -1,11 +0,0 @@ - -import jslogger = require("js-logger"); -import UISelector = require("./ui-selector"); - -export default function(window: Window, $: JQueryStatic) { - jslogger.debug("Creating QRCode from OTPAuth url"); - const qrcode = $(UISelector.QRCODE_ID_SELECTOR); - const val = qrcode.text(); - qrcode.empty(); - new (window as any).QRCode(qrcode.get(0), val); -} diff --git a/themes/squares/client/src/lib/totp-register/ui-selector.ts b/themes/squares/client/src/lib/totp-register/ui-selector.ts deleted file mode 100644 index 9d43fabea..000000000 --- a/themes/squares/client/src/lib/totp-register/ui-selector.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const QRCODE_ID_SELECTOR = "#qrcode"; \ No newline at end of file diff --git a/themes/squares/client/src/lib/u2f-register/u2f-register.ts b/themes/squares/client/src/lib/u2f-register/u2f-register.ts deleted file mode 100644 index abf40ee05..000000000 --- a/themes/squares/client/src/lib/u2f-register/u2f-register.ts +++ /dev/null @@ -1,56 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import U2f = require("u2f"); -import * as U2fApi from "u2f-api"; -import { Notifier } from "../Notifier"; -import GetPromised from "../GetPromised"; -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function checkRegistration(regResponse: U2fApi.RegisterResponse): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, regResponse, undefined, "json") - .done((body: RedirectionMessage | ErrorMessage) => { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail((xhr, status) => { - reject(new Error("Failed to register device.")); - }); - }); - } - - function requestRegistration(): BluebirdPromise { - return GetPromised($, Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, {}, - undefined, "json") - .then((registrationRequest: U2f.Request) => { - return U2fApi.register(registrationRequest, [], 60); - }) - .then((res) => checkRegistration(res)); - } - - function onRegisterFailure(err: Error) { - notifier.error(UserMessages.REGISTRATION_U2F_FAILED); - } - - $(document).ready(function () { - requestRegistration() - .then((redirectionUrl: string) => { - SafeRedirect(redirectionUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - }) - .catch((err) => { - onRegisterFailure(err); - }); - }); -} diff --git a/themes/squares/client/src/thirdparties/qrcode.min.js b/themes/squares/client/src/thirdparties/qrcode.min.js old mode 100644 new mode 100755 diff --git a/themes/squares/client/src/thirdparties/u2f-api.js b/themes/squares/client/src/thirdparties/u2f-api.js old mode 100644 new mode 100755 diff --git a/themes/squares/client/test/Notifier.test.ts b/themes/squares/client/test/Notifier.test.ts deleted file mode 100644 index 70bfea146..000000000 --- a/themes/squares/client/test/Notifier.test.ts +++ /dev/null @@ -1,71 +0,0 @@ - -import Assert = require("assert"); -import Sinon = require("sinon"); -import JQueryMock = require("./mocks/jquery"); - -import { Notifier } from "../src/lib/Notifier"; - -describe("test notifier", function() { - const SELECTOR = "dummy-selector"; - const MESSAGE = "This is a message"; - let jqueryMock: { jquery: JQueryMock.JQueryMock, element: JQueryMock.JQueryElementsMock }; - let clock: any; - - beforeEach(function() { - jqueryMock = JQueryMock.JQueryMock(); - clock = Sinon.useFakeTimers(); - }); - - afterEach(function() { - clock.restore(); - }); - - function should_fade_in_and_out_on_notification(notificationType: string): void { - const delayReturn = { - fadeOut: Sinon.stub() - }; - - jqueryMock.element.fadeIn.yields(); - - function onFadedInCallback() { - Assert(jqueryMock.element.fadeIn.calledOnce); - Assert(jqueryMock.element.addClass.calledWith(notificationType)); - Assert(!jqueryMock.element.removeClass.calledWith(notificationType)); - clock.tick(10 * 1000); - } - - function onFadedOutCallback() { - Assert(jqueryMock.element.removeClass.calledWith(notificationType)); - Assert(jqueryMock.element.fadeOut.calledOnce); - } - - const notifier = new Notifier(SELECTOR, jqueryMock.jquery as any); - - // Call the method by its name... Bad but allows code reuse. - (notifier as any)[notificationType](MESSAGE, { - onFadedIn: onFadedInCallback, - onFadedOut: onFadedOutCallback - }); - - clock.tick(510); - - Assert(jqueryMock.element.fadeIn.calledOnce); - } - - - it("should fade in and fade out an error message", function() { - should_fade_in_and_out_on_notification("error"); - }); - - it("should fade in and fade out an info message", function() { - should_fade_in_and_out_on_notification("info"); - }); - - it("should fade in and fade out an warning message", function() { - should_fade_in_and_out_on_notification("warning"); - }); - - it("should fade in and fade out an success message", function() { - should_fade_in_and_out_on_notification("success"); - }); -}); \ No newline at end of file diff --git a/themes/squares/client/test/firstfactor/FirstFactorValidator.test.ts b/themes/squares/client/test/firstfactor/FirstFactorValidator.test.ts deleted file mode 100644 index ac8353278..000000000 --- a/themes/squares/client/test/firstfactor/FirstFactorValidator.test.ts +++ /dev/null @@ -1,44 +0,0 @@ - -import FirstFactorValidator = require("../../src/lib/firstfactor/FirstFactorValidator"); -import JQueryMock = require("../mocks/jquery"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -describe("test FirstFactorValidator", function () { - it("should validate first factor successfully", () => { - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.done.yields({ redirect: "http://redirect" }); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return FirstFactorValidator.validate("username", "password", "http://redirect", jqueryMock.jquery as any); - }); - - function should_fail_first_factor_validation(errorMessage: string) { - const xhr = { - status: 401 - }; - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.fail.yields(xhr, errorMessage); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return FirstFactorValidator.validate("username", "password", "http://redirect", jqueryMock.jquery as any) - .then(function () { - return BluebirdPromise.reject(new Error("First factor validation successfully finished while it should have not.")); - }, function (err: Error) { - Assert.equal(errorMessage, err.message); - return BluebirdPromise.resolve(); - }); - } - - describe("should fail first factor validation", () => { - it("should fail with error", () => { - return should_fail_first_factor_validation("Authentication failed. Please check your credentials."); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/client/test/mocks/NotifierStub.ts b/themes/squares/client/test/mocks/NotifierStub.ts deleted file mode 100644 index 9c268d66d..000000000 --- a/themes/squares/client/test/mocks/NotifierStub.ts +++ /dev/null @@ -1,33 +0,0 @@ - -import Sinon = require("sinon"); -import { INotifier } from "../../src/lib/INotifier"; - -export class NotifierStub implements INotifier { - successStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - warnStub: Sinon.SinonStub; - infoStub: Sinon.SinonStub; - - constructor() { - this.successStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - this.warnStub = Sinon.stub(); - this.infoStub = Sinon.stub(); - } - - success(msg: string) { - this.successStub(); - } - - error(msg: string) { - this.errorStub(); - } - - warning(msg: string) { - this.warnStub(); - } - - info(msg: string) { - this.infoStub(); - } -} \ No newline at end of file diff --git a/themes/squares/client/test/mocks/jquery.ts b/themes/squares/client/test/mocks/jquery.ts deleted file mode 100644 index 273f90861..000000000 --- a/themes/squares/client/test/mocks/jquery.ts +++ /dev/null @@ -1,59 +0,0 @@ - -import sinon = require("sinon"); -import jquery = require("jquery"); - - -export interface JQueryMock extends sinon.SinonStub { - get: sinon.SinonStub; - post: sinon.SinonStub; - ajax: sinon.SinonStub; - notify: sinon.SinonStub; -} - -export interface JQueryElementsMock { - ready: sinon.SinonStub; - show: sinon.SinonStub; - hide: sinon.SinonStub; - html: sinon.SinonStub; - addClass: sinon.SinonStub; - removeClass: sinon.SinonStub; - fadeIn: sinon.SinonStub; - fadeOut: sinon.SinonStub; - on: sinon.SinonStub; -} - -export interface JQueryDeferredMock { - done: sinon.SinonStub; - fail: sinon.SinonStub; -} - -export function JQueryMock(): { jquery: JQueryMock, element: JQueryElementsMock } { - const jquery = sinon.stub() as any; - const jqueryInstance: JQueryElementsMock = { - ready: sinon.stub(), - show: sinon.stub(), - hide: sinon.stub(), - html: sinon.stub(), - addClass: sinon.stub(), - removeClass: sinon.stub(), - fadeIn: sinon.stub(), - fadeOut: sinon.stub(), - on: sinon.stub() - }; - jquery.ajax = sinon.stub(); - jquery.get = sinon.stub(); - jquery.post = sinon.stub(); - jquery.notify = sinon.stub(); - jquery.returns(jqueryInstance); - return { - jquery: jquery, - element: jqueryInstance - }; -} - -export function JQueryDeferredMock(): JQueryDeferredMock { - return { - done: sinon.stub(), - fail: sinon.stub() - }; -} diff --git a/themes/squares/client/test/mocks/u2f-api.ts b/themes/squares/client/test/mocks/u2f-api.ts deleted file mode 100644 index d123f6a95..000000000 --- a/themes/squares/client/test/mocks/u2f-api.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import sinon = require("sinon"); - -export interface U2FApiMock { - sign: sinon.SinonStub; - register: sinon.SinonStub; -} - -export function U2FApiMock(): U2FApiMock { - return { - sign: sinon.stub(), - register: sinon.stub() - }; -} \ No newline at end of file diff --git a/themes/squares/client/test/secondfactor/TOTPValidator.test.ts b/themes/squares/client/test/secondfactor/TOTPValidator.test.ts deleted file mode 100644 index 5dd6f15c3..000000000 --- a/themes/squares/client/test/secondfactor/TOTPValidator.test.ts +++ /dev/null @@ -1,37 +0,0 @@ - -import TOTPValidator = require("../../src/lib/secondfactor/TOTPValidator"); -import JQueryMock = require("../mocks/jquery"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -describe("test TOTPValidator", function () { - it("should initiate an identity check successfully", () => { - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.done.yields({ redirect: "https://home.test.url" }); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return TOTPValidator.validate("totp_token", jqueryMock.jquery as any); - }); - - it("should fail validating TOTP token", () => { - const errorMessage = "Error while validating TOTP token"; - - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.fail.yields(undefined, errorMessage); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return TOTPValidator.validate("totp_token", jqueryMock.jquery as any) - .then(function () { - return BluebirdPromise.reject(new Error("Registration successfully finished while it should have not.")); - }, function (err: Error) { - Assert.equal(errorMessage, err.message); - return BluebirdPromise.resolve(); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/client/test/totp-register/totp-register.test.ts b/themes/squares/client/test/totp-register/totp-register.test.ts deleted file mode 100644 index 86fc455a1..000000000 --- a/themes/squares/client/test/totp-register/totp-register.test.ts +++ /dev/null @@ -1,31 +0,0 @@ - -import sinon = require("sinon"); -import assert = require("assert"); - -import UISelector = require("../../src/lib/totp-register/ui-selector"); -import TOTPRegister = require("../../src/lib/totp-register/totp-register"); - -describe("test totp-register", function() { - let jqueryMock: any; - let windowMock: any; - before(function() { - jqueryMock = sinon.stub(); - windowMock = { - QRCode: sinon.spy() - }; - }); - - it("should create qrcode in page", function() { - const mock = { - text: sinon.stub(), - empty: sinon.stub(), - get: sinon.stub() - }; - jqueryMock.withArgs(UISelector.QRCODE_ID_SELECTOR).returns(mock); - - TOTPRegister.default(windowMock, jqueryMock); - - assert(mock.text.calledOnce); - assert(mock.empty.calledOnce); - }); -}); \ No newline at end of file diff --git a/themes/squares/client/tsconfig.json b/themes/squares/client/tsconfig.json deleted file mode 100644 index 0bb4d62ff..000000000 --- a/themes/squares/client/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "moduleResolution": "node", - "noImplicitAny": true, - "sourceMap": true, - "removeComments": true, - "outDir": "../dist", - "baseUrl": ".", - "paths": { - "*": [ - "./types/*", - "../shared/types/*" - ] - } - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "test/**/*" - ] -} diff --git a/themes/squares/client/tslint.json b/themes/squares/client/tslint.json deleted file mode 100644 index c2c1b7501..000000000 --- a/themes/squares/client/tslint.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "no-var-keyword": true, - "quotemark": [ - true, - "double", - "avoid-escape" - ], - "semicolon": [ - true, - "always", - "ignore-bound-class-methods" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-module", - "check-separator", - "check-type" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "onespace", - "index-signature": "onespace", - "parameter": "onespace", - "property-declaration": "onespace", - "variable-declaration": "onespace" - } - ], - "no-internal-module": true, - "no-trailing-whitespace": true, - "no-null-keyword": true, - "prefer-const": true, - "jsdoc-format": true - } -} diff --git a/themes/squares/server/.directory b/themes/squares/server/.directory old mode 100644 new mode 100755 diff --git a/themes/squares/server/src/index.ts b/themes/squares/server/src/index.ts deleted file mode 100755 index fcbf4d022..000000000 --- a/themes/squares/server/src/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -#! /usr/bin/env node - -import Server from "./lib/Server"; -import { GlobalDependencies } from "../types/Dependencies"; -import YAML = require("yamljs"); - -const configurationFilepath = process.argv[2]; -if (!configurationFilepath) { - console.log("No config file has been provided."); - console.log("Usage: authelia "); - process.exit(0); -} - -const yamlContent = YAML.load(configurationFilepath); - -const deps: GlobalDependencies = { - u2f: require("u2f"), - ldapjs: require("ldapjs"), - session: require("express-session"), - winston: require("winston"), - speakeasy: require("speakeasy"), - nedb: require("nedb"), - ConnectRedis: require("connect-redis"), - Redis: require("redis") -}; - -const server = new Server(deps); -server.start(yamlContent, deps); diff --git a/themes/squares/server/src/lib/.directory b/themes/squares/server/src/lib/.directory deleted file mode 100644 index 006b379ad..000000000 --- a/themes/squares/server/src/lib/.directory +++ /dev/null @@ -1,4 +0,0 @@ -[Dolphin] -Timestamp=2018,12,17,20,59,13 -Version=3 -ViewMode=1 diff --git a/themes/squares/server/src/lib/AuthenticationSessionHandler.ts b/themes/squares/server/src/lib/AuthenticationSessionHandler.ts deleted file mode 100644 index 57361bf8a..000000000 --- a/themes/squares/server/src/lib/AuthenticationSessionHandler.ts +++ /dev/null @@ -1,45 +0,0 @@ - - -import express = require("express"); -import U2f = require("u2f"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { IRequestLogger } from "./logging/IRequestLogger"; -import { Level } from "./authentication/Level"; - -const INITIAL_AUTHENTICATION_SESSION: AuthenticationSession = { - keep_me_logged_in: false, - authentication_level: Level.NOT_AUTHENTICATED, - last_activity_datetime: undefined, - userid: undefined, - email: undefined, - groups: [], - register_request: undefined, - sign_request: undefined, - identity_check: undefined, - redirect: undefined -}; - -export class AuthenticationSessionHandler { - static reset(req: express.Request): void { - req.session.auth = Object.assign({}, INITIAL_AUTHENTICATION_SESSION, {}); - - // Initialize last activity with current time - req.session.auth.last_activity_datetime = new Date().getTime(); - } - - static get(req: express.Request, logger: IRequestLogger): AuthenticationSession { - if (!req.session) { - const errorMsg = "Something is wrong with session cookies. Please check Redis is running and Authelia can connect to it."; - logger.error(req, errorMsg); - throw new Error(errorMsg); - } - - if (!req.session.auth) { - logger.debug(req, "Authentication session %s was undefined. Resetting.", req.sessionID); - AuthenticationSessionHandler.reset(req); - } - - return req.session.auth; - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/ErrorReplies.ts b/themes/squares/server/src/lib/ErrorReplies.ts deleted file mode 100644 index f1c5f4fd1..000000000 --- a/themes/squares/server/src/lib/ErrorReplies.ts +++ /dev/null @@ -1,49 +0,0 @@ -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import { IRequestLogger } from "./logging/IRequestLogger"; - -function replyWithError(req: express.Request, res: express.Response, - code: number, logger: IRequestLogger, body?: Object): (err: Error) => void { - return function (err: Error): void { - if (req.originalUrl.startsWith("/api/") || code == 200) { - logger.error(req, "Reply with error %d: %s", code, err.message); - logger.debug(req, "%s", err.stack); - res.status(code); - res.send(body); - } - else { - logger.error(req, "Redirect to error %d: %s", code, err.message); - logger.debug(req, "%s", err.stack); - res.redirect("/error/" + code); - } - }; -} - -export function redirectTo(redirectUrl: string, req: express.Request, - res: express.Response, logger: IRequestLogger) { - return function(err: Error) { - logger.error(req, "Error: %s", err.message); - logger.debug(req, "Redirecting to %s", redirectUrl); - res.redirect(redirectUrl); - }; -} - -export function replyWithError400(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 400, logger); -} - -export function replyWithError401(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 401, logger); -} - -export function replyWithError403(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 403, logger); -} - -export function replyWithError200(req: express.Request, - res: express.Response, logger: IRequestLogger, message: string) { - return replyWithError(req, res, 200, logger, { error: message }); -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/Exceptions.ts b/themes/squares/server/src/lib/Exceptions.ts deleted file mode 100644 index 83fa4eb6b..000000000 --- a/themes/squares/server/src/lib/Exceptions.ts +++ /dev/null @@ -1,88 +0,0 @@ - -export class LdapSearchError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapSearchError"; - (Object).setPrototypeOf(this, LdapSearchError.prototype); - } -} - -export class LdapBindError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapBindError"; - (Object).setPrototypeOf(this, LdapBindError.prototype); - } -} - -export class LdapError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapError"; - (Object).setPrototypeOf(this, LdapError.prototype); - } -} - -export class IdentityError extends Error { - constructor(message?: string) { - super(message); - this.name = "IdentityError"; - (Object).setPrototypeOf(this, IdentityError.prototype); - } -} - -export class AccessDeniedError extends Error { - constructor(message?: string) { - super(message); - this.name = "AccessDeniedError"; - (Object).setPrototypeOf(this, AccessDeniedError.prototype); - } -} - -export class AuthenticationRegulationError extends Error { - constructor(message?: string) { - super(message); - this.name = "AuthenticationRegulationError"; - (Object).setPrototypeOf(this, AuthenticationRegulationError.prototype); - } -} - -export class InvalidTOTPError extends Error { - constructor(message?: string) { - super(message); - this.name = "InvalidTOTPError"; - (Object).setPrototypeOf(this, InvalidTOTPError.prototype); - } -} - -export class NotAuthenticatedError extends Error { - constructor(message?: string) { - super(message); - this.name = "NotAuthenticatedError"; - (Object).setPrototypeOf(this, NotAuthenticatedError.prototype); - } -} - -export class NotAuthorizedError extends Error { - constructor(message?: string) { - super(message); - this.name = "NotAuthanticatedError"; - (Object).setPrototypeOf(this, NotAuthorizedError.prototype); - } -} - -export class FirstFactorValidationError extends Error { - constructor(message?: string) { - super(message); - this.name = "FirstFactorValidationError"; - (Object).setPrototypeOf(this, FirstFactorValidationError.prototype); - } -} - -export class SecondFactorValidationError extends Error { - constructor(message?: string) { - super(message); - this.name = "SecondFactorValidationError"; - (Object).setPrototypeOf(this, FirstFactorValidationError.prototype); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/FirstFactorValidator.ts b/themes/squares/server/src/lib/FirstFactorValidator.ts deleted file mode 100644 index 231060002..000000000 --- a/themes/squares/server/src/lib/FirstFactorValidator.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import objectPath = require("object-path"); -import Exceptions = require("./Exceptions"); -import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler"; -import { IRequestLogger } from "./logging/IRequestLogger"; -import { Level } from "./authentication/Level"; - -export function validate(req: express.Request, logger: IRequestLogger): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, logger); - - if (!authSession.userid || authSession.authentication_level < Level.ONE_FACTOR) - return reject(new Exceptions.FirstFactorValidationError( - "First factor has not been validated yet.")); - - resolve(); - }); -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/IdentityCheckMiddleware.spec.ts b/themes/squares/server/src/lib/IdentityCheckMiddleware.spec.ts deleted file mode 100644 index 842ed6bcb..000000000 --- a/themes/squares/server/src/lib/IdentityCheckMiddleware.spec.ts +++ /dev/null @@ -1,176 +0,0 @@ - -import sinon = require("sinon"); -import IdentityValidator = require("./IdentityCheckMiddleware"); -import { AuthenticationSessionHandler } - from "./AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { UserDataStore } from "./storage/UserDataStore"; -import exceptions = require("./Exceptions"); -import { ServerVariables } from "./ServerVariables"; -import Assert = require("assert"); -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("./stubs/express.spec"); -import NotifierMock = require("./notifiers/NotifierStub.spec"); -import { IdentityValidableStub } from "./IdentityValidableStub.spec"; -import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "./ServerVariablesMockBuilder.spec"; -import { PRE_VALIDATION_TEMPLATE } - from "./IdentityCheckPreValidationTemplate"; - - -describe("IdentityCheckMiddleware", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let app: express.Application; - let app_get: sinon.SinonStub; - let app_post: sinon.SinonStub; - let identityValidable: IdentityValidableStub; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - - req.headers = {}; - req.originalUrl = "/non-api/xxx"; - req.session = {}; - - req.query = {}; - req.app = {}; - - identityValidable = new IdentityValidableStub(); - - mocks.notifier.notifyStub.returns(BluebirdPromise.resolve()); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve()); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({ userId: "user" })); - - app = express(); - app_get = sinon.stub(app, "get"); - app_post = sinon.stub(app, "post"); - }); - - afterEach(function () { - app_get.restore(); - app_post.restore(); - }); - - describe("test start GET", function () { - it("should redirect to error 401 if pre validation initialization \ -throws a first factor error", function () { - identityValidable.preValidationInitStub.returns(BluebirdPromise.reject( - new exceptions.FirstFactorValidationError( - "Error during prevalidation"))); - const callback = IdentityValidator.get_start_validation( - identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(() => { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - - // In that case we answer with 200 to avoid user enumeration. - it("should send 200 if email is missing in provided identity", function () { - const identity = { userid: "abc" }; - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(identityValidable.preValidationResponseStub.called); - }); - }); - - // In that case we answer with 200 to avoid user enumeration. - it("should send 200 if userid is missing in provided identity", - function () { - const endpoint = "/protected"; - const identity = { email: "abc@example.com" }; - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(identityValidable.preValidationResponseStub.called); - }); - }); - - it("should issue a token, send an email and return 204", function () { - const endpoint = "/protected"; - const identity = { userid: "user", email: "abc@example.com" }; - req.get = sinon.stub().withArgs("Host").returns("localhost"); - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/finish_endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(mocks.notifier.notifyStub.calledOnce); - Assert(mocks.userDataStore.produceIdentityValidationTokenStub - .calledOnce); - Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub - .getCall(0).args[0], "user"); - Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub - .getCall(0).args[3], 240000); - }); - }); - }); - - - - describe("test finish GET", function () { - it("should send 401 if no identity_token is provided", () => { - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - - it("should call postValidation if identity_token is provided and still \ -valid", function () { - req.query.identity_token = "token"; - - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - return callback(req as any, res as any, undefined); - }); - - it("should return 401 if identity_token is provided but invalid", - function () { - req.query.identity_token = "token"; - - identityValidable.postValidationInitStub - .returns(BluebirdPromise.resolve()); - mocks.userDataStore.consumeIdentityValidationTokenStub.reset(); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.reject(new Error("Invalid token"))); - - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - return callback(req as any, res as any, undefined) - .then(() => { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - }); -}); diff --git a/themes/squares/server/src/lib/IdentityCheckMiddleware.ts b/themes/squares/server/src/lib/IdentityCheckMiddleware.ts deleted file mode 100644 index e72ea4db1..000000000 --- a/themes/squares/server/src/lib/IdentityCheckMiddleware.ts +++ /dev/null @@ -1,138 +0,0 @@ -import objectPath = require("object-path"); -import randomstring = require("randomstring"); -import BluebirdPromise = require("bluebird"); -import util = require("util"); -import Exceptions = require("./Exceptions"); -import fs = require("fs"); -import ejs = require("ejs"); -import { IUserDataStore } from "./storage/IUserDataStore"; -import Express = require("express"); -import ErrorReplies = require("./ErrorReplies"); -import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { ServerVariables } from "./ServerVariables"; -import { IdentityValidable } from "./IdentityValidable"; - -import Identity = require("../../types/Identity"); -import { IdentityValidationDocument } - from "./storage/IdentityValidationDocument"; - -const filePath = __dirname + "/../resources/email-template.ejs"; -const email_template = fs.readFileSync(filePath, "utf8"); - -function createAndSaveToken(userid: string, challenge: string, - userDataStore: IUserDataStore): BluebirdPromise { - - const five_minutes = 4 * 60 * 1000; - const token = randomstring.generate({ length: 64 }); - const that = this; - - return userDataStore.produceIdentityValidationToken(userid, token, challenge, - five_minutes) - .then(function () { - return BluebirdPromise.resolve(token); - }); -} - -function consumeToken(token: string, challenge: string, - userDataStore: IUserDataStore) - : BluebirdPromise { - return userDataStore.consumeIdentityValidationToken(token, challenge); -} - -export function register(app: Express.Application, - pre_validation_endpoint: string, - post_validation_endpoint: string, - handler: IdentityValidable, - vars: ServerVariables) { - - app.get(pre_validation_endpoint, - get_start_validation(handler, post_validation_endpoint, vars)); - app.get(post_validation_endpoint, - get_finish_validation(handler, vars)); -} - -function checkIdentityToken(req: Express.Request, identityToken: string) - : BluebirdPromise { - if (!identityToken) - return BluebirdPromise.reject( - new Exceptions.AccessDeniedError("No identity token provided")); - return BluebirdPromise.resolve(); -} - -export function get_finish_validation(handler: IdentityValidable, - vars: ServerVariables) - : Express.RequestHandler { - - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - - let authSession: AuthenticationSession; - const identityToken = objectPath.get( - req, "query.identity_token"); - vars.logger.debug(req, "Identity token provided is %s", identityToken); - - return checkIdentityToken(req, identityToken) - .then(() => { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - return handler.postValidationInit(req); - }) - .then(() => { - return consumeToken(identityToken, handler.challenge(), - vars.userDataStore); - }) - .then((doc: IdentityValidationDocument) => { - authSession.identity_check = { - challenge: handler.challenge(), - userid: doc.userId - }; - handler.postValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - }; -} - -export function get_start_validation(handler: IdentityValidable, - postValidationEndpoint: string, - vars: ServerVariables) - : Express.RequestHandler { - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - let identity: Identity.Identity; - - return handler.preValidationInit(req) - .then((id: Identity.Identity) => { - identity = id; - const email = identity.email; - const userid = identity.userid; - vars.logger.info(req, "Start identity validation of user \"%s\"", - userid); - - if (!(email && userid)) - return BluebirdPromise.reject(new Exceptions.IdentityError( - "Missing user id or email address")); - - return createAndSaveToken(userid, handler.challenge(), - vars.userDataStore); - }) - .then((token) => { - const host = req.get("Host"); - const link_url = util.format("https://%s%s?identity_token=%s", host, - postValidationEndpoint, token); - vars.logger.info(req, "Notification sent to user \"%s\"", - identity.userid); - return vars.notifier.notify(identity.email, handler.mailSubject(), - link_url); - }) - .then(() => { - handler.preValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(Exceptions.IdentityError, (err: Error) => { - handler.preValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - }; -} diff --git a/themes/squares/server/src/lib/IdentityCheckPreValidationTemplate.ts b/themes/squares/server/src/lib/IdentityCheckPreValidationTemplate.ts deleted file mode 100644 index 0161ce406..000000000 --- a/themes/squares/server/src/lib/IdentityCheckPreValidationTemplate.ts +++ /dev/null @@ -1,3 +0,0 @@ - - -export const PRE_VALIDATION_TEMPLATE = "need-identity-validation"; \ No newline at end of file diff --git a/themes/squares/server/src/lib/IdentityValidable.ts b/themes/squares/server/src/lib/IdentityValidable.ts deleted file mode 100644 index 075580c9e..000000000 --- a/themes/squares/server/src/lib/IdentityValidable.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Bluebird = require("bluebird"); -import Identity = require("../../types/Identity"); - -// IdentityValidator allows user to go through a identity validation process -// in two steps: -// - Request an operation to be performed (password reset, registration). -// - Confirm operation with email. - -export interface IdentityValidable { - challenge(): string; - preValidationInit(req: Express.Request): Bluebird; - postValidationInit(req: Express.Request): Bluebird; - - // Serves a page after identity check request - preValidationResponse(req: Express.Request, res: Express.Response): void; - // Serves the page if identity validated - postValidationResponse(req: Express.Request, res: Express.Response): void; - mailSubject(): string; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/IdentityValidableStub.spec.ts b/themes/squares/server/src/lib/IdentityValidableStub.spec.ts deleted file mode 100644 index 20a977140..000000000 --- a/themes/squares/server/src/lib/IdentityValidableStub.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ - -import Sinon = require("sinon"); -import { IdentityValidable } from "./IdentityValidable"; -import express = require("express"); -import Bluebird = require("bluebird"); -import { Identity } from "../../types/Identity"; - - -export class IdentityValidableStub implements IdentityValidable { - challengeStub: Sinon.SinonStub; - preValidationInitStub: Sinon.SinonStub; - postValidationInitStub: Sinon.SinonStub; - preValidationResponseStub: Sinon.SinonStub; - postValidationResponseStub: Sinon.SinonStub; - mailSubjectStub: Sinon.SinonStub; - - constructor() { - this.challengeStub = Sinon.stub(); - - this.preValidationInitStub = Sinon.stub(); - this.postValidationInitStub = Sinon.stub(); - - this.preValidationResponseStub = Sinon.stub(); - this.postValidationResponseStub = Sinon.stub(); - - this.mailSubjectStub = Sinon.stub(); - } - - challenge(): string { - return this.challengeStub(); - } - - preValidationInit(req: Express.Request): Bluebird { - return this.preValidationInitStub(req); - } - - postValidationInit(req: Express.Request): Bluebird { - return this.postValidationInitStub(req); - } - - preValidationResponse(req: Express.Request, res: Express.Response): void { - return this.preValidationResponseStub(req, res); - } - - postValidationResponse(req: Express.Request, res: Express.Response): void { - return this.postValidationResponseStub(req, res); - } - - mailSubject(): string { - return this.mailSubjectStub(); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/Server.spec.ts b/themes/squares/server/src/lib/Server.spec.ts deleted file mode 100644 index 365163254..000000000 --- a/themes/squares/server/src/lib/Server.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ - -import Assert = require("assert"); -import Sinon = require("sinon"); -import nedb = require("nedb"); -import express = require("express"); -import winston = require("winston"); -import speakeasy = require("speakeasy"); -import u2f = require("u2f"); -import session = require("express-session"); -import { Configuration } from "./configuration/schema/Configuration"; -import { GlobalDependencies } from "../../types/Dependencies"; -import Server from "./Server"; -import { LdapjsMock, LdapjsClientMock } from "./stubs/ldapjs.spec"; - - -describe("Server", function () { - let deps: GlobalDependencies; - let sessionMock: Sinon.SinonSpy; - let ldapjsMock: LdapjsMock; - - before(function () { - sessionMock = Sinon.spy(session); - ldapjsMock = new LdapjsMock(); - - deps = { - speakeasy: speakeasy, - u2f: u2f, - nedb: nedb, - winston: winston, - ldapjs: ldapjsMock as any, - session: sessionMock as any, - ConnectRedis: Sinon.spy(), - Redis: Sinon.spy() as any - }; - }); - - - it("should set cookie scope to domain set in the config", function () { - const config: Configuration = { - port: 8081, - session: { - domain: "example.com", - secret: "secret" - }, - authentication_backend: { - ldap: { - url: "http://ldap", - user: "user", - password: "password", - base_dn: "dc=example,dc=com" - }, - }, - notifier: { - email: { - username: "user@example.com", - password: "password", - sender: "test@authelia.com", - service: "gmail" - } - }, - regulation: { - max_retries: 3, - ban_time: 5 * 60, - find_time: 5 * 60 - }, - storage: { - local: { - in_memory: true - } - } - }; - - const server = new Server(deps); - server.start(config, deps) - .then(function () { - Assert(sessionMock.calledOnce); - Assert.equal(sessionMock.getCall(0).args[0].cookie.domain, "example.com"); - server.stop(); - }); - }); -}); diff --git a/themes/squares/server/src/lib/Server.ts b/themes/squares/server/src/lib/Server.ts deleted file mode 100644 index 4090f6294..000000000 --- a/themes/squares/server/src/lib/Server.ts +++ /dev/null @@ -1,93 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import ObjectPath = require("object-path"); - -import { Configuration } from "./configuration/schema/Configuration"; -import { GlobalDependencies } from "../../types/Dependencies"; -import { UserDataStore } from "./storage/UserDataStore"; -import { ConfigurationParser } from "./configuration/ConfigurationParser"; -import { SessionConfigurationBuilder } from "./configuration/SessionConfigurationBuilder"; -import { GlobalLogger } from "./logging/GlobalLogger"; -import { RequestLogger } from "./logging/RequestLogger"; -import { ServerVariables } from "./ServerVariables"; -import { ServerVariablesInitializer } from "./ServerVariablesInitializer"; -import { Configurator } from "./web_server/Configurator"; - -import * as Express from "express"; -import * as Path from "path"; -import * as http from "http"; - -function clone(obj: any) { - return JSON.parse(JSON.stringify(obj)); -} - -export default class Server { - private httpServer: http.Server; - private globalLogger: GlobalLogger; - private requestLogger: RequestLogger; - - constructor(deps: GlobalDependencies) { - this.globalLogger = new GlobalLogger(deps.winston); - this.requestLogger = new RequestLogger(deps.winston); - } - - private displayConfigurations(configuration: Configuration) { - const displayableConfiguration: Configuration = clone(configuration); - const STARS = "*****"; - - if (displayableConfiguration.authentication_backend.ldap) { - displayableConfiguration.authentication_backend.ldap.password = STARS; - } - - displayableConfiguration.session.secret = STARS; - if (displayableConfiguration.notifier && displayableConfiguration.notifier.email) - displayableConfiguration.notifier.email.password = STARS; - if (displayableConfiguration.notifier && displayableConfiguration.notifier.smtp) - displayableConfiguration.notifier.smtp.password = STARS; - - this.globalLogger.debug("User configuration is %s", - JSON.stringify(displayableConfiguration, undefined, 2)); - } - - private setup(config: Configuration, app: Express.Application, deps: GlobalDependencies): BluebirdPromise { - const that = this; - return ServerVariablesInitializer.initialize( - config, this.globalLogger, this.requestLogger, deps) - .then(function (vars: ServerVariables) { - Configurator.configure(config, app, vars, deps); - return BluebirdPromise.resolve(); - }); - } - - private startServer(app: Express.Application, port: number) { - const that = this; - that.globalLogger.info("Starting Authelia..."); - return new BluebirdPromise((resolve, reject) => { - this.httpServer = app.listen(port, function (err: string) { - that.globalLogger.info("Listening on port %d...", port); - resolve(); - }); - }); - } - - start(configuration: Configuration, deps: GlobalDependencies) - : BluebirdPromise { - const that = this; - const app = Express(); - - const appConfiguration = ConfigurationParser.parse(configuration); - - // by default the level of logs is info - deps.winston.level = appConfiguration.logs_level; - this.displayConfigurations(appConfiguration); - - return this.setup(appConfiguration, app, deps) - .then(function () { - return that.startServer(app, appConfiguration.port); - }); - } - - stop() { - this.httpServer.close(); - } -} - diff --git a/themes/squares/server/src/lib/ServerVariables.ts b/themes/squares/server/src/lib/ServerVariables.ts deleted file mode 100644 index cd3dd6dc8..000000000 --- a/themes/squares/server/src/lib/ServerVariables.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { IRequestLogger } from "./logging/IRequestLogger"; -import { ITotpHandler } from "./authentication/totp/ITotpHandler"; -import { IU2fHandler } from "./authentication/u2f/IU2fHandler"; -import { IUserDataStore } from "./storage/IUserDataStore"; -import { INotifier } from "./notifiers/INotifier"; -import { IRegulator } from "./regulation/IRegulator"; -import { Configuration } from "./configuration/schema/Configuration"; -import { IAuthorizer } from "./authorization/IAuthorizer"; -import { IUsersDatabase } from "./authentication/backends/IUsersDatabase"; - -export interface ServerVariables { - logger: IRequestLogger; - usersDatabase: IUsersDatabase; - totpHandler: ITotpHandler; - u2f: IU2fHandler; - userDataStore: IUserDataStore; - notifier: INotifier; - regulator: IRegulator; - config: Configuration; - authorizer: IAuthorizer; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/ServerVariablesInitializer.ts b/themes/squares/server/src/lib/ServerVariablesInitializer.ts deleted file mode 100644 index df79238cc..000000000 --- a/themes/squares/server/src/lib/ServerVariablesInitializer.ts +++ /dev/null @@ -1,116 +0,0 @@ - -import winston = require("winston"); -import BluebirdPromise = require("bluebird"); -import U2F = require("u2f"); -import Nodemailer = require("nodemailer"); - -import { IRequestLogger } from "./logging/IRequestLogger"; -import { RequestLogger } from "./logging/RequestLogger"; - -import { TotpHandler } from "./authentication/totp/TotpHandler"; -import { ITotpHandler } from "./authentication/totp/ITotpHandler"; -import { NotifierFactory } from "./notifiers/NotifierFactory"; -import { MailSenderBuilder } from "./notifiers/MailSenderBuilder"; -import { LdapUsersDatabase } from "./authentication/backends/ldap/LdapUsersDatabase"; -import { ConnectorFactory } from "./authentication/backends/ldap/connector/ConnectorFactory"; - -import { IUserDataStore } from "./storage/IUserDataStore"; -import { UserDataStore } from "./storage/UserDataStore"; -import { INotifier } from "./notifiers/INotifier"; -import { Regulator } from "./regulation/Regulator"; -import { IRegulator } from "./regulation/IRegulator"; -import Configuration = require("./configuration/schema/Configuration"); -import { CollectionFactoryFactory } from "./storage/CollectionFactoryFactory"; -import { ICollectionFactory } from "./storage/ICollectionFactory"; -import { MongoCollectionFactory } from "./storage/mongo/MongoCollectionFactory"; -import { IMongoClient } from "./connectors/mongo/IMongoClient"; - -import { GlobalDependencies } from "../../types/Dependencies"; -import { ServerVariables } from "./ServerVariables"; -import { MongoClient } from "./connectors/mongo/MongoClient"; -import { IGlobalLogger } from "./logging/IGlobalLogger"; -import { SessionFactory } from "./authentication/backends/ldap/SessionFactory"; -import { IUsersDatabase } from "./authentication/backends/IUsersDatabase"; -import { FileUsersDatabase } from "./authentication/backends/file/FileUsersDatabase"; -import { Authorizer } from "./authorization/Authorizer"; - -class UserDataStoreFactory { - static create(config: Configuration.Configuration, globalLogger: IGlobalLogger): BluebirdPromise { - if (config.storage.local) { - const nedbOptions: Nedb.DataStoreOptions = { - filename: config.storage.local.path, - inMemoryOnly: config.storage.local.in_memory - }; - const collectionFactory = CollectionFactoryFactory.createNedb(nedbOptions); - return BluebirdPromise.resolve(new UserDataStore(collectionFactory)); - } - else if (config.storage.mongo) { - const mongoClient = new MongoClient( - config.storage.mongo, - globalLogger); - const collectionFactory = CollectionFactoryFactory.createMongo(mongoClient); - return BluebirdPromise.resolve(new UserDataStore(collectionFactory)); - } - - return BluebirdPromise.reject(new Error("Storage backend incorrectly configured.")); - } -} - -export class ServerVariablesInitializer { - static createUsersDatabase( - config: Configuration.Configuration, - deps: GlobalDependencies) - : IUsersDatabase { - - if (config.authentication_backend.ldap) { - const ldapConfig = config.authentication_backend.ldap; - return new LdapUsersDatabase( - new SessionFactory( - ldapConfig, - new ConnectorFactory(ldapConfig, deps.ldapjs), - deps.winston - ), - ldapConfig - ); - } - else if (config.authentication_backend.file) { - return new FileUsersDatabase(config.authentication_backend.file); - } - } - - static initialize( - config: Configuration.Configuration, - globalLogger: IGlobalLogger, - requestLogger: IRequestLogger, - deps: GlobalDependencies) - : BluebirdPromise { - - const mailSenderBuilder = - new MailSenderBuilder(Nodemailer); - const notifier = NotifierFactory.build( - config.notifier, mailSenderBuilder); - const authorizer = new Authorizer(config.access_control, deps.winston); - const totpHandler = new TotpHandler(deps.speakeasy); - const usersDatabase = this.createUsersDatabase( - config, deps); - - return UserDataStoreFactory.create(config, globalLogger) - .then(function (userDataStore: UserDataStore) { - const regulator = new Regulator(userDataStore, config.regulation.max_retries, - config.regulation.find_time, config.regulation.ban_time); - - const variables: ServerVariables = { - authorizer: authorizer, - config: config, - usersDatabase: usersDatabase, - logger: requestLogger, - notifier: notifier, - regulator: regulator, - totpHandler: totpHandler, - u2f: deps.u2f, - userDataStore: userDataStore - }; - return BluebirdPromise.resolve(variables); - }); - } -} diff --git a/themes/squares/server/src/lib/ServerVariablesMockBuilder.spec.ts b/themes/squares/server/src/lib/ServerVariablesMockBuilder.spec.ts deleted file mode 100644 index 7874702a0..000000000 --- a/themes/squares/server/src/lib/ServerVariablesMockBuilder.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { ServerVariables } from "./ServerVariables"; - -import { Configuration } from "./configuration/schema/Configuration"; -import { IUsersDatabaseStub } from "./authentication/backends/IUsersDatabaseStub.spec"; -import { AuthorizerStub } from "./authorization/AuthorizerStub.spec"; -import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec"; -import { NotifierStub } from "./notifiers/NotifierStub.spec"; -import { RegulatorStub } from "./regulation/RegulatorStub.spec"; -import { TotpHandlerStub } from "./authentication/totp/TotpHandlerStub.spec"; -import { UserDataStoreStub } from "./storage/UserDataStoreStub.spec"; -import { U2fHandlerStub } from "./authentication/u2f/U2fHandlerStub.spec"; - -export interface ServerVariablesMock { - authorizer: AuthorizerStub; - config: Configuration; - usersDatabase: IUsersDatabaseStub; - logger: RequestLoggerStub; - notifier: NotifierStub; - regulator: RegulatorStub; - totpHandler: TotpHandlerStub; - userDataStore: UserDataStoreStub; - u2f: U2fHandlerStub; -} - -export class ServerVariablesMockBuilder { - static build(enableLogging?: boolean): { variables: ServerVariables, mocks: ServerVariablesMock} { - const mocks: ServerVariablesMock = { - authorizer: new AuthorizerStub(), - config: { - access_control: {}, - totp: { - issuer: "authelia.com" - }, - authentication_backend: { - ldap: { - url: "ldap://ldap", - base_dn: "dc=example,dc=com", - user: "user", - password: "password", - mail_attribute: "mail", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn" - }, - }, - logs_level: "debug", - notifier: {}, - port: 8080, - regulation: { - ban_time: 50, - find_time: 50, - max_retries: 3 - }, - session: { - secret: "my_secret", - domain: "mydomain" - }, - storage: {} - }, - usersDatabase: new IUsersDatabaseStub(), - logger: new RequestLoggerStub(enableLogging), - notifier: new NotifierStub(), - regulator: new RegulatorStub(), - totpHandler: new TotpHandlerStub(), - userDataStore: new UserDataStoreStub(), - u2f: new U2fHandlerStub() - }; - const vars: ServerVariables = { - authorizer: mocks.authorizer, - config: mocks.config, - usersDatabase: mocks.usersDatabase, - logger: mocks.logger, - notifier: mocks.notifier, - regulator: mocks.regulator, - totpHandler: mocks.totpHandler, - userDataStore: mocks.userDataStore, - u2f: mocks.u2f - }; - - return { - variables: vars, - mocks: mocks - }; - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/Level.ts b/themes/squares/server/src/lib/authentication/Level.ts deleted file mode 100644 index 57b6a2346..000000000 --- a/themes/squares/server/src/lib/authentication/Level.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum Level { - NOT_AUTHENTICATED = 0, - ONE_FACTOR = 1, - TWO_FACTOR = 2 -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/GroupsAndEmails.ts b/themes/squares/server/src/lib/authentication/backends/GroupsAndEmails.ts deleted file mode 100644 index 3434ba66f..000000000 --- a/themes/squares/server/src/lib/authentication/backends/GroupsAndEmails.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface GroupsAndEmails { - groups: string[]; - emails: string[]; -} diff --git a/themes/squares/server/src/lib/authentication/backends/IUsersDatabase.ts b/themes/squares/server/src/lib/authentication/backends/IUsersDatabase.ts deleted file mode 100644 index d7fa13b7a..000000000 --- a/themes/squares/server/src/lib/authentication/backends/IUsersDatabase.ts +++ /dev/null @@ -1,10 +0,0 @@ -import Bluebird = require("bluebird"); - -import { GroupsAndEmails } from "./GroupsAndEmails"; - -export interface IUsersDatabase { - checkUserPassword(username: string, password: string): Bluebird; - getEmails(username: string): Bluebird; - getGroups(username: string): Bluebird; - updatePassword(username: string, newPassword: string): Bluebird; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts b/themes/squares/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts deleted file mode 100644 index 19341a5dd..000000000 --- a/themes/squares/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); - -import { IUsersDatabase } from "./IUsersDatabase"; -import { GroupsAndEmails } from "./GroupsAndEmails"; - -export class IUsersDatabaseStub implements IUsersDatabase { - checkUserPasswordStub: Sinon.SinonStub; - getEmailsStub: Sinon.SinonStub; - getGroupsStub: Sinon.SinonStub; - updatePasswordStub: Sinon.SinonStub; - - constructor() { - this.checkUserPasswordStub = Sinon.stub(); - this.getEmailsStub = Sinon.stub(); - this.getGroupsStub = Sinon.stub(); - this.updatePasswordStub = Sinon.stub(); - } - - checkUserPassword(username: string, password: string): Bluebird { - return this.checkUserPasswordStub(username, password); - } - - getEmails(username: string): Bluebird { - return this.getEmailsStub(username); - } - - getGroups(username: string): Bluebird { - return this.getGroupsStub(username); - } - - updatePassword(username: string, newPassword: string): Bluebird { - return this.updatePasswordStub(username, newPassword); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts b/themes/squares/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts deleted file mode 100644 index a258a78f7..000000000 --- a/themes/squares/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts +++ /dev/null @@ -1,224 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); -import Fs = require("fs"); -import Sinon = require("sinon"); -import Tmp = require("tmp"); - -import { FileUsersDatabase } from "./FileUsersDatabase"; -import { FileUsersDatabaseConfiguration } from "../../../configuration/schema/FileUsersDatabaseConfiguration"; -import { HashGenerator } from "../../../utils/HashGenerator"; - -const GOOD_DATABASE = ` -users: - john: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev - - harry: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - emails: harry.potter@authelia.com - groups: [] -`; - -const BAD_HASH = ` -users: - john: - password: "{CRYPT}$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev -`; - -const NO_PASSWORD_DATABASE = ` -users: - john: - email: john.doe@authelia.com - groups: - - admins - - dev -`; - -const NO_EMAIL_DATABASE = ` -users: - john: - password: "{CRYPT}$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - groups: - - admins - - dev -`; - -const SINGLE_USER_DATABASE = ` -users: - john: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev -` - -function createTmpFileFrom(yaml: string) { - const tmpFileAsync = Bluebird.promisify(Tmp.file); - return tmpFileAsync() - .then((path: string) => { - Fs.writeFileSync(path, yaml, "utf-8"); - return Bluebird.resolve(path); - }); -} - -describe("authentication/backends/file/FileUsersDatabase", function() { - let configuration: FileUsersDatabaseConfiguration; - - describe("checkUserPassword", () => { - describe("good config", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then((groupsAndEmails) => { - Assert.deepEqual(groupsAndEmails.groups, ["admins", "dev"]); - Assert.deepEqual(groupsAndEmails.emails, ["john.doe@authelia.com"]); - }); - }); - - it("should fail when password is wrong", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "bad_password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("no_user", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("bad hash", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail when hash is wrong", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("no password", () => { - beforeEach(() => { - return createTmpFileFrom(NO_PASSWORD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - }); - - describe("getEmails", () => { - describe("good config", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("john") - .then((emails) => { - Assert.deepEqual(emails, ["john.doe@authelia.com"]); - }); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("no_user") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("no email provided", () => { - beforeEach(() => { - return createTmpFileFrom(NO_EMAIL_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("john") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - }); - - describe("updatePassword", () => { - beforeEach(() => { - return createTmpFileFrom(SINGLE_USER_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - const NEW_HASH = "{CRYPT}$6$rounds=500000$Qw6MhgADvLyYMEq9$ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const stub = Sinon.stub(HashGenerator, "ssha512").returns(Bluebird.resolve(NEW_HASH)); - return usersDatabase.updatePassword("john", "mypassword") - .then(() => { - const content = Fs.readFileSync(configuration.path, "utf-8"); - const matches = content.match(/password: '(.+)'/); - Assert.equal(matches[1], NEW_HASH); - }) - .finally(() => stub.restore()); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.updatePassword("bad_user", "mypassword") - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => Bluebird.resolve()); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/file/FileUsersDatabase.ts b/themes/squares/server/src/lib/authentication/backends/file/FileUsersDatabase.ts deleted file mode 100644 index d34dde21e..000000000 --- a/themes/squares/server/src/lib/authentication/backends/file/FileUsersDatabase.ts +++ /dev/null @@ -1,182 +0,0 @@ -import Bluebird = require("bluebird"); -import Fs = require("fs"); -import Yaml = require("yamljs"); - -import { FileUsersDatabaseConfiguration } - from "../../../configuration/schema/FileUsersDatabaseConfiguration"; -import { GroupsAndEmails } from "../GroupsAndEmails"; -import { IUsersDatabase } from "../IUsersDatabase"; -import { HashGenerator } from "../../../utils/HashGenerator"; -import { ReadWriteQueue } from "./ReadWriteQueue"; - -const loadAsync = Bluebird.promisify(Yaml.load); - -export class FileUsersDatabase implements IUsersDatabase { - private configuration: FileUsersDatabaseConfiguration; - private queue: ReadWriteQueue; - - constructor(configuration: FileUsersDatabaseConfiguration) { - this.configuration = configuration; - this.queue = new ReadWriteQueue(this.configuration.path); - } - - /** - * Read database from file. - * It enqueues the read task so that it is scheduled - * between other reads and writes. - */ - private readDatabase(): Bluebird { - return new Bluebird((resolve, reject) => { - this.queue.read((err: Error, data: string) => { - if (err) { - reject(err); - return; - } - resolve(data); - this.queue.next(); - }); - }) - .then((content) => { - const database = Yaml.parse(content); - if (!database) { - return Bluebird.reject(new Error("Unable to parse YAML file.")); - } - return Bluebird.resolve(database); - }); - } - - /** - * Checks the user exists in the database. - */ - private checkUserExists( - database: any, - username: string) - : Bluebird { - if (!(username in database.users)) { - return Bluebird.reject( - new Error(`User ${username} does not exist in database.`)); - } - return Bluebird.resolve(); - } - - /** - * Check the password of a given user. - */ - private checkPassword( - database: any, - username: string, - password: string) - : Bluebird { - const storedHash: string = database.users[username].password; - const matches = storedHash.match(/rounds=([0-9]+)\$([a-zA-z0-9]+)\$/); - if (!(matches && matches.length == 3)) { - return Bluebird.reject(new Error("Unable to detect the hash salt and rounds. " + - "Make sure the password is hashed with SSHA512.")); - } - - const rounds: number = parseInt(matches[1]); - const salt = matches[2]; - - return HashGenerator.ssha512(password, rounds, salt) - .then((hash: string) => { - if (hash !== storedHash) { - return Bluebird.reject(new Error("Wrong username/password.")); - } - return Bluebird.resolve(); - }); - } - - /** - * Retrieve email addresses of a given user. - */ - private retrieveEmails( - database: any, - username: string) - : Bluebird { - if (!("email" in database.users[username])) { - return Bluebird.reject( - new Error(`User ${username} has no email address.`)); - } - return Bluebird.resolve( - [database.users[username].email]); - } - - private retrieveGroups( - database: any, - username: string) - : Bluebird { - if (!("groups" in database.users[username])) { - return Bluebird.resolve([]); - } - return Bluebird.resolve( - database.users[username].groups); - } - - private replacePassword( - database: any, - username: string, - newPassword: string) - : Bluebird { - const that = this; - return HashGenerator.ssha512(newPassword) - .then((hash) => { - database.users[username].password = hash; - const str = Yaml.stringify(database, 4, 2); - return Bluebird.resolve(str); - }) - .then((content: string) => { - return new Bluebird((resolve, reject) => { - that.queue.write(content, (err) => { - if (err) { - return reject(err); - } - resolve(); - that.queue.next(); - }); - }); - }); - } - - checkUserPassword( - username: string, - password: string) - : Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.checkPassword(database, username, password)) - .then(() => { - return Bluebird.join( - this.retrieveEmails(database, username), - this.retrieveGroups(database, username) - ).spread((emails: string[], groups: string[]) => { - return { emails: emails, groups: groups }; - }); - }); - }); - } - - getEmails(username: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.retrieveEmails(database, username)); - }); - } - - getGroups(username: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.retrieveGroups(database, username)); - }); - } - - updatePassword(username: string, newPassword: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.replacePassword(database, username, newPassword)); - }); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/file/ReadWriteQueue.ts b/themes/squares/server/src/lib/authentication/backends/file/ReadWriteQueue.ts deleted file mode 100644 index 957ddaec5..000000000 --- a/themes/squares/server/src/lib/authentication/backends/file/ReadWriteQueue.ts +++ /dev/null @@ -1,60 +0,0 @@ -import Fs = require("fs"); - -type Callback = (err: Error, data?: string) => void; -type ContentAndCallback = [string, Callback] | [string, string, Callback]; - -/** - * WriteQueue is a queue synchronizing writes to a file. - * - * Example of use: - * - * queue.add(mycontent, (err) => { - * // do whatever you want here. - * queue.next(); - * }) - */ -export class ReadWriteQueue { - private filePath: string; - private queue: ContentAndCallback[]; - - constructor (filePath: string) { - this.queue = []; - this.filePath = filePath; - } - - next () { - if (this.queue.length === 0) - return; - - const task = this.queue[0]; - - if (task[0] == "write") { - Fs.writeFile(this.filePath, task[1], "utf-8", (err) => { - this.queue.shift(); - const cb = task[2] as Callback; - cb(err); - }); - } - else if (task[0] == "read") { - Fs.readFile(this.filePath, { encoding: "utf-8"} , (err, data) => { - this.queue.shift(); - const cb = task[1] as Callback; - cb(err, data); - }); - } - } - - write (content: string, cb: Callback) { - this.queue.push(["write", content, cb]); - if (this.queue.length === 1) { - this.next(); - } - } - - read (cb: Callback) { - this.queue.push(["read", cb]); - if (this.queue.length === 1) { - this.next(); - } - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/ISession.ts b/themes/squares/server/src/lib/authentication/backends/ldap/ISession.ts deleted file mode 100644 index da2c74433..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/ISession.ts +++ /dev/null @@ -1,12 +0,0 @@ - -import BluebirdPromise = require("bluebird"); - -export interface ISession { - open(): BluebirdPromise; - close(): BluebirdPromise; - - searchUserDn(username: string): BluebirdPromise; - searchEmails(username: string): BluebirdPromise; - searchGroups(username: string): BluebirdPromise; - modifyPassword(username: string, newPassword: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/ISessionFactory.ts b/themes/squares/server/src/lib/authentication/backends/ldap/ISessionFactory.ts deleted file mode 100644 index 014d1eea5..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/ISessionFactory.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import { ISession } from "./ISession"; - -export interface ISessionFactory { - create(userDN: string, password: string): ISession; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts b/themes/squares/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts deleted file mode 100644 index f4a6e6306..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts +++ /dev/null @@ -1,386 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); - -import { LdapUsersDatabase } from "./LdapUsersDatabase"; - -import { SessionFactoryStub } from "./SessionFactoryStub.spec"; -import { SessionStub } from "./SessionStub.spec"; - -const ADMIN_USER_DN = "cn=admin,dc=example,dc=com"; -const ADMIN_PASSWORD = "password"; - -describe("ldap/connector/LdapUsersDatabase", function() { - let sessionFactory: SessionFactoryStub; - let usersDatabase: LdapUsersDatabase; - - const USERNAME = "user"; - const PASSWORD = "pass"; - const NEW_PASSWORD = "pass2"; - - const LDAP_CONFIG = { - url: "http://localhost:324", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={0}", - mail_attribute: "mail", - group_name_attribute: "cn", - user: ADMIN_USER_DN, - password: ADMIN_PASSWORD - }; - - beforeEach(function() { - sessionFactory = new SessionFactoryStub(); - usersDatabase = new LdapUsersDatabase(sessionFactory, LDAP_CONFIG); - }) - - describe("checkUserPassword", function() { - it("should return groups and emails when user/password matches", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME).returns(Bluebird.resolve(emails)); - adminSession.searchGroupsStub.withArgs(USERNAME).returns(Bluebird.resolve(groups)); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => { - Assert.deepEqual(groupsAndEmails.groups, groups); - Assert.deepEqual(groupsAndEmails.emails, emails); - }) - }); - - it("should fail when username/password is wrong", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - - userSession.openStub.returns(Bluebird.reject(new Error("Failed binding"))); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when admin binding fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.reject(new Error("Failed binding"))); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.notCalled); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when search for user dn fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.reject(new Error("Failed searching user dn"))); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.notCalled); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when groups retrieval fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME) - .returns(Bluebird.resolve(emails)); - adminSession.searchGroupsStub.withArgs(USERNAME) - .returns(Bluebird.reject(new Error("Failed retrieving groups"))); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - }) - }); - - it("should fail when emails retrieval fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME) - .returns(Bluebird.reject(new Error("Emails retrieval failed"))); - adminSession.searchGroupsStub.withArgs(USERNAME) - .returns(Bluebird.resolve(groups)); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - }) - }); - }); - - describe("getEmails", function() { - it("should succefully retrieves email", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.resolve(emails)); - - return usersDatabase.getEmails(USERNAME) - .then((foundEmails) => { - Assert(session.closeStub.called); - Assert.deepEqual(foundEmails, emails); - }) - }); - - it("should fail when binding fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbinding fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.resolve(emails)); - session.closeStub.returns(Bluebird.reject(new Error("Unbinding failed"))); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when search fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.reject(new Error("Search failed"))); - session.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - }); - - - describe("getGroups", function() { - it("should succefully retrieves groups", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.resolve(groups)); - - return usersDatabase.getGroups(USERNAME) - .then((foundGroups) => { - Assert(session.closeStub.called); - Assert.deepEqual(foundGroups, groups); - }) - }); - - it("should fail when binding fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbinding fails", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.resolve(groups)); - session.closeStub.returns(Bluebird.reject(new Error("Unbinding failed"))); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when search fails", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.reject(new Error("Search failed"))); - session.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - }); - - - describe("updatePassword", function() { - it("should successfully update password", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => { - Assert(session.modifyPasswordStub.calledWith(USERNAME, NEW_PASSWORD)); - Assert(session.closeStub.called); - }) - }); - - it("should fail when binding fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when update fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.reject(new Error("Update failed"))); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbind fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.reject(new Error("Unbind failed"))); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts b/themes/squares/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts deleted file mode 100644 index edda62ec6..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts +++ /dev/null @@ -1,107 +0,0 @@ -import Bluebird = require("bluebird"); -import { IUsersDatabase } from "../IUsersDatabase"; -import { ISessionFactory } from "./ISessionFactory"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { ISession } from "./ISession"; -import { GroupsAndEmails } from "../GroupsAndEmails"; -import Exceptions = require("../../../Exceptions"); - -type SessionCallback = (session: ISession) => Bluebird; - -export class LdapUsersDatabase implements IUsersDatabase { - private sessionFactory: ISessionFactory; - private configuration: LdapConfiguration; - - constructor( - sessionFactory: ISessionFactory, - configuration: LdapConfiguration) { - this.sessionFactory = sessionFactory; - this.configuration = configuration; - } - - private withSession( - username: string, - password: string, - cb: SessionCallback): Bluebird { - const session = this.sessionFactory.create(username, password); - return session.open() - .then(() => cb(session)) - .finally(() => session.close()); - } - - checkUserPassword(username: string, password: string): Bluebird { - const that = this; - function verifyUserPassword(userDN: string) { - return that.withSession( - userDN, - password, - (session) => Bluebird.resolve() - ); - } - - function getInfo(session: ISession) { - return Bluebird.join( - session.searchGroups(username), - session.searchEmails(username) - ) - .spread((groups: string[], emails: string[]) => { - return { groups: groups, emails: emails }; - }); - } - - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchUserDn(username) - .then(verifyUserPassword) - .then(() => getInfo(session)); - }) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError(err.message))); - } - - getEmails(username: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchEmails(username); - } - ) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError("Failed during email retrieval: " + err.message)) - ); - } - - getGroups(username: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchGroups(username); - } - ) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError("Failed during email retrieval: " + err.message)) - ); - } - - updatePassword(username: string, newPassword: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.modifyPassword(username, newPassword); - } - ) - .catch(function (err: Error) { - return Bluebird.reject( - new Exceptions.LdapError( - "Error while updating password: " + err.message)); - }); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts b/themes/squares/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts deleted file mode 100644 index 9dedfcb7c..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { SessionStub } from "./SessionStub.spec"; -import { SafeSession } from "./SafeSession"; - -describe("ldap/SanitizedClient", function () { - let client: SafeSession; - - beforeEach(function () { - const clientStub = new SessionStub(); - clientStub.searchUserDnStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.searchGroupsStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.searchEmailsStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.modifyPasswordStub.onCall(0).returns(BluebirdPromise.resolve()); - client = new SafeSession(clientStub); - }); - - describe("special chars are used", function () { - it("should fail when special chars are used in searchUserDn", function () { - // potential ldap injection"; - return client.searchUserDn("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in searchGroups", function () { - // potential ldap injection"; - return client.searchGroups("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in searchEmails", function () { - // potential ldap injection"; - return client.searchEmails("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in modifyPassword", function () { - // potential ldap injection"; - return client.modifyPassword("cn=dummy_user,ou=groupgs", "abc") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - }); - - describe("no special chars are used", function() { - it("should succeed when no special chars are used in searchUserDn", function () { - return client.searchUserDn("dummy_user"); - }); - - it("should succeed when no special chars are used in searchGroups", function () { - return client.searchGroups("dummy_user"); - }); - - it("should succeed when no special chars are used in searchEmails", function () { - return client.searchEmails("dummy_user"); - }); - - it("should succeed when no special chars are used in modifyPassword", function () { - return client.modifyPassword("dummy_user", "abc"); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/SafeSession.ts b/themes/squares/server/src/lib/authentication/backends/ldap/SafeSession.ts deleted file mode 100644 index 572209066..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/SafeSession.ts +++ /dev/null @@ -1,62 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ISession } from "./ISession"; -import { Sanitizer } from "./Sanitizer"; - -const SPECIAL_CHAR_USED_MESSAGE = "Special character used in LDAP query."; - - -export class SafeSession implements ISession { - private sesion: ISession; - - constructor(sesion: ISession) { - this.sesion = sesion; - } - - open(): BluebirdPromise { - return this.sesion.open(); - } - - close(): BluebirdPromise { - return this.sesion.close(); - } - - searchGroups(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchGroups(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - searchUserDn(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchUserDn(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - searchEmails(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchEmails(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - modifyPassword(username: string, newPassword: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.modifyPassword(sanitizedUsername, newPassword); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } -} diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts b/themes/squares/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts deleted file mode 100644 index 9dd33fed4..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import Assert = require("assert"); -import { Sanitizer } from "./Sanitizer"; - -describe("ldap/InputsSanitizer", function () { - it("should fail when special characters are used", function () { - Assert.throws(() => { Sanitizer.sanitize("ab,c"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a\\bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a'bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a#bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a+bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a { Sanitizer.sanitize("a>bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a;bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a\"bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a=bc"); }, Error); - }); - - it("should return original string", function () { - Assert.equal(Sanitizer.sanitize("abcdef"), "abcdef"); - }); - - it("should trim", function () { - Assert.throws(() => { Sanitizer.sanitize(" abc "); }, Error); - }); -}); diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/Sanitizer.ts b/themes/squares/server/src/lib/authentication/backends/ldap/Sanitizer.ts deleted file mode 100644 index be74132ad..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/Sanitizer.ts +++ /dev/null @@ -1,25 +0,0 @@ - -// returns true for 1 or more matches, where 'a' is an array and 'b' is a search string or an array of multiple search strings -function contains(a: string, character: string) { - // string match - return a.indexOf(character) > -1; -} - -function containsOneOf(s: string, characters: string[]) { - return characters - .map((character: string) => { return contains(s, character); }) - .reduce((acc: boolean, current: boolean) => { return acc || current; }, false); -} - -export class Sanitizer { - static sanitize(input: string): string { - const forbiddenChars = [",", "\\", "'", "#", "+", "<", ">", ";", "\"", "="]; - if (containsOneOf(input, forbiddenChars)) - throw new Error("Input containing unsafe characters."); - - if (input != input.trim()) - throw new Error("Input has unexpected spaces."); - - return input; - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/Session.spec.ts b/themes/squares/server/src/lib/authentication/backends/ldap/Session.spec.ts deleted file mode 100644 index d55f6a805..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/Session.spec.ts +++ /dev/null @@ -1,127 +0,0 @@ - -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Session } from "./Session"; -import { ConnectorFactoryStub } from "./connector/ConnectorFactoryStub.spec"; -import { ConnectorStub } from "./connector/ConnectorStub.spec"; - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import Winston = require("winston"); - -describe("ldap/Session", function () { - const USERNAME = "username"; - const ADMIN_USER_DN = "cn=admin,dc=example,dc=com"; - const ADMIN_PASSWORD = "password"; - - it("should replace {0} by username when searching for groups in LDAP", function () { - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member=cn={0},ou=users,dc=example,dc=com", - group_name_attribute: "cn", - mail_attribute: "mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const connectorStub = new ConnectorStub(); - connectorStub.searchAsyncStub.returns(BluebirdPromise.resolve([{ - cn: "group1" - }])); - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, connectorStub, Winston); - - return client.searchGroups("user1") - .then(function () { - Assert.equal(connectorStub.searchAsyncStub.getCall(0).args[1].filter, - "member=cn=user1,ou=users,dc=example,dc=com"); - }); - }); - - it("should replace {dn} by user DN when searching for groups in LDAP", function () { - const USER_DN = "cn=user1,ou=users,dc=example,dc=com"; - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn", - mail_attribute: "mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const ldapClient = new ConnectorStub(); - - // Retrieve user DN - ldapClient.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: "cn=user1" - }).returns(BluebirdPromise.resolve([{ - dn: USER_DN - }])); - - // Retrieve groups - ldapClient.searchAsyncStub.withArgs("ou=groups,dc=example,dc=com", { - scope: "sub", - attributes: ["cn"], - filter: "member=" + USER_DN - }).returns(BluebirdPromise.resolve([{ - cn: "group1" - }])); - - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, ldapClient, Winston); - - return client.searchGroups("user1") - .then(function (groups: string[]) { - Assert.deepEqual(groups, ["group1"]); - }); - }); - - it("should retrieve mail from custom attribute", function () { - const USER_DN = "cn=user1,ou=users,dc=example,dc=com"; - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn", - mail_attribute: "custom_mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const connector = new ConnectorStub(); - // Retrieve user DN - connector.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: "cn=user1" - }).returns(BluebirdPromise.resolve([{ - dn: USER_DN - }])); - - // Retrieve email - connector.searchAsyncStub.withArgs("cn=user1,ou=users,dc=example,dc=com", { - scope: "base", - sizeLimit: 1, - attributes: ["custom_mail"], - }).returns(BluebirdPromise.resolve([{ - custom_mail: "user1@example.com" - }])); - - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, connector, Winston); - - return client.searchEmails("user1") - .then(function (emails: string[]) { - Assert.deepEqual(emails, ["user1@example.com"]); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/Session.ts b/themes/squares/server/src/lib/authentication/backends/ldap/Session.ts deleted file mode 100644 index e0284b3c4..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/Session.ts +++ /dev/null @@ -1,156 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import exceptions = require("../../../Exceptions"); -import { EventEmitter } from "events"; -import { ISession } from "./ISession"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Winston } from "../../../../../types/Dependencies"; -import Util = require("util"); -import { HashGenerator } from "../../../utils/HashGenerator"; -import { IConnector } from "./connector/IConnector"; - -export class Session implements ISession { - private userDN: string; - private password: string; - private connector: IConnector; - private logger: Winston; - private options: LdapConfiguration; - - private groupsSearchBase: string; - private usersSearchBase: string; - - constructor(userDN: string, password: string, options: LdapConfiguration, - connector: IConnector, logger: Winston) { - this.options = options; - this.logger = logger; - this.userDN = userDN; - this.password = password; - this.connector = connector; - - this.groupsSearchBase = (this.options.additional_groups_dn) - ? Util.format("%s,%s", this.options.additional_groups_dn, this.options.base_dn) - : this.options.base_dn; - - this.usersSearchBase = (this.options.additional_users_dn) - ? Util.format("%s,%s", this.options.additional_users_dn, this.options.base_dn) - : this.options.base_dn; - } - - open(): BluebirdPromise { - this.logger.debug("LDAP: Bind user '%s'", this.userDN); - return this.connector.bindAsync(this.userDN, this.password) - .error(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapBindError(err.message)); - }); - } - - close(): BluebirdPromise { - this.logger.debug("LDAP: Unbind user '%s'", this.userDN); - return this.connector.unbindAsync() - .error(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapBindError(err.message)); - }); - } - - private createGroupsFilter(userGroupsFilter: string, username: string): BluebirdPromise { - if (userGroupsFilter.indexOf("{0}") > 0) { - return BluebirdPromise.resolve(userGroupsFilter.replace("{0}", username)); - } - else if (userGroupsFilter.indexOf("{dn}") > 0) { - return this.searchUserDn(username) - .then(function (userDN: string) { - return BluebirdPromise.resolve(userGroupsFilter.replace("{dn}", userDN)); - }); - } - return BluebirdPromise.resolve(userGroupsFilter); - } - - searchGroups(username: string): BluebirdPromise { - const that = this; - return this.createGroupsFilter(this.options.groups_filter, username) - .then(function (groupsFilter: string) { - that.logger.debug("Computed groups filter is %s", groupsFilter); - const query = { - scope: "sub", - attributes: [that.options.group_name_attribute], - filter: groupsFilter - }; - return that.connector.searchAsync(that.groupsSearchBase, query); - }) - .then(function (docs: { cn: string }[]) { - const groups = docs.map((doc: any) => { return doc.cn; }); - that.logger.debug("LDAP: groups of user %s are [%s]", username, groups.join(",")); - return BluebirdPromise.resolve(groups); - }); - } - - searchUserDn(username: string): BluebirdPromise { - const that = this; - const filter = this.options.users_filter.replace("{0}", username); - this.logger.debug("Computed users filter is %s", filter); - const query = { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: filter - }; - - that.logger.debug("LDAP: searching for user dn of %s", username); - return that.connector.searchAsync(this.usersSearchBase, query) - .then(function (users: { dn: string }[]) { - if (users.length > 0) { - that.logger.debug("LDAP: retrieved user dn is %s", users[0].dn); - return BluebirdPromise.resolve(users[0].dn); - } - return BluebirdPromise.reject(new Error( - Util.format("No user DN found for user '%s'", username))); - }); - } - - searchEmails(username: string): BluebirdPromise { - const that = this; - const query = { - scope: "base", - sizeLimit: 1, - attributes: [this.options.mail_attribute] - }; - - return this.searchUserDn(username) - .then(function (userDN) { - return that.connector.searchAsync(userDN, query); - }) - .then(function (docs: { [mail_attribute: string]: string }[]) { - const emails: string[] = docs - .filter((d) => { return typeof d[that.options.mail_attribute] === "string"; }) - .map((d) => { return d[that.options.mail_attribute]; }); - that.logger.debug("LDAP: emails of user '%s' are %s", username, emails); - return BluebirdPromise.resolve(emails); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapError("Error while searching emails. " + err.stack)); - }); - } - - modifyPassword(username: string, newPassword: string): BluebirdPromise { - const that = this; - this.logger.debug("LDAP: update password of user '%s'", username); - return this.searchUserDn(username) - .then(function (userDN: string) { - return BluebirdPromise.join( - HashGenerator.ssha512(newPassword), - BluebirdPromise.resolve(userDN)); - }) - .then(function (res: string[]) { - const change = { - operation: "replace", - modification: { - userPassword: res[0] - } - }; - that.logger.debug("Password new='%s'", change.modification.userPassword); - return that.connector.modifyAsync(res[1], change); - }) - .then(function () { - return that.connector.unbindAsync(); - }); - } -} diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/SessionFactory.ts b/themes/squares/server/src/lib/authentication/backends/ldap/SessionFactory.ts deleted file mode 100644 index 0b6c4bff3..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/SessionFactory.ts +++ /dev/null @@ -1,37 +0,0 @@ -import Ldapjs = require("ldapjs"); -import Winston = require("winston"); - -import { IConnectorFactory } from "./connector/IConnectorFactory"; -import { ISessionFactory } from "./ISessionFactory"; -import { ISession } from "./ISession"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Session } from "./Session"; -import { SafeSession } from "./SafeSession"; - - -export class SessionFactory implements ISessionFactory { - private config: LdapConfiguration; - private connectorFactory: IConnectorFactory; - private logger: typeof Winston; - - constructor(ldapConfiguration: LdapConfiguration, - connectorFactory: IConnectorFactory, - logger: typeof Winston) { - this.config = ldapConfiguration; - this.connectorFactory = connectorFactory; - this.logger = logger; - } - - create(userDN: string, password: string): ISession { - const connector = this.connectorFactory.create(); - return new SafeSession( - new Session( - userDN, - password, - this.config, - connector, - this.logger - ) - ); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts b/themes/squares/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts deleted file mode 100644 index face3930d..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); - -import { ISession } from "./ISession"; -import { ISessionFactory } from "./ISessionFactory"; - -export class SessionFactoryStub implements ISessionFactory { - createStub: Sinon.SinonStub; - - constructor() { - this.createStub = Sinon.stub(); - } - - create(userDN: string, password: string): ISession { - return this.createStub(userDN, password); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts b/themes/squares/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts deleted file mode 100644 index 5faf2ba18..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); - -import { ISession } from "./ISession"; - -export class SessionStub implements ISession { - openStub: Sinon.SinonStub; - closeStub: Sinon.SinonStub; - searchUserDnStub: Sinon.SinonStub; - searchEmailsStub: Sinon.SinonStub; - searchGroupsStub: Sinon.SinonStub; - modifyPasswordStub: Sinon.SinonStub; - - constructor() { - this.openStub = Sinon.stub(); - this.closeStub = Sinon.stub(); - this.searchUserDnStub = Sinon.stub(); - this.searchEmailsStub = Sinon.stub(); - this.searchGroupsStub = Sinon.stub(); - this.modifyPasswordStub = Sinon.stub(); - } - - open(): Bluebird { - return this.openStub(); - } - - close(): Bluebird { - return this.closeStub(); - } - - searchUserDn(username: string): Bluebird { - return this.searchUserDnStub(username); - } - - searchEmails(username: string): Bluebird { - return this.searchEmailsStub(username); - } - - searchGroups(username: string): Bluebird { - return this.searchGroupsStub(username); - } - - modifyPassword(username: string, newPassword: string): Bluebird { - return this.modifyPasswordStub(username, newPassword); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/connector/Connector.ts b/themes/squares/server/src/lib/authentication/backends/ldap/connector/Connector.ts deleted file mode 100644 index 2542ea7f9..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/connector/Connector.ts +++ /dev/null @@ -1,69 +0,0 @@ -import LdapJs = require("ldapjs"); -import EventEmitter = require("events"); -import Bluebird = require("bluebird"); -import { IConnector } from "./IConnector"; -import Exceptions = require("../../../../Exceptions"); - -interface SearchEntry { - object: any; -} - -export interface ClientAsync { - on(event: string, callback: (data?: any) => void): void; - bindAsync(username: string, password: string): Bluebird; - unbindAsync(): Bluebird; - searchAsync(base: string, query: LdapJs.SearchOptions): Bluebird; - modifyAsync(userdn: string, change: LdapJs.Change): Bluebird; -} - -export class Connector implements IConnector { - private client: ClientAsync; - - constructor(url: string, ldapjs: typeof LdapJs) { - const ldapClient = ldapjs.createClient({ - url: url, - reconnect: true - }); - - /*const clientLogger = (ldapClient as any).log; - if (clientLogger) { - clientLogger.level("trace"); - }*/ - - this.client = Bluebird.promisifyAll(ldapClient) as any; - } - - bindAsync(username: string, password: string): Bluebird { - return this.client.bindAsync(username, password); - } - - unbindAsync(): Bluebird { - return this.client.unbindAsync(); - } - - searchAsync(base: string, query: any): Bluebird { - const that = this; - return this.client.searchAsync(base, query) - .then(function (res: EventEmitter) { - const doc: SearchEntry[] = []; - return new Bluebird((resolve, reject) => { - res.on("searchEntry", function (entry: SearchEntry) { - doc.push(entry.object); - }); - res.on("error", function (err: Error) { - reject(new Exceptions.LdapSearchError(err.message)); - }); - res.on("end", function () { - resolve(doc); - }); - }); - }) - .catch(function (err: Error) { - return Bluebird.reject(new Exceptions.LdapSearchError(err.message)); - }); - } - - modifyAsync(dn: string, changeRequest: any): Bluebird { - return this.client.modifyAsync(dn, changeRequest); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts b/themes/squares/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts deleted file mode 100644 index 61fef07a4..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IConnector } from "./IConnector"; -import { Connector } from "./Connector"; -import { LdapConfiguration } from "../../../../configuration/schema/LdapConfiguration"; -import { Ldapjs } from "Dependencies"; - -export class ConnectorFactory { - private configuration: LdapConfiguration; - private ldapjs: Ldapjs; - - constructor(configuration: LdapConfiguration, ldapjs: Ldapjs) { - this.configuration = configuration; - this.ldapjs = ldapjs; - } - - create(): IConnector { - return new Connector(this.configuration.url, this.ldapjs); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts b/themes/squares/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts deleted file mode 100644 index d11fa6386..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); - -import { IConnectorFactory } from "./IConnectorFactory"; -import { IConnector } from "./IConnector"; - -export class ConnectorFactoryStub implements IConnectorFactory { - createStub: Sinon.SinonStub; - - constructor() { - this.createStub = Sinon.stub(); - } - - create(): IConnector { - return this.createStub(); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts b/themes/squares/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts deleted file mode 100644 index 0b78225bf..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); - -import { IConnector } from "./IConnector"; - -export class ConnectorStub implements IConnector { - bindAsyncStub: Sinon.SinonStub; - unbindAsyncStub: Sinon.SinonStub; - searchAsyncStub: Sinon.SinonStub; - modifyAsyncStub: Sinon.SinonStub; - - constructor() { - this.bindAsyncStub = Sinon.stub(); - this.unbindAsyncStub = Sinon.stub(); - this.searchAsyncStub = Sinon.stub(); - this.modifyAsyncStub = Sinon.stub(); - } - - bindAsync(username: string, password: string): BluebirdPromise { - return this.bindAsyncStub(username, password); - } - - unbindAsync(): BluebirdPromise { - return this.unbindAsyncStub(); - } - - searchAsync(base: string, query: any): BluebirdPromise { - return this.searchAsyncStub(base, query); - } - - modifyAsync(dn: string, changeRequest: any): BluebirdPromise { - return this.modifyAsyncStub(dn, changeRequest); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/connector/IConnector.ts b/themes/squares/server/src/lib/authentication/backends/ldap/connector/IConnector.ts deleted file mode 100644 index 1e63ab193..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/connector/IConnector.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Bluebird = require("bluebird"); -import EventEmitter = require("events"); - -export interface IConnector { - bindAsync(username: string, password: string): Bluebird; - unbindAsync(): Bluebird; - searchAsync(base: string, query: any): Bluebird; - modifyAsync(dn: string, changeRequest: any): Bluebird; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts b/themes/squares/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts deleted file mode 100644 index f9ed65eff..000000000 --- a/themes/squares/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { IConnector } from "./IConnector"; - -export interface IConnectorFactory { - create(): IConnector; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/totp/ITotpHandler.ts b/themes/squares/server/src/lib/authentication/totp/ITotpHandler.ts deleted file mode 100644 index d600d31e4..000000000 --- a/themes/squares/server/src/lib/authentication/totp/ITotpHandler.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { TOTPSecret } from "../../../../types/TOTPSecret"; - -export interface ITotpHandler { - generate(label: string, issuer: string): TOTPSecret; - validate(token: string, secret: string): boolean; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/totp/TotpHandler.spec.ts b/themes/squares/server/src/lib/authentication/totp/TotpHandler.spec.ts deleted file mode 100644 index 67cffa638..000000000 --- a/themes/squares/server/src/lib/authentication/totp/TotpHandler.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import { TotpHandler } from "./TotpHandler"; -import Sinon = require("sinon"); -import Speakeasy = require("speakeasy"); -import Assert = require("assert"); - -describe("authentication/totp/TotpHandler", function() { - let totpValidator: TotpHandler; - let validateStub: Sinon.SinonStub; - - beforeEach(() => { - validateStub = Sinon.stub(Speakeasy.totp, "verify"); - totpValidator = new TotpHandler(Speakeasy); - }); - - afterEach(function() { - validateStub.restore(); - }); - - it("should validate the TOTP token", function() { - const totp_secret = "NBD2ZV64R9UV1O7K"; - const token = "token"; - validateStub.withArgs({ - secret: totp_secret, - token: token, - encoding: "base32", - window: 1 - }).returns(true); - Assert(totpValidator.validate(token, totp_secret)); - }); - - it("should not validate a wrong TOTP token", function() { - const totp_secret = "NBD2ZV64R9UV1O7K"; - const token = "wrong token"; - validateStub.returns(false); - Assert(!totpValidator.validate(token, totp_secret)); - }); -}); - diff --git a/themes/squares/server/src/lib/authentication/totp/TotpHandler.ts b/themes/squares/server/src/lib/authentication/totp/TotpHandler.ts deleted file mode 100644 index dfab502a7..000000000 --- a/themes/squares/server/src/lib/authentication/totp/TotpHandler.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ITotpHandler } from "./ITotpHandler"; -import { TOTPSecret } from "../../../../types/TOTPSecret"; -import Speakeasy = require("speakeasy"); - -const TOTP_ENCODING = "base32"; -const WINDOW: number = 1; - -export class TotpHandler implements ITotpHandler { - private speakeasy: typeof Speakeasy; - - constructor(speakeasy: typeof Speakeasy) { - this.speakeasy = speakeasy; - } - - generate(label: string, issuer: string): TOTPSecret { - const secret = this.speakeasy.generateSecret({ - otpauth_url: false - }) as TOTPSecret; - - secret.otpauth_url = this.speakeasy.otpauthURL({ - secret: secret.ascii, - label: label, - issuer: issuer - }); - return secret; - } - - validate(token: string, secret: string): boolean { - return this.speakeasy.totp.verify({ - secret: secret, - encoding: TOTP_ENCODING, - token: token, - window: WINDOW - }); - } -} diff --git a/themes/squares/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts b/themes/squares/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts deleted file mode 100644 index ea93330d9..000000000 --- a/themes/squares/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import { ITotpHandler } from "./ITotpHandler"; -import { TOTPSecret } from "../../../../types/TOTPSecret"; - -export class TotpHandlerStub implements ITotpHandler { - generateStub: Sinon.SinonStub; - validateStub: Sinon.SinonStub; - - constructor() { - this.generateStub = Sinon.stub(); - this.validateStub = Sinon.stub(); - } - - generate(label: string, issuer: string): TOTPSecret { - return this.generateStub(label, issuer); - } - - validate(token: string, secret: string): boolean { - return this.validateStub(token, secret); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/u2f/IU2fHandler.ts b/themes/squares/server/src/lib/authentication/u2f/IU2fHandler.ts deleted file mode 100644 index b9b7d6f26..000000000 --- a/themes/squares/server/src/lib/authentication/u2f/IU2fHandler.ts +++ /dev/null @@ -1,9 +0,0 @@ -import U2f = require("u2f"); - -export interface IU2fHandler { - request(appId: string, keyHandle?: string): U2f.Request; - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error; - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authentication/u2f/U2fHandler.ts b/themes/squares/server/src/lib/authentication/u2f/U2fHandler.ts deleted file mode 100644 index bf3891e5b..000000000 --- a/themes/squares/server/src/lib/authentication/u2f/U2fHandler.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { IU2fHandler } from "./IU2fHandler"; -import U2f = require("u2f"); - -export class U2fHandler implements IU2fHandler { - private u2f: typeof U2f; - - constructor(u2f: typeof U2f) { - this.u2f = u2f; - } - - request(appId: string, keyHandle?: string): U2f.Request { - return this.u2f.request(appId, keyHandle); - } - - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error { - return this.u2f.checkRegistration(registrationRequest, registrationResponse); - } - - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error { - return this.u2f.checkSignature(signatureRequest, signatureResponse, publicKey); - } -} diff --git a/themes/squares/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts b/themes/squares/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts deleted file mode 100644 index 135d7eb06..000000000 --- a/themes/squares/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import U2f = require("u2f"); -import { IU2fHandler } from "./IU2fHandler"; - - -export class U2fHandlerStub implements IU2fHandler { - requestStub: Sinon.SinonStub; - checkRegistrationStub: Sinon.SinonStub; - checkSignatureStub: Sinon.SinonStub; - - constructor() { - this.requestStub = Sinon.stub(); - this.checkRegistrationStub = Sinon.stub(); - this.checkSignatureStub = Sinon.stub(); - } - - request(appId: string, keyHandle?: string): U2f.Request { - return this.requestStub(appId, keyHandle); - } - - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error { - return this.checkRegistrationStub(registrationRequest, registrationResponse); - } - - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error { - return this.checkSignatureStub(signatureRequest, signatureResponse, publicKey); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authorization/Authorizer.spec.ts b/themes/squares/server/src/lib/authorization/Authorizer.spec.ts deleted file mode 100644 index 586814042..000000000 --- a/themes/squares/server/src/lib/authorization/Authorizer.spec.ts +++ /dev/null @@ -1,372 +0,0 @@ - -import Assert = require("assert"); -import winston = require("winston"); -import { Authorizer } from "./Authorizer"; -import { ACLConfiguration, ACLRule } from "../configuration/schema/AclConfiguration"; -import { Level } from "./Level"; - -describe("authorization/Authorizer", function () { - let authorizer: Authorizer; - let configuration: ACLConfiguration; - - describe("configuration is null", function() { - it("should allow access to anything, anywhere for anybody", function() { - configuration = undefined; - authorizer = new Authorizer(configuration, winston); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc"}, {user: "user1", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user2", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "admin.example.com", resource: "/"}, {user: "user3", groups: ["group3"]}), Level.BYPASS); - }); - }); - - describe("configuration is not null", function () { - beforeEach(function () { - configuration = { - default_policy: "deny", - rules: [] - }; - authorizer = new Authorizer(configuration, winston); - }); - - describe("check access control with default policy to deny", function () { - beforeEach(function () { - configuration.default_policy = "deny"; - }); - - it("should deny access when no rule is provided", function () { - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should control access when multiple domain matcher is provided", function () { - configuration.rules = [{ - domain: "*.mail.example.com", - policy: "two_factor", - subject: "user:user1", - resources: [".*"] - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "mx1.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mx1.server.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should allow access to all resources when resources is not provided", function () { - configuration.rules = [{ - domain: "*.mail.example.com", - policy: "two_factor", - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "mx1.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mx1.server.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - describe("check user rules", function () { - it("should allow access when user has a matching allowing rule", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/another/resource"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should deny to other users", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user2", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/another/resource"}, {user: "user2", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, {user: "user2", groups: ["group1"]}), Level.DENY); - }); - - it("should allow user access only to specific resources", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["/private/.*", "^/begin", "/end$"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/class"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/middle/private/class"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/begin"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/not/begin"}, {user: "user1", groups: ["group1"]}), Level.DENY); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc/end"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc/end/x"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should allow access to multiple domains", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }, { - domain: "home1.example.com", - policy: "one_factor", - resources: [".*"], - subject: "user:user1" - }, { - domain: "home2.example.com", - policy: "deny", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home1.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.ONE_FACTOR); - Assert.equal(authorizer.authorization({domain: "home2.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home3.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should apply rules in order", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "one_factor", - resources: ["/my/private/resource"], - subject: "user:user1" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/my/private/.*"], - subject: "user:user1" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/my/.*"], - subject: "user:user1" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/poney"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/private/duck"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/private/resource"}, {user: "user1", groups: ["group1"]}), Level.ONE_FACTOR); - }); - }); - - describe("check group rules", function () { - it("should allow access when user is in group having a matching allowing rule", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/$"], - subject: "group:group1" - }, { - domain: "home.example.com", - policy: "one_factor", - resources: ["^/test$"], - subject: "group:group2" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/private$"], - subject: "group:group2" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.ONE_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - }); - }); - }); - - describe("check any rules", function () { - it("should control access when any rules are defined", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "bypass", - resources: ["^/public$"] - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/private$"] - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, - {user: "user4", groups: ["group5"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user4", groups: ["group5"]}), Level.DENY); - }); - }); - - describe("check access control with default policy to allow", function () { - beforeEach(function () { - configuration.default_policy = "bypass"; - }); - - it("should allow access to anything when no rule is provided", function () { - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - }); - - it("should deny access to one resource when defined", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["/test"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - }); - }); - - describe("check access control with complete use case", function () { - beforeEach(function () { - configuration.default_policy = "deny"; - }); - - it("should control access of multiple user (real use case)", function () { - // Let say we have three users: admin, john, harry. - // admin is in groups ["admins"] - // john is in groups ["dev", "admin-private"] - // harry is in groups ["dev"] - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/public$", "^/$"] - }, { - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "group:admins" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/?.*"], - subject: "group:admin-private" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/john$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/harry"], - subject: "user:harry" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - }); - - it("should allow when allowed at group level and denied at user level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"], - subject: "group:dev" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should allow access when allowed at 'any' level and denied at user level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should allow access when allowed at 'any' level and denied at group level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "group:dev" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should respect rules precedence", function () { - // the priority from least to most is 'default_policy', 'all', 'group', 'user' - // and the first rules in each category as a lower priority than the latest. - // You can think of it that way: they override themselves inside each category. - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "group:dev" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - }); - }); - }); -}); diff --git a/themes/squares/server/src/lib/authorization/Authorizer.ts b/themes/squares/server/src/lib/authorization/Authorizer.ts deleted file mode 100644 index 889b7ec20..000000000 --- a/themes/squares/server/src/lib/authorization/Authorizer.ts +++ /dev/null @@ -1,85 +0,0 @@ - -import { ACLConfiguration, ACLPolicy, ACLRule } from "../configuration/schema/AclConfiguration"; -import { IAuthorizer } from "./IAuthorizer"; -import { Winston } from "../../../types/Dependencies"; -import { MultipleDomainMatcher } from "./MultipleDomainMatcher"; -import { Level } from "./Level"; -import { Object } from "./Object"; -import { Subject } from "./Subject"; - -function MatchDomain(actualDomain: string) { - return function (rule: ACLRule): boolean { - return MultipleDomainMatcher.match(actualDomain, rule.domain); - }; -} - -function MatchResource(actualResource: string) { - return function (rule: ACLRule): boolean { - // If resources key is not provided, the rule applies to all resources. - if (!rule.resources) return true; - - for (let i = 0; i < rule.resources.length; ++i) { - const regexp = new RegExp(rule.resources[i]); - if (regexp.test(actualResource)) return true; - } - return false; - }; -} - -function MatchSubject(subject: Subject) { - return (rule: ACLRule) => { - // If no subject, matches anybody - if (!rule.subject) return true; - - if (rule.subject.startsWith("user:")) { - const ruleUser = rule.subject.split(":")[1]; - if (subject.user == ruleUser) return true; - } - - if (rule.subject.startsWith("group:")) { - const ruleGroup = rule.subject.split(":")[1]; - if (subject.groups.indexOf(ruleGroup) > -1) return true; - } - return false; - }; -} - -export class Authorizer implements IAuthorizer { - private logger: Winston; - private readonly configuration: ACLConfiguration; - - constructor(configuration: ACLConfiguration, logger_: Winston) { - this.logger = logger_; - this.configuration = configuration; - } - - private getMatchingRules(object: Object, subject: Subject): ACLRule[] { - const rules = this.configuration.rules; - if (!rules) return []; - return rules - .filter(MatchDomain(object.domain)) - .filter(MatchResource(object.resource)) - .filter(MatchSubject(subject)); - } - - private ruleToLevel(policy: string): Level { - if (policy == "bypass") { - return Level.BYPASS; - } else if (policy == "one_factor") { - return Level.ONE_FACTOR; - } else if (policy == "two_factor") { - return Level.TWO_FACTOR; - } - return Level.DENY; - } - - authorization(object: Object, subject: Subject): Level { - if (!this.configuration) return Level.BYPASS; - - const rules = this.getMatchingRules(object, subject); - - return (rules.length > 0) - ? this.ruleToLevel(rules[0].policy) // extract the policy of the first matching rule - : this.ruleToLevel(this.configuration.default_policy); // otherwise use the default policy - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authorization/AuthorizerStub.spec.ts b/themes/squares/server/src/lib/authorization/AuthorizerStub.spec.ts deleted file mode 100644 index 9bd6f4a85..000000000 --- a/themes/squares/server/src/lib/authorization/AuthorizerStub.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import Sinon = require("sinon"); -import { IAuthorizer } from "./IAuthorizer"; -import { Level } from "./Level"; -import { Object } from "./Object"; -import { Subject } from "./Subject"; - -export class AuthorizerStub implements IAuthorizer { - authorizationMock: Sinon.SinonStub; - - constructor() { - this.authorizationMock = Sinon.stub(); - } - - authorization(object: Object, subject: Subject): Level { - return this.authorizationMock(object, subject); - } -} diff --git a/themes/squares/server/src/lib/authorization/IAuthorizer.ts b/themes/squares/server/src/lib/authorization/IAuthorizer.ts deleted file mode 100644 index fe7ba367a..000000000 --- a/themes/squares/server/src/lib/authorization/IAuthorizer.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Level } from "./Level"; -import { Subject } from "./Subject"; -import { Object } from "./Object"; - -export interface IAuthorizer { - authorization(object: Object, subject: Subject): Level; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authorization/Level.ts b/themes/squares/server/src/lib/authorization/Level.ts deleted file mode 100644 index d12802610..000000000 --- a/themes/squares/server/src/lib/authorization/Level.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum Level { - BYPASS = 0, - ONE_FACTOR = 1, - TWO_FACTOR = 2, - DENY = 3 -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authorization/MultipleDomainMatcher.ts b/themes/squares/server/src/lib/authorization/MultipleDomainMatcher.ts deleted file mode 100644 index 64c647a4a..000000000 --- a/themes/squares/server/src/lib/authorization/MultipleDomainMatcher.ts +++ /dev/null @@ -1,12 +0,0 @@ - -export class MultipleDomainMatcher { - static match(domain: string, pattern: string): boolean { - if (pattern.startsWith("*") && - domain.endsWith(pattern.substr(1))) { - return true; - } - else if (domain == pattern) { - return true; - } - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authorization/Object.ts b/themes/squares/server/src/lib/authorization/Object.ts deleted file mode 100644 index 5411b0d24..000000000 --- a/themes/squares/server/src/lib/authorization/Object.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface Object { - domain: string; - resource: string; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/authorization/Subject.ts b/themes/squares/server/src/lib/authorization/Subject.ts deleted file mode 100644 index 310d6b4c0..000000000 --- a/themes/squares/server/src/lib/authorization/Subject.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface Subject { - user: string; - groups: string[]; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/ConfigurationParser.spec.ts b/themes/squares/server/src/lib/configuration/ConfigurationParser.spec.ts deleted file mode 100644 index 60c0f6182..000000000 --- a/themes/squares/server/src/lib/configuration/ConfigurationParser.spec.ts +++ /dev/null @@ -1,171 +0,0 @@ -import * as Assert from "assert"; -import { Configuration } from "./schema/Configuration"; -import { ACLConfiguration } from "./schema/AclConfiguration"; -import { ConfigurationParser } from "./ConfigurationParser"; - -describe("configuration/ConfigurationParser", function () { - function buildYamlConfig(): Configuration { - const yaml_config: Configuration = { - port: 8080, - authentication_backend: { - ldap: { - url: "http://ldap", - base_dn: "dc=example,dc=com", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - user: "user", - password: "pass" - }, - }, - session: { - domain: "example.com", - secret: "secret", - expiration: 40000 - }, - storage: { - local: { - path: "/mydirectory" - } - }, - regulation: { - max_retries: 3, - find_time: 5 * 60, - ban_time: 5 * 60 - }, - logs_level: "debug", - notifier: { - email: { - username: "user", - password: "password", - sender: "admin@example.com", - service: "gmail" - } - } - }; - return yaml_config; - } - - describe("port", function () { - it("should read the port from the yaml file", function () { - const yaml_config = buildYamlConfig(); - yaml_config.port = 7070; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.port, 7070); - }); - - it("should default the port to 8080 if not provided", function () { - const yaml_config = buildYamlConfig(); - delete yaml_config.port; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.port, 8080); - }); - }); - - describe("test session configuration", function() { - it("should get the session attributes", function () { - const yaml_config = buildYamlConfig(); - yaml_config.session = { - domain: "example.com", - secret: "secret", - expiration: 3600, - inactivity: 4000 - }; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.session.domain, "example.com"); - Assert.equal(config.session.secret, "secret"); - Assert.equal(config.session.expiration, 3600); - Assert.equal(config.session.inactivity, 4000); - }); - - it("should be ok not specifying inactivity", function () { - const yaml_config = buildYamlConfig(); - yaml_config.session = { - domain: "example.com", - secret: "secret", - expiration: 3600 - }; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.session.domain, "example.com"); - Assert.equal(config.session.secret, "secret"); - Assert.equal(config.session.expiration, 3600); - Assert.equal(config.session.inactivity, undefined); - }); - }); - - it("should get the log level", function () { - const yaml_config = buildYamlConfig(); - yaml_config.logs_level = "debug"; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.logs_level, "debug"); - }); - - it("should get the notifier config", function () { - const userConfig = buildYamlConfig(); - userConfig.notifier = { - email: { - username: "user", - password: "pass", - sender: "admin@example.com", - service: "gmail" - } - }; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.notifier, { - email: { - username: "user", - password: "pass", - sender: "admin@example.com", - service: "gmail" - } - }); - }); - - describe("access_control", function() { - it("should adapt access_control when it is already ok", function () { - const userConfig = buildYamlConfig(); - userConfig.access_control = { - default_policy: "deny", - rules: [{ - domain: "www.example.com", - policy: "two_factor", - subject: "user:user" - }, { - domain: "public.example.com", - policy: "two_factor" - }] - }; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.access_control, { - default_policy: "deny", - rules: [{ - domain: "www.example.com", - policy: "two_factor", - subject: "user:user" - }, { - domain: "public.example.com", - policy: "two_factor" - }] - } as ACLConfiguration); - }); - - - it("should adapt access_control when it is empty", function () { - const userConfig = buildYamlConfig(); - userConfig.access_control = {} as any; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.access_control, { - default_policy: "bypass", - rules: [] - }); - }); - }); - - describe("default_redirection_url", function() { - it("should parse default_redirection_url", function() { - const userConfig = buildYamlConfig(); - userConfig.default_redirection_url = "dummy_url"; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.default_redirection_url, "dummy_url"); - }); - }); -}); diff --git a/themes/squares/server/src/lib/configuration/ConfigurationParser.ts b/themes/squares/server/src/lib/configuration/ConfigurationParser.ts deleted file mode 100644 index d92d163c4..000000000 --- a/themes/squares/server/src/lib/configuration/ConfigurationParser.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import * as ObjectPath from "object-path"; -import { Configuration, complete } from "./schema/Configuration"; -import Ajv = require("ajv"); -import Path = require("path"); -import Util = require("util"); - -export class ConfigurationParser { - private static parseTypes(configuration: Configuration): string[] { - const schema = require(Path.resolve(__dirname, "./Configuration.schema.json")); - const ajv = new Ajv({ - allErrors: true, - missingRefs: "fail" - }); - ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-06.json")); - const valid = ajv.validate(schema, configuration); - if (!valid) - return ajv.errors.map( - (e: Ajv.ErrorObject) => { return ajv.errorsText([e]); }); - return []; - } - - static parse(configuration: Configuration): Configuration { - const validationErrors = this.parseTypes(configuration); - if (validationErrors.length > 0) { - validationErrors.forEach((e: string) => { console.log(e); }); - throw new Error("Malformed configuration (schema). Please double-check your configuration file."); - } - - const [newConfiguration, completionErrors] = complete(configuration); - - if (completionErrors.length > 0) { - completionErrors.forEach((e: string) => { console.log(e); }); - throw new Error("Malformed configuration (validator). Please double-check your configuration file."); - } - return newConfiguration; - } -} - diff --git a/themes/squares/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts b/themes/squares/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts deleted file mode 100644 index d4a3093ee..000000000 --- a/themes/squares/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { SessionConfigurationBuilder } from "./SessionConfigurationBuilder"; -import { Configuration } from "./schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; - -import ExpressSession = require("express-session"); -import ConnectRedis = require("connect-redis"); -import Sinon = require("sinon"); -import Assert = require("assert"); - -describe("configuration/SessionConfigurationBuilder", function () { - const configuration: Configuration = { - access_control: { - default_policy: "deny", - rules: [] - }, - totp: { - issuer: "authelia.com" - }, - authentication_backend: { - ldap: { - url: "ldap://ldap", - user: "user", - base_dn: "dc=example,dc=com", - password: "password", - additional_groups_dn: "ou=groups", - additional_users_dn: "ou=users", - group_name_attribute: "", - groups_filter: "", - mail_attribute: "", - users_filter: "" - }, - }, - logs_level: "debug", - notifier: { - filesystem: { - filename: "/test" - } - }, - port: 8080, - session: { - name: "authelia_session", - domain: "example.com", - expiration: 3600, - secret: "secret" - }, - regulation: { - max_retries: 3, - ban_time: 5 * 60, - find_time: 5 * 60 - }, - storage: { - local: { - in_memory: true - } - } - }; - - const deps: GlobalDependencies = { - ConnectRedis: Sinon.spy() as any, - ldapjs: Sinon.spy() as any, - nedb: Sinon.spy() as any, - session: Sinon.spy() as any, - speakeasy: Sinon.spy() as any, - u2f: Sinon.spy() as any, - winston: Sinon.spy() as any, - Redis: Sinon.spy() as any - }; - - it("should return session options without redis options", function () { - const options = SessionConfigurationBuilder.build(configuration, deps); - const expectedOptions = { - name: "authelia_session", - secret: "secret", - resave: false, - saveUninitialized: true, - cookie: { - secure: true, - httpOnly: true, - maxAge: 3600, - domain: "example.com" - } - }; - - Assert.deepEqual(expectedOptions, options); - }); - - it("should return session options with redis options", function () { - configuration.session["redis"] = { - host: "redis.example.com", - port: 6379 - }; - const RedisStoreMock = Sinon.spy(); - const redisClient = Sinon.mock().returns({ on: Sinon.spy() }); - - deps.ConnectRedis = Sinon.stub().returns(RedisStoreMock) as any; - deps.Redis = { - createClient: Sinon.mock().returns(redisClient) - } as any; - - const options = SessionConfigurationBuilder.build(configuration, deps); - - const expectedOptions: ExpressSession.SessionOptions = { - secret: "secret", - resave: false, - saveUninitialized: true, - name: "authelia_session", - cookie: { - secure: true, - httpOnly: true, - maxAge: 3600, - domain: "example.com" - }, - store: Sinon.match.object as any - }; - - Assert((deps.ConnectRedis as Sinon.SinonStub).calledWith(deps.session)); - Assert.equal(options.secret, expectedOptions.secret); - Assert.equal(options.resave, expectedOptions.resave); - Assert.equal(options.saveUninitialized, expectedOptions.saveUninitialized); - Assert.deepEqual(options.cookie, expectedOptions.cookie); - Assert(options.store != undefined); - }); - - it("should return session options with redis password", function () { - configuration.session["redis"] = { - host: "redis.example.com", - port: 6379, - password: "authelia_pass" - }; - const RedisStoreMock = Sinon.spy(); - const redisClient = Sinon.mock().returns({ on: Sinon.spy() }); - const createClientStub = Sinon.stub(); - - deps.ConnectRedis = Sinon.stub().returns(RedisStoreMock) as any; - deps.Redis = { - createClient: createClientStub - } as any; - - createClientStub.returns(redisClient); - - const options = SessionConfigurationBuilder.build(configuration, deps); - - Assert(createClientStub.calledWith({ - host: "redis.example.com", - port: 6379, - password: "authelia_pass" - })); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/SessionConfigurationBuilder.ts b/themes/squares/server/src/lib/configuration/SessionConfigurationBuilder.ts deleted file mode 100644 index 6ce643d9d..000000000 --- a/themes/squares/server/src/lib/configuration/SessionConfigurationBuilder.ts +++ /dev/null @@ -1,52 +0,0 @@ -import ExpressSession = require("express-session"); -import Redis = require("redis"); - -import { Configuration } from "./schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; -import { RedisStoreOptions } from "connect-redis"; - -export class SessionConfigurationBuilder { - - static build(configuration: Configuration, deps: GlobalDependencies): ExpressSession.SessionOptions { - const sessionOptions: ExpressSession.SessionOptions = { - name: configuration.session.name, - secret: configuration.session.secret, - resave: false, - saveUninitialized: true, - cookie: { - secure: true, - httpOnly: true, - maxAge: configuration.session.expiration, - domain: configuration.session.domain - }, - }; - - if (configuration.session.redis) { - let redisOptions; - const options: Redis.ClientOpts = { - host: configuration.session.redis.host, - port: configuration.session.redis.port - }; - - if (configuration.session.redis.password) { - options["password"] = configuration.session.redis.password; - } - const client = deps.Redis.createClient(options); - - client.on("error", function (err: Error) { - console.error("Redis error:", err); - }); - - redisOptions = { - client: client, - logErrors: true - }; - - if (redisOptions) { - const RedisStore = deps.ConnectRedis(deps.session); - sessionOptions.store = new RedisStore(redisOptions); - } - } - return sessionOptions; - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/AclConfiguration.spec.ts b/themes/squares/server/src/lib/configuration/schema/AclConfiguration.spec.ts deleted file mode 100644 index d1e2a03a2..000000000 --- a/themes/squares/server/src/lib/configuration/schema/AclConfiguration.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ACLConfiguration, complete } from "./AclConfiguration"; -import Assert = require("assert"); - -describe("configuration/schema/AclConfiguration", function() { - it("should complete ACLConfiguration", function() { - const configuration: ACLConfiguration = {}; - const [newConfiguration, errors] = complete(configuration); - - Assert.deepEqual(newConfiguration.default_policy, "bypass"); - Assert.deepEqual(newConfiguration.rules, []); - }); - - it("should return errors when subject is not good", function() { - const configuration: ACLConfiguration = { - default_policy: "deny", - rules: [{ - domain: "dev.example.com", - subject: "user:abc", - policy: "bypass" - }, { - domain: "dev.example.com", - subject: "user:def", - policy: "bypass" - }, { - domain: "dev.example.com", - subject: "badkey:abc", - policy: "bypass" - }] - }; - const [newConfiguration, errors] = complete(configuration); - - Assert.deepEqual(errors, ["Rule 2 has wrong subject. It should be starting with user: or group:."]); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/AclConfiguration.ts b/themes/squares/server/src/lib/configuration/schema/AclConfiguration.ts deleted file mode 100644 index 40401dd64..000000000 --- a/themes/squares/server/src/lib/configuration/schema/AclConfiguration.ts +++ /dev/null @@ -1,41 +0,0 @@ - -export type ACLPolicy = "deny" | "bypass" | "one_factor" | "two_factor"; - -export type ACLRule = { - domain: string; - resources?: string[]; - subject?: string; - policy: ACLPolicy; -}; - -export interface ACLConfiguration { - default_policy?: ACLPolicy; - rules?: ACLRule[]; -} - -export function complete(configuration: ACLConfiguration): [ACLConfiguration, string[]] { - const newConfiguration: ACLConfiguration = (configuration) - ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.default_policy) { - newConfiguration.default_policy = "bypass"; - } - - if (!newConfiguration.rules) { - newConfiguration.rules = []; - } - - if (newConfiguration.rules.length > 0) { - const errors: string[] = []; - newConfiguration.rules.forEach((r, idx) => { - if (r.subject && !r.subject.match(/^(user|group):[a-zA-Z0-9]+$/)) { - errors.push(`Rule ${idx} has wrong subject. It should be starting with user: or group:.`); - } - }); - if (errors.length > 0) { - return [newConfiguration, errors]; - } - } - - return [newConfiguration, []]; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts b/themes/squares/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts deleted file mode 100644 index 3ca86381c..000000000 --- a/themes/squares/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { AuthenticationBackendConfiguration, complete } from "./AuthenticationBackendConfiguration"; -import Assert = require("assert"); - -describe("configuration/schema/AuthenticationBackendConfiguration", function() { - it("should ensure there is at least one key", function() { - const configuration: AuthenticationBackendConfiguration = {} as any; - const [newConfiguration, error] = complete(configuration); - - Assert.equal(error, "Authentication backend must have one of the following keys:`ldap` or `file`"); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts b/themes/squares/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts deleted file mode 100644 index 7f77f894f..000000000 --- a/themes/squares/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { LdapConfiguration } from "./LdapConfiguration"; -import { FileUsersDatabaseConfiguration } from "./FileUsersDatabaseConfiguration"; - -export interface AuthenticationBackendConfiguration { - ldap?: LdapConfiguration; - file?: FileUsersDatabaseConfiguration; -} - -export function complete( - configuration: AuthenticationBackendConfiguration) - : [AuthenticationBackendConfiguration, string] { - - const newConfiguration: AuthenticationBackendConfiguration = (configuration) - ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (Object.keys(newConfiguration).length != 1) { - return [ - newConfiguration, - "Authentication backend must have one of the following keys:" + - "`ldap` or `file`" - ]; - } - - return [newConfiguration, undefined]; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/Configuration.ts b/themes/squares/server/src/lib/configuration/schema/Configuration.ts deleted file mode 100644 index 8d16a5fb2..000000000 --- a/themes/squares/server/src/lib/configuration/schema/Configuration.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { ACLConfiguration, complete as AclConfigurationComplete } from "./AclConfiguration"; -import { AuthenticationBackendConfiguration, complete as AuthenticationBackendComplete } from "./AuthenticationBackendConfiguration"; -import { NotifierConfiguration, complete as NotifierConfigurationComplete } from "./NotifierConfiguration"; -import { RegulationConfiguration, complete as RegulationConfigurationComplete } from "./RegulationConfiguration"; -import { SessionConfiguration, complete as SessionConfigurationComplete } from "./SessionConfiguration"; -import { StorageConfiguration, complete as StorageConfigurationComplete } from "./StorageConfiguration"; -import { TotpConfiguration, complete as TotpConfigurationComplete } from "./TotpConfiguration"; - -export interface Configuration { - access_control?: ACLConfiguration; - authentication_backend: AuthenticationBackendConfiguration; - default_redirection_url?: string; - logs_level?: string; - notifier?: NotifierConfiguration; - port?: number; - regulation?: RegulationConfiguration; - session?: SessionConfiguration; - storage?: StorageConfiguration; - totp?: TotpConfiguration; -} - -export function complete( - configuration: Configuration): - [Configuration, string[]] { - - const newConfiguration: Configuration = JSON.parse( - JSON.stringify(configuration)); - const errors: string[] = []; - - const [acls, aclsErrors] = AclConfigurationComplete( - newConfiguration.access_control); - - newConfiguration.access_control = acls; - if (aclsErrors.length > 0) { - errors.concat(aclsErrors); - } - - const [backend, error] = - AuthenticationBackendComplete( - newConfiguration.authentication_backend); - - if (error) errors.push(error); - newConfiguration.authentication_backend = backend; - - if (!newConfiguration.logs_level) { - newConfiguration.logs_level = "info"; - } - - const [notifier, notifierError] = NotifierConfigurationComplete( - newConfiguration.notifier); - newConfiguration.notifier = notifier; - if (notifierError) errors.push(notifierError); - - if (!newConfiguration.port) { - newConfiguration.port = 8080; - } - - newConfiguration.regulation = RegulationConfigurationComplete( - newConfiguration.regulation); - newConfiguration.session = SessionConfigurationComplete( - newConfiguration.session); - newConfiguration.storage = StorageConfigurationComplete( - newConfiguration.storage); - newConfiguration.totp = TotpConfigurationComplete( - newConfiguration.totp); - - return [newConfiguration, errors]; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts b/themes/squares/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts deleted file mode 100644 index d19002ba9..000000000 --- a/themes/squares/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export interface FileUsersDatabaseConfiguration { - path: string; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/LdapConfiguration.spec.ts b/themes/squares/server/src/lib/configuration/schema/LdapConfiguration.spec.ts deleted file mode 100644 index cc73d1085..000000000 --- a/themes/squares/server/src/lib/configuration/schema/LdapConfiguration.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import Assert = require("assert"); -import { LdapConfiguration, complete } from "./LdapConfiguration"; - -describe("configuration/schema/AuthenticationMethodsConfiguration", function() { - it("should ensure at least one key is provided", function() { - const configuration: LdapConfiguration = { - url: "ldap.example.com", - base_dn: "dc=example,dc=com", - user: "admin", - password: "password" - }; - const newConfiguration = complete(configuration); - - Assert.deepEqual(newConfiguration, { - url: "ldap.example.com", - base_dn: "dc=example,dc=com", - user: "admin", - password: "password", - users_filter: "cn={0}", - group_name_attribute: "cn", - groups_filter: "member={dn}", - mail_attribute: "mail" - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/LdapConfiguration.ts b/themes/squares/server/src/lib/configuration/schema/LdapConfiguration.ts deleted file mode 100644 index 5dacb9390..000000000 --- a/themes/squares/server/src/lib/configuration/schema/LdapConfiguration.ts +++ /dev/null @@ -1,40 +0,0 @@ -import Util = require("util"); - -export interface LdapConfiguration { - url: string; - base_dn: string; - - additional_users_dn?: string; - users_filter?: string; - - additional_groups_dn?: string; - groups_filter?: string; - - group_name_attribute?: string; - mail_attribute?: string; - - user: string; // admin username - password: string; // admin password -} - -export function complete(configuration: LdapConfiguration): LdapConfiguration { - const newConfiguration: LdapConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.users_filter) { - newConfiguration.users_filter = "cn={0}"; - } - - if (!newConfiguration.groups_filter) { - newConfiguration.groups_filter = "member={dn}"; - } - - if (!newConfiguration.group_name_attribute) { - newConfiguration.group_name_attribute = "cn"; - } - - if (!newConfiguration.mail_attribute) { - newConfiguration.mail_attribute = "mail"; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts b/themes/squares/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts deleted file mode 100644 index 6c576e8e0..000000000 --- a/themes/squares/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import Assert = require("assert"); -import { NotifierConfiguration, complete } from "./NotifierConfiguration"; - -describe("configuration/schema/NotifierConfiguration", function() { - it("should use a default notifier when none is provided", function() { - const configuration: NotifierConfiguration = {}; - const [newConfiguration, error] = complete(configuration); - - Assert.deepEqual(newConfiguration.filesystem, {filename: "/tmp/authelia/notification.txt"}); - }); - - it("should ensure correct key is provided", function() { - const configuration = { - abc: "badvalue" - }; - const [newConfiguration, error] = complete(configuration as any); - - Assert.equal(error, "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"); - }); - - it("should ensure there is no more than one key", function() { - const configuration: NotifierConfiguration = { - smtp: { - host: "smtp.example.com", - port: 25, - secure: false, - sender: "test@example.com" - }, - email: { - username: "test", - password: "test", - sender: "test@example.com", - service: "gmail" - } - }; - const [newConfiguration, error] = complete(configuration); - - Assert.equal(error, "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/NotifierConfiguration.ts b/themes/squares/server/src/lib/configuration/schema/NotifierConfiguration.ts deleted file mode 100644 index 7bcce15c2..000000000 --- a/themes/squares/server/src/lib/configuration/schema/NotifierConfiguration.ts +++ /dev/null @@ -1,45 +0,0 @@ - -export interface EmailNotifierConfiguration { - username: string; - password: string; - sender: string; - service: string; -} - -export interface SmtpNotifierConfiguration { - username?: string; - password?: string; - host: string; - port: number; - secure: boolean; - sender: string; -} - -export interface FileSystemNotifierConfiguration { - filename: string; -} - -export interface NotifierConfiguration { - email?: EmailNotifierConfiguration; - smtp?: SmtpNotifierConfiguration; - filesystem?: FileSystemNotifierConfiguration; -} - -export function complete(configuration: NotifierConfiguration): [NotifierConfiguration, string] { - const newConfiguration: NotifierConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (Object.keys(newConfiguration).length == 0) - newConfiguration.filesystem = { filename: "/tmp/authelia/notification.txt" }; - - const ERROR = "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"; - - if (Object.keys(newConfiguration).length != 1) - return [newConfiguration, ERROR]; - - const key = Object.keys(newConfiguration)[0]; - - if (key != "filesystem" && key != "smtp" && key != "email") - return [newConfiguration, ERROR]; - - return [newConfiguration, undefined]; -} diff --git a/themes/squares/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts b/themes/squares/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts deleted file mode 100644 index dce2caf4e..000000000 --- a/themes/squares/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Assert = require("assert"); -import { RegulationConfiguration, complete } from "./RegulationConfiguration"; - -describe("configuration/schema/RegulationConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: RegulationConfiguration = {}; - const newConfiguration = complete(configuration); - - Assert.equal(newConfiguration.ban_time, 300); - Assert.equal(newConfiguration.find_time, 120); - Assert.equal(newConfiguration.max_retries, 3); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/RegulationConfiguration.ts b/themes/squares/server/src/lib/configuration/schema/RegulationConfiguration.ts deleted file mode 100644 index 117463f43..000000000 --- a/themes/squares/server/src/lib/configuration/schema/RegulationConfiguration.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface RegulationConfiguration { - max_retries?: number; - find_time?: number; - ban_time?: number; -} - -export function complete(configuration: RegulationConfiguration): RegulationConfiguration { - const newConfiguration: RegulationConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.max_retries) { - newConfiguration.max_retries = 3; - } - - if (!newConfiguration.find_time) { - newConfiguration.find_time = 120; // seconds - } - - if (!newConfiguration.ban_time) { - newConfiguration.ban_time = 300; // seconds - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/SessionConfiguration.spec.ts b/themes/squares/server/src/lib/configuration/schema/SessionConfiguration.spec.ts deleted file mode 100644 index e54010837..000000000 --- a/themes/squares/server/src/lib/configuration/schema/SessionConfiguration.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Assert = require("assert"); -import { SessionConfiguration, complete } from "./SessionConfiguration"; - -describe("configuration/schema/SessionConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: SessionConfiguration = { - domain: "example.com", - secret: "unsecure_secret" - }; - const newConfiguration = complete(configuration); - - Assert.equal(newConfiguration.name, 'authelia_session'); - Assert.equal(newConfiguration.expiration, 3600000); - Assert.equal(newConfiguration.inactivity, undefined); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/SessionConfiguration.ts b/themes/squares/server/src/lib/configuration/schema/SessionConfiguration.ts deleted file mode 100644 index 2c88bb215..000000000 --- a/themes/squares/server/src/lib/configuration/schema/SessionConfiguration.ts +++ /dev/null @@ -1,32 +0,0 @@ -export interface SessionRedisOptions { - host: string; - port: number; - password?: string; -} - -export interface SessionConfiguration { - name?: string; - domain: string; - secret: string; - expiration?: number; - inactivity?: number; - redis?: SessionRedisOptions; -} - -export function complete(configuration: SessionConfiguration): SessionConfiguration { - const newConfiguration: SessionConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.name) { - newConfiguration.name = "authelia_session"; - } - - if (!newConfiguration.expiration) { - newConfiguration.expiration = 3600000; // 1 hour - } - - if (!newConfiguration.inactivity) { - newConfiguration.inactivity = undefined; // disabled - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/StorageConfiguration.spec.ts b/themes/squares/server/src/lib/configuration/schema/StorageConfiguration.spec.ts deleted file mode 100644 index 9d02a11b6..000000000 --- a/themes/squares/server/src/lib/configuration/schema/StorageConfiguration.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import Assert = require("assert"); -import { StorageConfiguration, complete } from "./StorageConfiguration"; - -describe("configuration/schema/StorageConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: StorageConfiguration = {}; - const newConfiguration = complete(configuration); - - Assert.deepEqual(newConfiguration, { - local: { - in_memory: true - } - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/StorageConfiguration.ts b/themes/squares/server/src/lib/configuration/schema/StorageConfiguration.ts deleted file mode 100644 index 47e356ef4..000000000 --- a/themes/squares/server/src/lib/configuration/schema/StorageConfiguration.ts +++ /dev/null @@ -1,30 +0,0 @@ -export interface MongoStorageConfiguration { - url: string; - database: string; - auth?: { - username: string; - password: string; - }; -} - -export interface LocalStorageConfiguration { - path?: string; - in_memory?: boolean; -} - -export interface StorageConfiguration { - local?: LocalStorageConfiguration; - mongo?: MongoStorageConfiguration; -} - -export function complete(configuration: StorageConfiguration): StorageConfiguration { - const newConfiguration: StorageConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.local && !newConfiguration.mongo) { - newConfiguration.local = { - in_memory: true - }; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/TotpConfiguration.ts b/themes/squares/server/src/lib/configuration/schema/TotpConfiguration.ts deleted file mode 100644 index 683135639..000000000 --- a/themes/squares/server/src/lib/configuration/schema/TotpConfiguration.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface TotpConfiguration { - issuer: string; -} - -export function complete(configuration: TotpConfiguration): TotpConfiguration { - const newConfiguration: TotpConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.issuer) { - newConfiguration.issuer = "authelia.com"; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts b/themes/squares/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts deleted file mode 100644 index 8008b4833..000000000 --- a/themes/squares/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts +++ /dev/null @@ -1,9 +0,0 @@ - -export interface UserInfo { - username: string; - password_hash: string; - email: string; - groups?: string[]; -} - -export type UserDatabaseConfiguration = UserInfo[]; \ No newline at end of file diff --git a/themes/squares/server/src/lib/connectors/mongo/IMongoClient.d.ts b/themes/squares/server/src/lib/connectors/mongo/IMongoClient.d.ts deleted file mode 100644 index 36cb4b8bf..000000000 --- a/themes/squares/server/src/lib/connectors/mongo/IMongoClient.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import MongoDB = require("mongodb"); -import Bluebird = require("bluebird"); - -export interface IMongoClient { - collection(name: string): Bluebird -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/connectors/mongo/MongoClient.spec.ts b/themes/squares/server/src/lib/connectors/mongo/MongoClient.spec.ts deleted file mode 100644 index ca0c68593..000000000 --- a/themes/squares/server/src/lib/connectors/mongo/MongoClient.spec.ts +++ /dev/null @@ -1,119 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); -import MongoDB = require("mongodb"); -import Sinon = require("sinon"); - -import { MongoClient } from "./MongoClient"; -import { GlobalLoggerStub } from "../../logging/GlobalLoggerStub.spec"; -import { MongoStorageConfiguration } from "../../configuration/schema/StorageConfiguration"; - -describe("connectors/mongo/MongoClient", function () { - let MongoClientStub: any; - let mongoClientStub: any; - let mongoDatabaseStub: any; - let logger: GlobalLoggerStub = new GlobalLoggerStub(); - - const configuration: MongoStorageConfiguration = { - url: "mongo://url", - database: "databasename" - }; - - describe("connection", () => { - before(() => { - mongoClientStub = { - db: Sinon.stub() - }; - mongoDatabaseStub = { - on: Sinon.stub(), - collection: Sinon.stub() - } - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - undefined, mongoClientStub); - mongoClientStub.db.returns( - mongoDatabaseStub); - }); - - after(() => { - MongoClientStub.restore(); - }); - - it("should use credentials from configuration", () => { - configuration.auth = { - username: "authelia", - password: "authelia_pass" - }; - - const client = new MongoClient(configuration, logger); - return client.collection("test") - .then(() => { - Assert(MongoClientStub.calledWith("mongo://url", { - auth: { - user: "authelia", - password: "authelia_pass" - } - })) - }); - }); - }); - - describe("collection", () => { - before(function() { - mongoClientStub = { - db: Sinon.stub() - }; - mongoDatabaseStub = { - on: Sinon.stub(), - collection: Sinon.stub() - } - }); - - describe("Connection to mongo is ok", function() { - before(function () { - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - undefined, mongoClientStub); - mongoClientStub.db.returns( - mongoDatabaseStub); - }); - - after(function () { - MongoClientStub.restore(); - }); - - it("should create a collection", function () { - const COLLECTION_NAME = "mycollection"; - const client = new MongoClient(configuration, logger); - - mongoDatabaseStub.collection.returns("COL"); - return client.collection(COLLECTION_NAME) - .then((collection) => mongoDatabaseStub.collection.calledWith(COLLECTION_NAME)); - }); - }); - - describe("Connection to mongo is broken", function() { - before(function () { - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - new Error("Failed connection"), undefined); - }); - - after(function () { - MongoClientStub.restore(); - }); - - it("should fail creating the collection", function() { - const COLLECTION_NAME = "mycollection"; - const client = new MongoClient(configuration, logger); - - mongoDatabaseStub.collection.returns("COL"); - return client.collection(COLLECTION_NAME) - .then((collection) => Bluebird.reject(new Error("should not be here."))) - .catch((err) => Bluebird.resolve()); - }); - }) - }); -}); diff --git a/themes/squares/server/src/lib/connectors/mongo/MongoClient.ts b/themes/squares/server/src/lib/connectors/mongo/MongoClient.ts deleted file mode 100644 index d15731e97..000000000 --- a/themes/squares/server/src/lib/connectors/mongo/MongoClient.ts +++ /dev/null @@ -1,76 +0,0 @@ - -import MongoDB = require("mongodb"); -import { IMongoClient } from "./IMongoClient"; -import Bluebird = require("bluebird"); -import { AUTHENTICATION_FAILED } from "../../../../../shared/UserMessages"; -import { IGlobalLogger } from "../../logging/IGlobalLogger"; -import { MongoStorageConfiguration } from "../../configuration/schema/StorageConfiguration"; - -export class MongoClient implements IMongoClient { - private configuration: MongoStorageConfiguration; - - private database: MongoDB.Db; - private client: MongoDB.MongoClient; - private logger: IGlobalLogger; - - constructor( - configuration: MongoStorageConfiguration, - logger: IGlobalLogger) { - - this.configuration = configuration; - this.logger = logger; - } - - connect(): Bluebird { - const that = this; - const options: MongoDB.MongoClientOptions = {}; - if (that.configuration.auth) { - options["auth"] = { - user: that.configuration.auth.username, - password: that.configuration.auth.password - }; - } - - return new Bluebird((resolve, reject) => { - MongoDB.MongoClient.connect( - this.configuration.url, - options, - function(err, client) { - if (err) { - reject(err); - return; - } - resolve(client); - }); - }) - .then(function (client: MongoDB.MongoClient) { - that.database = client.db(that.configuration.database); - that.database.on("close", () => { - that.logger.info("[MongoClient] Lost connection."); - }); - that.database.on("reconnect", () => { - that.logger.info("[MongoClient] Reconnected."); - }); - that.client = client; - }); - } - - close(): Bluebird { - if (this.client) { - this.client.close(); - this.database = undefined; - this.client = undefined; - } - return Bluebird.resolve(); - } - - collection(name: string): Bluebird { - if (!this.client) { - const that = this; - return this.connect() - .then(() => Bluebird.resolve(that.database.collection(name))); - } - - return Bluebird.resolve(this.database.collection(name)); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/connectors/mongo/MongoClientStub.spec.ts b/themes/squares/server/src/lib/connectors/mongo/MongoClientStub.spec.ts deleted file mode 100644 index 1cfd48e32..000000000 --- a/themes/squares/server/src/lib/connectors/mongo/MongoClientStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import MongoDB = require("mongodb"); -import Bluebird = require("bluebird"); -import { IMongoClient } from "../../../../src/lib/connectors/mongo/IMongoClient"; - -export class MongoClientStub implements IMongoClient { - public collectionStub: Sinon.SinonStub; - - constructor() { - this.collectionStub = Sinon.stub(); - } - - collection(name: string): Bluebird { - return this.collectionStub(name); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/logging/GlobalLogger.ts b/themes/squares/server/src/lib/logging/GlobalLogger.ts deleted file mode 100644 index 4da7acf49..000000000 --- a/themes/squares/server/src/lib/logging/GlobalLogger.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { IGlobalLogger } from "./IGlobalLogger"; -import Util = require("util"); -import Express = require("express"); -import Winston = require("winston"); - -declare module "express" { - interface Request { - id: string; - } -} - -export class GlobalLogger implements IGlobalLogger { - private winston: typeof Winston; - constructor(winston: typeof Winston) { - this.winston = winston; - } - - private buildMessage(message: string, ...args: any[]): string { - return Util.format("date='%s' message='%s'", new Date(), - Util.format(message, ...args)); - } - - info(message: string, ...args: any[]): void { - this.winston.info(this.buildMessage(message, ...args)); - } - - debug(message: string, ...args: any[]): void { - this.winston.debug(this.buildMessage(message, ...args)); - } - - error(message: string, ...args: any[]): void { - this.winston.debug(this.buildMessage(message, ...args)); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/logging/GlobalLoggerStub.spec.ts b/themes/squares/server/src/lib/logging/GlobalLoggerStub.spec.ts deleted file mode 100644 index d4bb13710..000000000 --- a/themes/squares/server/src/lib/logging/GlobalLoggerStub.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import Sinon = require("sinon"); -import { GlobalLogger } from "./GlobalLogger"; -import Winston = require("winston"); -import Express = require("express"); -import { IGlobalLogger } from "./IGlobalLogger"; - -export class GlobalLoggerStub implements IGlobalLogger { - infoStub: Sinon.SinonStub; - debugStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - private globalLogger: IGlobalLogger; - - constructor(enableLogging?: boolean) { - this.infoStub = Sinon.stub(); - this.debugStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - if (enableLogging) - this.globalLogger = new GlobalLogger(Winston); - } - - info(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.infoStub(message, ...args); - } - - debug(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.debugStub(message, ...args); - } - - error(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.errorStub(message, ...args); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/logging/IGlobalLogger.ts b/themes/squares/server/src/lib/logging/IGlobalLogger.ts deleted file mode 100644 index 548515ec7..000000000 --- a/themes/squares/server/src/lib/logging/IGlobalLogger.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface IGlobalLogger { - info(message: string, ...args: any[]): void; - debug(message: string, ...args: any[]): void; - error(message: string, ...args: any[]): void; -} diff --git a/themes/squares/server/src/lib/logging/IRequestLogger.ts b/themes/squares/server/src/lib/logging/IRequestLogger.ts deleted file mode 100644 index 126a601fe..000000000 --- a/themes/squares/server/src/lib/logging/IRequestLogger.ts +++ /dev/null @@ -1,7 +0,0 @@ -import Express = require("express"); - -export interface IRequestLogger { - info(req: Express.Request, message: string, ...args: any[]): void; - debug(req: Express.Request, message: string, ...args: any[]): void; - error(req: Express.Request, message: string, ...args: any[]): void; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/logging/RequestLogger.ts b/themes/squares/server/src/lib/logging/RequestLogger.ts deleted file mode 100644 index c45c66018..000000000 --- a/themes/squares/server/src/lib/logging/RequestLogger.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { IRequestLogger } from "./IRequestLogger"; -import Util = require("util"); -import Express = require("express"); -import Winston = require("winston"); - -declare module "express" { - interface Request { - id: string; - } -} - -export class RequestLogger implements IRequestLogger { - private winston: typeof Winston; - - constructor(winston: typeof Winston) { - this.winston = winston; - } - - private formatHeader(req: Express.Request) { - const clientIP = req.ip; // The IP of the original client going through the proxy chain. - return Util.format("date='%s' method='%s', path='%s' requestId='%s' sessionId='%s' ip='%s'", - new Date(), req.method, req.path, req.id, req.sessionID, clientIP); - } - - private formatBody(message: string) { - return Util.format("message='%s'", message); - } - - private formatMessage(req: Express.Request, message: string) { - return Util.format("%s %s", this.formatHeader(req), - this.formatBody(message)); - } - - info(req: Express.Request, message: string, ...args: any[]): void { - this.winston.info(this.formatMessage(req, message), ...args); - } - - debug(req: Express.Request, message: string, ...args: any[]): void { - this.winston.debug(this.formatMessage(req, message), ...args); - } - - error(req: Express.Request, message: string, ...args: any[]): void { - this.winston.error(this.formatMessage(req, message), ...args); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/logging/RequestLoggerStub.spec.ts b/themes/squares/server/src/lib/logging/RequestLoggerStub.spec.ts deleted file mode 100644 index b0e375210..000000000 --- a/themes/squares/server/src/lib/logging/RequestLoggerStub.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { IRequestLogger } from "./IRequestLogger"; -import Sinon = require("sinon"); -import { RequestLogger } from "./RequestLogger"; -import Winston = require("winston"); -import Express = require("express"); - -export class RequestLoggerStub implements IRequestLogger { - infoStub: Sinon.SinonStub; - debugStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - private requestLogger: RequestLogger; - - constructor(enableLogging?: boolean) { - this.infoStub = Sinon.stub(); - this.debugStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - if (enableLogging) - this.requestLogger = new RequestLogger(Winston); - } - - info(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.infoStub(req, message, ...args); - } - - debug(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.debugStub(req, message, ...args); - } - - error(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.errorStub(req, message, ...args); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/notifiers/AbstractEmailNotifier.ts b/themes/squares/server/src/lib/notifiers/AbstractEmailNotifier.ts deleted file mode 100644 index 198e4e5db..000000000 --- a/themes/squares/server/src/lib/notifiers/AbstractEmailNotifier.ts +++ /dev/null @@ -1,23 +0,0 @@ - -import { INotifier } from "../notifiers/INotifier"; -import { Identity } from "../../../types/Identity"; - -import Fs = require("fs"); -import Path = require("path"); -import Ejs = require("ejs"); -import BluebirdPromise = require("bluebird"); - -const email_template = Fs.readFileSync(Path.join(__dirname, "../../resources/email-template.ejs"), "UTF-8"); - -export abstract class AbstractEmailNotifier implements INotifier { - notify(to: string, subject: string, link: string): BluebirdPromise { - const d = { - url: link, - button_title: "Continue", - title: subject - }; - return this.sendEmail(to, subject, Ejs.render(email_template, d)); - } - - abstract sendEmail(to: string, subject: string, content: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/notifiers/EmailNotifier.spec.ts b/themes/squares/server/src/lib/notifiers/EmailNotifier.spec.ts deleted file mode 100644 index 8211bbc02..000000000 --- a/themes/squares/server/src/lib/notifiers/EmailNotifier.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as sinon from "sinon"; -import * as Assert from "assert"; -import BluebirdPromise = require("bluebird"); - -import { MailSenderStub } from "./MailSenderStub.spec"; -import EmailNotifier = require("./EmailNotifier"); - - -describe("notifiers/EmailNotifier", function () { - it("should send an email to given user", function () { - const mailSender = new MailSenderStub(); - const options = { - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }; - - mailSender.sendStub.returns(BluebirdPromise.resolve()); - const sender = new EmailNotifier.EmailNotifier(options, mailSender); - const subject = "subject"; - const url = "http://test.com"; - - return sender.notify("user@example.com", subject, url) - .then(function () { - Assert.equal(mailSender.sendStub.getCall(0).args[0].to, "user@example.com"); - Assert.equal(mailSender.sendStub.getCall(0).args[0].subject, "subject"); - return BluebirdPromise.resolve(); - }); - }); - - it("should fail while sending an email", function () { - const mailSender = new MailSenderStub(); - const options = { - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }; - - mailSender.sendStub.returns(BluebirdPromise.reject(new Error("Failed to send mail"))); - const sender = new EmailNotifier.EmailNotifier(options, mailSender); - const subject = "subject"; - const url = "http://test.com"; - - return sender.notify("user@example.com", subject, url) - .then(function () { - return BluebirdPromise.reject(new Error()); - }, function() { - Assert.equal(mailSender.sendStub.getCall(0).args[0].from, "admin@example.com"); - return BluebirdPromise.resolve(); - }); - }); -}); diff --git a/themes/squares/server/src/lib/notifiers/EmailNotifier.ts b/themes/squares/server/src/lib/notifiers/EmailNotifier.ts deleted file mode 100644 index 4df7c861e..000000000 --- a/themes/squares/server/src/lib/notifiers/EmailNotifier.ts +++ /dev/null @@ -1,27 +0,0 @@ - -import * as BluebirdPromise from "bluebird"; - -import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier"; -import { EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; -import { IMailSender } from "./IMailSender"; - -export class EmailNotifier extends AbstractEmailNotifier { - private mailSender: IMailSender; - private sender: string; - - constructor(options: EmailNotifierConfiguration, mailSender: IMailSender) { - super(); - this.mailSender = mailSender; - this.sender = options.sender; - } - - sendEmail(to: string, subject: string, content: string) { - const mailOptions = { - from: this.sender, - to: to, - subject: subject, - html: content - }; - return this.mailSender.send(mailOptions); - } -} diff --git a/themes/squares/server/src/lib/notifiers/FileSystemNotifier.ts b/themes/squares/server/src/lib/notifiers/FileSystemNotifier.ts deleted file mode 100644 index 23f6242c4..000000000 --- a/themes/squares/server/src/lib/notifiers/FileSystemNotifier.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as BluebirdPromise from "bluebird"; -import * as util from "util"; -import * as Fs from "fs"; -import { INotifier } from "./INotifier"; -import { Identity } from "../../../types/Identity"; - -import { FileSystemNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class FileSystemNotifier implements INotifier { - private filename: string; - - constructor(options: FileSystemNotifierConfiguration) { - this.filename = options.filename; - } - - notify(to: string, subject: string, link: string): BluebirdPromise { - const content = util.format("Date: %s\nEmail: %s\nSubject: %s\nLink: %s", - new Date().toString(), to, subject, link); - const writeFilePromised: any = BluebirdPromise.promisify(Fs.writeFile); - return writeFilePromised(this.filename, content); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/notifiers/IMailSender.ts b/themes/squares/server/src/lib/notifiers/IMailSender.ts deleted file mode 100644 index 34ac464a8..000000000 --- a/themes/squares/server/src/lib/notifiers/IMailSender.ts +++ /dev/null @@ -1,6 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); - -export interface IMailSender { - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/notifiers/IMailSenderBuilder.ts b/themes/squares/server/src/lib/notifiers/IMailSenderBuilder.ts deleted file mode 100644 index 36d4dcdf7..000000000 --- a/themes/squares/server/src/lib/notifiers/IMailSenderBuilder.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export interface IMailSenderBuilder { - buildEmail(options: EmailNotifierConfiguration): IMailSender; - buildSmtp(options: SmtpNotifierConfiguration): IMailSender; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/notifiers/INotifier.ts b/themes/squares/server/src/lib/notifiers/INotifier.ts deleted file mode 100644 index b9a6b138c..000000000 --- a/themes/squares/server/src/lib/notifiers/INotifier.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as BluebirdPromise from "bluebird"; - -export interface INotifier { - notify(to: string, subject: string, link: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/notifiers/MailSender.ts b/themes/squares/server/src/lib/notifiers/MailSender.ts deleted file mode 100644 index 536a88e63..000000000 --- a/themes/squares/server/src/lib/notifiers/MailSender.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import Nodemailer = require("nodemailer"); -import NodemailerDirectTransport = require("nodemailer-direct-transport"); -import NodemailerSmtpTransport = require("nodemailer-smtp-transport"); -import BluebirdPromise = require("bluebird"); - -export class MailSender implements IMailSender { - private transporter: Nodemailer.Transporter; - - constructor(options: NodemailerDirectTransport.DirectOptions | - NodemailerSmtpTransport.SmtpOptions, nodemailer: typeof Nodemailer) { - this.transporter = nodemailer.createTransport(options); - } - - verify(): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - that.transporter.verify(function (error: Error, success: any) { - if (error) { - reject(new Error("Unable to connect to SMTP server. \ - Please check the service is running and your credentials are correct.")); - return; - } - resolve(); - }); - }); - } - - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - that.transporter.sendMail(mailOptions, (error: Error, - data: Nodemailer.SentMessageInfo) => { - if (error) { - reject(new Error("Error while sending email: " + error.message)); - return; - } - resolve(); - }); - }); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/notifiers/MailSenderBuilder.spec.ts b/themes/squares/server/src/lib/notifiers/MailSenderBuilder.spec.ts deleted file mode 100644 index 41e0db426..000000000 --- a/themes/squares/server/src/lib/notifiers/MailSenderBuilder.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ - -import { MailSenderBuilder } from ".//MailSenderBuilder"; -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); -import Assert = require("assert"); - -describe("notifiers/MailSenderBuilder", function() { - let createTransportStub: Sinon.SinonStub; - beforeEach(function() { - createTransportStub = Sinon.stub(Nodemailer, "createTransport"); - }); - - afterEach(function() { - createTransportStub.restore(); - }); - - it("should create a email mail sender", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildEmail({ - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }); - Assert.equal(createTransportStub.getCall(0).args[0].auth.user, "user_gmail"); - Assert.equal(createTransportStub.getCall(0).args[0].auth.pass, "pass_gmail"); - Assert.equal(createTransportStub.getCall(0).args[0].service, "gmail"); - }); - - describe("build smtp mail sender", function() { - it("should create a smtp mail sender with authenticated user", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildSmtp({ - host: "mail.example.com", - password: "password", - port: 25, - secure: true, - username: "user", - sender: "admin@example.com" - }); - Assert.deepStrictEqual(createTransportStub.getCall(0).args[0], { - host: "mail.example.com", - auth: { - pass: "password", - user: "user" - }, - port: 25, - secure: true, - }); - }); - - it("should create a smtp mail sender with anonymous user", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildSmtp({ - host: "mail.example.com", - port: 25, - secure: true, - sender: "admin@example.com" - }); - Assert.deepStrictEqual(createTransportStub.getCall(0).args[0], { - host: "mail.example.com", - port: 25, - secure: true, - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/notifiers/MailSenderBuilder.ts b/themes/squares/server/src/lib/notifiers/MailSenderBuilder.ts deleted file mode 100644 index 1d06be52f..000000000 --- a/themes/squares/server/src/lib/notifiers/MailSenderBuilder.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import { IMailSenderBuilder } from "./IMailSenderBuilder"; -import { MailSender } from "./MailSender"; -import Nodemailer = require("nodemailer"); -import NodemailerSmtpTransport = require("nodemailer-smtp-transport"); -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class MailSenderBuilder implements IMailSenderBuilder { - private nodemailer: typeof Nodemailer; - - constructor(nodemailer: typeof Nodemailer) { - this.nodemailer = nodemailer; - } - - buildEmail(options: EmailNotifierConfiguration): IMailSender { - const emailOptions = { - service: options.service, - auth: { - user: options.username, - pass: options.password - } - }; - return new MailSender(emailOptions, this.nodemailer); - } - - buildSmtp(options: SmtpNotifierConfiguration): IMailSender { - const smtpOptions: NodemailerSmtpTransport.SmtpOptions = { - host: options.host, - port: options.port, - secure: options.secure, // upgrade later with STARTTLS - }; - - if (options.username && options.password) { - smtpOptions.auth = { - user: options.username, - pass: options.password - }; - } - - return new MailSender(smtpOptions, this.nodemailer); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts b/themes/squares/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts deleted file mode 100644 index 5b76f6e56..000000000 --- a/themes/squares/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { IMailSenderBuilder } from "../../../src/lib/notifiers/IMailSenderBuilder"; -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); -import { IMailSender } from "../../../src/lib/notifiers/IMailSender"; -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../../../src/lib/configuration/schema/NotifierConfiguration"; - -export class MailSenderBuilderStub implements IMailSenderBuilder { - buildEmailStub: Sinon.SinonStub; - buildSmtpStub: Sinon.SinonStub; - - constructor() { - this.buildEmailStub = Sinon.stub(); - this.buildSmtpStub = Sinon.stub(); - } - - buildEmail(options: EmailNotifierConfiguration): IMailSender { - return this.buildEmailStub(options); - } - - buildSmtp(options: SmtpNotifierConfiguration): IMailSender { - return this.buildSmtpStub(options); - } - -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/notifiers/MailSenderStub.spec.ts b/themes/squares/server/src/lib/notifiers/MailSenderStub.spec.ts deleted file mode 100644 index d57c458fc..000000000 --- a/themes/squares/server/src/lib/notifiers/MailSenderStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { IMailSender } from "../../../src/lib/notifiers/IMailSender"; -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); - -export class MailSenderStub implements IMailSender { - sendStub: Sinon.SinonStub; - - constructor() { - this.sendStub = Sinon.stub(); - } - - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise { - return this.sendStub(mailOptions); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/notifiers/NotifierFactory.spec.ts b/themes/squares/server/src/lib/notifiers/NotifierFactory.spec.ts deleted file mode 100644 index f15e7667f..000000000 --- a/themes/squares/server/src/lib/notifiers/NotifierFactory.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ - -import * as sinon from "sinon"; -import * as BluebirdPromise from "bluebird"; -import * as assert from "assert"; - -import { NotifierFactory } from "./NotifierFactory"; -import { EmailNotifier } from "./EmailNotifier"; -import { SmtpNotifier } from "./SmtpNotifier"; -import { MailSenderBuilderStub } from "./MailSenderBuilderStub.spec"; - - -describe("notifiers/NotifierFactory", function () { - let mailSenderBuilderStub: MailSenderBuilderStub; - it("should build a Email Notifier", function () { - const options = { - email: { - username: "abc", - password: "password", - sender: "admin@example.com", - service: "gmail" - } - }; - mailSenderBuilderStub = new MailSenderBuilderStub(); - assert(NotifierFactory.build(options, mailSenderBuilderStub) instanceof EmailNotifier); - }); - - it("should build a SMTP Notifier", function () { - const options = { - smtp: { - username: "user", - password: "pass", - secure: true, - host: "localhost", - port: 25, - sender: "admin@example.com" - } - }; - - mailSenderBuilderStub = new MailSenderBuilderStub(); - assert(NotifierFactory.build(options, mailSenderBuilderStub) instanceof SmtpNotifier); - }); -}); diff --git a/themes/squares/server/src/lib/notifiers/NotifierFactory.ts b/themes/squares/server/src/lib/notifiers/NotifierFactory.ts deleted file mode 100644 index a89155feb..000000000 --- a/themes/squares/server/src/lib/notifiers/NotifierFactory.ts +++ /dev/null @@ -1,33 +0,0 @@ - -import { NotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; -import Nodemailer = require("nodemailer"); -import { INotifier } from "./INotifier"; - -import { FileSystemNotifier } from "./FileSystemNotifier"; -import { EmailNotifier } from "./EmailNotifier"; -import { SmtpNotifier } from "./SmtpNotifier"; -import { IMailSender } from "./IMailSender"; -import { IMailSenderBuilder } from "./IMailSenderBuilder"; - -export class NotifierFactory { - static build(options: NotifierConfiguration, mailSenderBuilder: IMailSenderBuilder): INotifier { - if ("email" in options) { - const mailSender = mailSenderBuilder.buildEmail(options.email); - return new EmailNotifier(options.email, mailSender); - } - else if ("smtp" in options) { - const mailSender = mailSenderBuilder.buildSmtp(options.smtp); - return new SmtpNotifier(options.smtp, mailSender); - } - else if ("filesystem" in options) { - return new FileSystemNotifier(options.filesystem); - } - else { - throw new Error("No available notifier option detected."); - } - } -} - - - - diff --git a/themes/squares/server/src/lib/notifiers/NotifierStub.spec.ts b/themes/squares/server/src/lib/notifiers/NotifierStub.spec.ts deleted file mode 100644 index f99231b50..000000000 --- a/themes/squares/server/src/lib/notifiers/NotifierStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); - -import { INotifier } from "./INotifier"; - -export class NotifierStub implements INotifier { - notifyStub: Sinon.SinonStub; - - constructor() { - this.notifyStub = Sinon.stub(); - } - - notify(to: string, subject: string, link: string): BluebirdPromise { - return this.notifyStub(to, subject, link); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/notifiers/SmtpNotifier.ts b/themes/squares/server/src/lib/notifiers/SmtpNotifier.ts deleted file mode 100644 index f93a6d4a0..000000000 --- a/themes/squares/server/src/lib/notifiers/SmtpNotifier.ts +++ /dev/null @@ -1,30 +0,0 @@ - - -import * as BluebirdPromise from "bluebird"; - -import { IMailSender } from "./IMailSender"; -import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier"; -import { SmtpNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class SmtpNotifier extends AbstractEmailNotifier { - private mailSender: IMailSender; - private sender: string; - - constructor(options: SmtpNotifierConfiguration, - mailSender: IMailSender) { - super(); - this.mailSender = mailSender; - this.sender = options.sender; - } - - sendEmail(to: string, subject: string, content: string) { - const mailOptions = { - from: this.sender, - to: to, - subject: subject, - html: content - }; - const that = this; - return this.mailSender.send(mailOptions); - } -} diff --git a/themes/squares/server/src/lib/regulation/IRegulator.ts b/themes/squares/server/src/lib/regulation/IRegulator.ts deleted file mode 100644 index c49425b24..000000000 --- a/themes/squares/server/src/lib/regulation/IRegulator.ts +++ /dev/null @@ -1,6 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -export interface IRegulator { - mark(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise; - regulate(userId: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/regulation/Regulator.spec.ts b/themes/squares/server/src/lib/regulation/Regulator.spec.ts deleted file mode 100644 index f9c6e6086..000000000 --- a/themes/squares/server/src/lib/regulation/Regulator.spec.ts +++ /dev/null @@ -1,186 +0,0 @@ - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -import { Regulator } from "./Regulator"; -import MockDate = require("mockdate"); -import exceptions = require("../Exceptions"); -import { UserDataStoreStub } from "../storage/UserDataStoreStub.spec"; - -describe("regulation/Regulator", function () { - const USER1 = "USER1"; - const USER2 = "USER2"; - let userDataStoreStub: UserDataStoreStub; - - beforeEach(function () { - userDataStoreStub = new UserDataStoreStub(); - const dataStore: { [userId: string]: { userId: string, date: Date, isAuthenticationSuccessful: boolean }[] } = { - [USER1]: [], - [USER2]: [] - }; - - userDataStoreStub.saveAuthenticationTraceStub.callsFake(function (userId, isAuthenticationSuccessful) { - dataStore[userId].unshift({ - userId: userId, - date: new Date(), - isAuthenticationSuccessful: isAuthenticationSuccessful, - }); - return BluebirdPromise.resolve(); - }); - - userDataStoreStub.retrieveLatestAuthenticationTracesStub.callsFake(function (userId, count) { - const ret = (dataStore[userId].length <= count) ? dataStore[userId] : dataStore[userId].slice(0, 3); - return BluebirdPromise.resolve(ret); - }); - }); - - afterEach(function () { - MockDate.reset(); - }); - - function markAuthenticationAt(regulator: Regulator, user: string, time: string, success: boolean) { - MockDate.set(time); - return regulator.mark(user, success); - } - - it("should mark 2 authentication and regulate (accept)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 10, 10); - - return regulator.mark(USER1, false) - .then(function () { - return regulator.mark(USER1, true); - }) - .then(function () { - return regulator.regulate(USER1); - }); - }); - - it("should mark 3 authentications and regulate (reject)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 10, 10); - - return regulator.mark(USER1, false) - .then(function () { - return regulator.mark(USER1, false); - }) - .then(function () { - return regulator.mark(USER1, false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { return BluebirdPromise.reject(new Error("should not be here!")); }) - .catch(exceptions.AuthenticationRegulationError, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should mark 1 failed, 1 successful and 1 failed authentications within minimum time and regulate (accept)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 60, 30); - - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:10", true); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:20", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:30", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:39", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here!")); - }, - function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should regulate user if number of failures is greater than 3 in allowed time lapse", function () { - function markAuthentications(regulator: Regulator, user: string) { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:45", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:01:05", false); - }) - .then(function () { - return regulator.regulate(user); - }); - } - - const regulator1 = new Regulator(userDataStoreStub, 3, 60, 60); - const regulator2 = new Regulator(userDataStoreStub, 3, 2 * 60, 60); - - const p1 = markAuthentications(regulator1, USER1); - const p2 = markAuthentications(regulator2, USER2); - - return BluebirdPromise.join(p1, p2) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here...")); - }, function () { - Assert(p1.isFulfilled()); - Assert(p2.isRejected()); - }); - }); - - it("should user wait after regulation to authenticate again", function () { - function markAuthentications(regulator: Regulator, user: string) { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:10", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:15", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:25", false); - }) - .then(function () { - MockDate.set("1/2/2000 00:00:54"); - return regulator.regulate(user); - }) - .then(function () { - return BluebirdPromise.reject(new Error("should fail at this time")); - }, function () { - MockDate.set("1/2/2000 00:00:56"); - return regulator.regulate(user); - }); - } - - const regulator = new Regulator(userDataStoreStub, 4, 30, 30); - return markAuthentications(regulator, USER1); - }); - - it("should disable regulation when max_retries is set to 0", function () { - const maxRetries = 0; - const regulator = new Regulator(userDataStoreStub, maxRetries, 60, 30); - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:10", false); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:15", false); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:25", false); - }) - .then(function () { - MockDate.set("1/2/2000 00:00:26"); - return regulator.regulate(USER1); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/regulation/Regulator.ts b/themes/squares/server/src/lib/regulation/Regulator.ts deleted file mode 100644 index 1037a6a17..000000000 --- a/themes/squares/server/src/lib/regulation/Regulator.ts +++ /dev/null @@ -1,55 +0,0 @@ - -import * as BluebirdPromise from "bluebird"; -import exceptions = require("../Exceptions"); -import { IUserDataStore } from "../storage/IUserDataStore"; -import { AuthenticationTraceDocument } from "../storage/AuthenticationTraceDocument"; -import { IRegulator } from "./IRegulator"; - -export class Regulator implements IRegulator { - private userDataStore: IUserDataStore; - private banTime: number; - private findTime: number; - private maxRetries: number; - - constructor(userDataStore: any, maxRetries: number, findTime: number, banTime: number) { - this.userDataStore = userDataStore; - this.banTime = banTime; - this.findTime = findTime; - this.maxRetries = maxRetries; - } - - // Mark authentication - mark(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - return this.userDataStore.saveAuthenticationTrace(userId, isAuthenticationSuccessful); - } - - regulate(userId: string): BluebirdPromise { - const that = this; - - if (that.maxRetries <= 0) return BluebirdPromise.resolve(); - - return this.userDataStore.retrieveLatestAuthenticationTraces(userId, that.maxRetries) - .then((docs: AuthenticationTraceDocument[]) => { - // less than the max authorized number of authentication in time range, thus authorizing access - if (docs.length < that.maxRetries) return BluebirdPromise.resolve(); - - const numberOfFailedAuth = docs - .map(function (d: AuthenticationTraceDocument) { return d.isAuthenticationSuccessful == false ? 1 : 0; }) - .reduce(function (acc, v) { return acc + v; }, 0); - - if (numberOfFailedAuth < this.maxRetries) return BluebirdPromise.resolve(); - - const newestDocument = docs[0]; - const oldestDocument = docs[that.maxRetries - 1]; - - const authenticationsTimeRangeInSeconds = (newestDocument.date.getTime() - oldestDocument.date.getTime()) / 1000; - const tooManyAuthInTimelapse = (authenticationsTimeRangeInSeconds < this.findTime); - const stillInBannedTimeRange = (new Date(new Date().getTime() - this.banTime * 1000) < newestDocument.date); - - if (tooManyAuthInTimelapse && stillInBannedTimeRange) - throw new exceptions.AuthenticationRegulationError("Max number of authentication. Please retry in few minutes."); - - return BluebirdPromise.resolve(); - }); - } -} diff --git a/themes/squares/server/src/lib/regulation/RegulatorStub.spec.ts b/themes/squares/server/src/lib/regulation/RegulatorStub.spec.ts deleted file mode 100644 index ca8a00fb1..000000000 --- a/themes/squares/server/src/lib/regulation/RegulatorStub.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); -import { IRegulator } from "./IRegulator"; - - -export class RegulatorStub implements IRegulator { - markStub: Sinon.SinonStub; - regulateStub: Sinon.SinonStub; - - constructor() { - this.markStub = Sinon.stub(); - this.regulateStub = Sinon.stub(); - } - - mark(userId: string, isAuthenticationSuccessful: boolean): Bluebird { - return this.markStub(userId, isAuthenticationSuccessful); - } - - regulate(userId: string): Bluebird { - return this.regulateStub(userId); - } -} diff --git a/themes/squares/server/src/lib/routes/error/401/get.spec.ts b/themes/squares/server/src/lib/routes/error/401/get.spec.ts deleted file mode 100644 index 9fdac9c3c..000000000 --- a/themes/squares/server/src/lib/routes/error/401/get.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get401 from "./get"; -import { ServerVariables } from "../../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../../ServerVariablesMockBuilder.spec"; - -describe("routes/error/401/get", function () { - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let req: any; - let res: any; - let renderSpy: Sinon.SinonSpy; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - - renderSpy = Sinon.spy(); - req = { - headers: {} - }; - res = { - render: renderSpy - }; - }); - - it("should set redirection url to the default redirection url", function () { - vars.config.default_redirection_url = "http://default-redirection"; - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: "http://default-redirection" - })); - }); - }); - - it("should set redirection url to the referer", function () { - req.headers["referer"] = "http://redirection"; - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: "http://redirection" - })); - }); - }); - - it("should render without redirecting the user", function () { - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: undefined - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/error/401/get.ts b/themes/squares/server/src/lib/routes/error/401/get.ts deleted file mode 100644 index ca4a3963d..000000000 --- a/themes/squares/server/src/lib/routes/error/401/get.ts +++ /dev/null @@ -1,15 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import redirector from "../redirector"; -import { ServerVariables } from "../../../ServerVariables"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - const redirectionUrl = redirector(req, vars); - res.render("errors/401", { - redirection_url: redirectionUrl - }); - return BluebirdPromise.resolve(); - }; -} diff --git a/themes/squares/server/src/lib/routes/error/403/get.spec.ts b/themes/squares/server/src/lib/routes/error/403/get.spec.ts deleted file mode 100644 index 22eb84853..000000000 --- a/themes/squares/server/src/lib/routes/error/403/get.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get403 from "./get"; -import { ServerVariables } from "../../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../../ServerVariablesMockBuilder.spec"; - -describe("routes/error/403/get", function () { - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let req: any; - let res: any; - let renderSpy: Sinon.SinonSpy; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - - renderSpy = Sinon.spy(); - req = { - headers: {} - }; - res = { - render: renderSpy - }; - }); - - it("should set redirection url to the default redirection url", function () { - vars.config.default_redirection_url = "http://default-redirection"; - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: "http://default-redirection" - })); - }); - }); - - it("should set redirection url to the referer", function () { - req.headers["referer"] = "http://redirection"; - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: "http://redirection" - })); - }); - }); - - it("should render without redirecting the user", function () { - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: undefined - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/error/403/get.ts b/themes/squares/server/src/lib/routes/error/403/get.ts deleted file mode 100644 index 3ab0319e5..000000000 --- a/themes/squares/server/src/lib/routes/error/403/get.ts +++ /dev/null @@ -1,15 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import redirector from "../redirector"; -import { ServerVariables } from "../../../ServerVariables"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - const redirectionUrl = redirector(req, vars); - res.render("errors/403", { - redirection_url: redirectionUrl - }); - return BluebirdPromise.resolve(); - }; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/error/404/get.spec.ts b/themes/squares/server/src/lib/routes/error/404/get.spec.ts deleted file mode 100644 index 73e4e6cef..000000000 --- a/themes/squares/server/src/lib/routes/error/404/get.spec.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get404 from "./get"; - -describe("routes/error/404/get", function () { - it("should render the page", function () { - const req = {} as Express.Request; - const res = { - render: Sinon.stub() - }; - - return Get404(req, res as any) - .then(function () { - Assert(res.render.calledOnce); - Assert(res.render.calledWith("errors/404")); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/error/404/get.ts b/themes/squares/server/src/lib/routes/error/404/get.ts deleted file mode 100644 index 6693b6fc8..000000000 --- a/themes/squares/server/src/lib/routes/error/404/get.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); - -export default function (req: express.Request, res: express.Response): BluebirdPromise { - res.render("errors/404"); - return BluebirdPromise.resolve(); -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/error/redirector.ts b/themes/squares/server/src/lib/routes/error/redirector.ts deleted file mode 100644 index b1a3ccc11..000000000 --- a/themes/squares/server/src/lib/routes/error/redirector.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Express = require("express"); -import { ServerVariables } from "../../ServerVariables"; - -export default function (req: Express.Request, vars: ServerVariables): string { - let redirectionUrl: string; - - if (req.headers && req.headers["referer"]) - redirectionUrl = "" + req.headers["referer"]; - else if (vars.config.default_redirection_url) - redirectionUrl = vars.config.default_redirection_url; - - return redirectionUrl; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/firstfactor/get.ts b/themes/squares/server/src/lib/routes/firstfactor/get.ts deleted file mode 100644 index d94f656c8..000000000 --- a/themes/squares/server/src/lib/routes/firstfactor/get.ts +++ /dev/null @@ -1,72 +0,0 @@ - -import express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import Util = require("util"); -import { ServerVariables } from "../../ServerVariables"; -import { SafeRedirector } from "../../utils/SafeRedirection"; -import { Level } from "../../authentication/Level"; - -function getRedirectParam( - req: express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -function redirectToSecondFactorPage( - req: express.Request, - res: express.Response) { - - const redirectUrl = getRedirectParam(req); - if (!redirectUrl) - res.redirect(Endpoints.SECOND_FACTOR_GET); - else - res.redirect( - Util.format("%s?%s=%s", - Endpoints.SECOND_FACTOR_GET, - Constants.REDIRECT_QUERY_PARAM, - redirectUrl)); -} - -function redirectToService( - req: express.Request, - res: express.Response, - redirector: SafeRedirector) { - const redirectUrl = getRedirectParam(req); - if (!redirectUrl) { - res.redirect(Endpoints.LOGGED_IN); - } else { - redirector.redirectOrElse(res, redirectUrl, Endpoints.LOGGED_IN); - } -} - -function renderFirstFactor( - res: express.Response) { - - res.render("firstfactor", { - first_factor_post_endpoint: Endpoints.FIRST_FACTOR_POST, - reset_password_request_endpoint: Endpoints.RESET_PASSWORD_REQUEST_GET - }); -} - -export default function ( - vars: ServerVariables) { - - const redirector = new SafeRedirector(vars.config.session.domain); - return function (req: express.Request, res: express.Response): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (authSession.authentication_level == Level.ONE_FACTOR) { - redirectToSecondFactorPage(req, res); - } else if (authSession.authentication_level == Level.TWO_FACTOR) { - redirectToService(req, res, redirector); - } else { - renderFirstFactor(res); - } - resolve(); - }); - }; -} diff --git a/themes/squares/server/src/lib/routes/firstfactor/post.spec.ts b/themes/squares/server/src/lib/routes/firstfactor/post.spec.ts deleted file mode 100644 index e1d078cdd..000000000 --- a/themes/squares/server/src/lib/routes/firstfactor/post.spec.ts +++ /dev/null @@ -1,136 +0,0 @@ - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import FirstFactorPost = require("./post"); -import exceptions = require("../../Exceptions"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import Endpoints = require("../../../../../shared/api"); -import AuthenticationRegulatorMock = require("../../regulation/RegulatorStub.spec"); -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; - -describe("routes/firstfactor/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let emails: string[]; - let groups: string[]; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let authSession: AuthenticationSession; - - beforeEach(function () { - emails = ["test_ok@example.com"]; - groups = ["group1", "group2" ]; - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - mocks.authorizer.authorizationMock.returns(true); - mocks.regulator.regulateStub.returns(BluebirdPromise.resolve()); - mocks.regulator.markStub.returns(BluebirdPromise.resolve()); - - req = { - originalUrl: "/api/firstfactor", - body: { - username: "username", - password: "password" - }, - query: { - redirect: "http://redirect.url" - }, - session: { - cookie: {} - }, - headers: { - host: "home.example.com" - } - }; - - res = ExpressMock.ResponseMock(); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - it("should reply with 204 if success", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal("username", authSession.userid); - Assert(res.send.calledOnce); - }); - }); - - describe("keep me logged in", () => { - beforeEach(() => { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - req.body.keepMeLoggedIn = "true"; - return FirstFactorPost.default(vars)(req as any, res as any); - }); - - it("should set keep_me_logged_in session variable to true", function () { - Assert.equal(authSession.keep_me_logged_in, true); - }); - - it("should set cookie maxAge to one year", function () { - Assert.equal(req.session.cookie.maxAge, 365 * 24 * 60 * 60 * 1000); - }); - }); - - it("should retrieve email from LDAP", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve([{ mail: ["test@example.com"] }])); - return FirstFactorPost.default(vars)(req as any, res as any); - }); - - it("should set first email address as user session variable", function () { - const emails = ["test_ok@example.com"]; - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal("test_ok@example.com", authSession.email); - }); - }); - - it("should return error message when LDAP authenticator throws", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.reject(new exceptions.LdapBindError("Bad credentials"))); - - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.equal(mocks.regulator.markStub.getCall(0).args[0], "username"); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); - - it("should return error message when regulator rejects authentication", function () { - const err = new exceptions.AuthenticationRegulationError("Authentication regulation..."); - mocks.regulator.regulateStub.returns(BluebirdPromise.reject(err)); - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); -}); - - diff --git a/themes/squares/server/src/lib/routes/firstfactor/post.ts b/themes/squares/server/src/lib/routes/firstfactor/post.ts deleted file mode 100644 index 565681d6a..000000000 --- a/themes/squares/server/src/lib/routes/firstfactor/post.ts +++ /dev/null @@ -1,101 +0,0 @@ - -import Exceptions = require("../../Exceptions"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import Endpoint = require("../../../../../shared/api"); -import ErrorReplies = require("../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import UserMessages = require("../../../../../shared/UserMessages"); -import { ServerVariables } from "../../ServerVariables"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import { GroupsAndEmails } from "../../authentication/backends/GroupsAndEmails"; -import { Level as AuthenticationLevel } from "../../authentication/Level"; -import { Level as AuthorizationLevel } from "../../authorization/Level"; -import { URLDecomposer } from "../../utils/URLDecomposer"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response) - : BluebirdPromise { - const username: string = req.body.username; - const password: string = req.body.password; - const keepMeLoggedIn: boolean = req.body.keepMeLoggedIn && - req.body.keepMeLoggedIn === "true"; - let authSession: AuthenticationSession; - - if (keepMeLoggedIn) { - // Stay connected for 1 year. - vars.logger.debug(req, "User requested to stay logged in for one year."); - req.session.cookie.maxAge = 365 * 24 * 60 * 60 * 1000; - } - - return BluebirdPromise.resolve() - .then(function () { - if (!username || !password) { - return BluebirdPromise.reject(new Error("No username or password.")); - } - vars.logger.info(req, "Starting authentication of user \"%s\"", username); - authSession = AuthenticationSessionHandler.get(req, vars.logger); - return vars.regulator.regulate(username); - }) - .then(function () { - vars.logger.info(req, "No regulation applied."); - return vars.usersDatabase.checkUserPassword(username, password); - }) - .then(function (groupsAndEmails: GroupsAndEmails) { - vars.logger.info(req, - "LDAP binding successful. Retrieved information about user are %s", - JSON.stringify(groupsAndEmails)); - authSession.userid = username; - authSession.keep_me_logged_in = keepMeLoggedIn; - authSession.authentication_level = AuthenticationLevel.ONE_FACTOR; - const redirectUrl: string = req.query[Constants.REDIRECT_QUERY_PARAM] !== "undefined" - // Fuck, don't know why it is a string! - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : ""; - - const emails: string[] = groupsAndEmails.emails; - const groups: string[] = groupsAndEmails.groups; - const decomposition = URLDecomposer.fromUrl(redirectUrl); - const authorizationLevel = (decomposition) - ? vars.authorizer.authorization( - {domain: decomposition.domain, resource: decomposition.path}, - {user: username, groups: groups}) - : AuthorizationLevel.TWO_FACTOR; - - if (emails.length > 0) - authSession.email = emails[0]; - authSession.groups = groups; - - vars.logger.debug(req, "Mark successful authentication to regulator."); - vars.regulator.mark(username, true); - - if (authorizationLevel <= AuthorizationLevel.ONE_FACTOR) { - let newRedirectionUrl: string = redirectUrl; - if (!newRedirectionUrl) - newRedirectionUrl = Endpoint.LOGGED_IN; - res.send({ - redirect: newRedirectionUrl - }); - vars.logger.debug(req, "Redirect to '%s'", redirectUrl); - } - else { - let newRedirectUrl = Endpoint.SECOND_FACTOR_GET; - if (redirectUrl) { - newRedirectUrl += "?" + Constants.REDIRECT_QUERY_PARAM + "=" - + redirectUrl; - } - vars.logger.debug(req, "Redirect to '%s'", newRedirectUrl); - res.send({ - redirect: newRedirectUrl - }); - } - return BluebirdPromise.resolve(); - }) - .catch(Exceptions.LdapBindError, function (err: Error) { - vars.regulator.mark(username, false); - return ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.OPERATION_FAILED)(err); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.OPERATION_FAILED)); - }; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/loggedin/get.ts b/themes/squares/server/src/lib/routes/loggedin/get.ts deleted file mode 100644 index 283a041b1..000000000 --- a/themes/squares/server/src/lib/routes/loggedin/get.ts +++ /dev/null @@ -1,23 +0,0 @@ -import Express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; -import ErrorReplies = require("../../ErrorReplies"); - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - res.render("already-logged-in", { - logout_endpoint: Endpoints.LOGOUT_GET, - username: authSession.userid, - redirection_url: vars.config.default_redirection_url - }); - resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - } - - return handler; -} diff --git a/themes/squares/server/src/lib/routes/logout/get.ts b/themes/squares/server/src/lib/routes/logout/get.ts deleted file mode 100644 index 4d5112146..000000000 --- a/themes/squares/server/src/lib/routes/logout/get.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import express = require("express"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import { ServerVariables } from "../../ServerVariables"; - -function getRedirectParam(req: express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -export default function (vars: ServerVariables) { - return function(req: express.Request, res: express.Response) { - const redirect_param = getRedirectParam(req); - const redirect_url = redirect_param || "/"; - AuthenticationSessionHandler.reset(req); - res.redirect(redirect_url); - }; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/password-reset/constants.ts b/themes/squares/server/src/lib/routes/password-reset/constants.ts deleted file mode 100644 index 5c639e92a..000000000 --- a/themes/squares/server/src/lib/routes/password-reset/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const CHALLENGE = "reset-password"; \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/password-reset/form/post.spec.ts b/themes/squares/server/src/lib/routes/password-reset/form/post.spec.ts deleted file mode 100644 index ed029c906..000000000 --- a/themes/squares/server/src/lib/routes/password-reset/form/post.spec.ts +++ /dev/null @@ -1,122 +0,0 @@ - -import PasswordResetFormPost = require("./post"); -import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../types/AuthenticationSession"; -import { UserDataStore } from "../../../storage/UserDataStore"; -import Sinon = require("sinon"); -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../ServerVariables"; -import { Level } from "../../../authentication/Level"; - -describe("routes/password-reset/form/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = { - originalUrl: "/api/password-reset", - body: { - userid: "user" - }, - session: {}, - headers: { - host: "localhost" - } - }; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - - mocks.config.authentication_backend.ldap = { - url: "ldap://ldapjs", - mail_attribute: "mail", - user: "user", - password: "password", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "user", - group_name_attribute: "cn", - groups_filter: "groups" - }; - - res = ExpressMock.ResponseMock(); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - authSession.userid = "user"; - authSession.email = "user@example.com"; - authSession.authentication_level = Level.ONE_FACTOR; - }); - - describe("test reset password post", () => { - it("should update the password and reset auth_session for reauthentication", function () { - req.body = {}; - req.body.password = "new-password"; - - mocks.usersDatabase.updatePasswordStub.returns(BluebirdPromise.resolve()); - - authSession.identity_check = { - userid: "user", - challenge: "reset-password" - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }).then(function (_authSession) { - Assert.equal(res.status.getCall(0).args[0], 204); - Assert.equal(_authSession.authentication_level, Level.NOT_AUTHENTICATED); - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if identity_challenge does not exist", function () { - authSession.identity_check = { - userid: "user", - challenge: undefined - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "An error occurred during password reset. Your password has not been changed." - }); - }); - }); - - it("should fail when ldap fails", function () { - req.body = {}; - req.body.password = "new-password"; - - mocks.usersDatabase.updatePasswordStub - .returns(BluebirdPromise.reject("Internal error with LDAP")); - - authSession.identity_check = { - challenge: "reset-password", - userid: "user" - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "An error occurred during password reset. Your password has not been changed." - }); - return BluebirdPromise.resolve(); - }); - }); - }); -}); diff --git a/themes/squares/server/src/lib/routes/password-reset/form/post.ts b/themes/squares/server/src/lib/routes/password-reset/form/post.ts deleted file mode 100644 index fccd7471b..000000000 --- a/themes/squares/server/src/lib/routes/password-reset/form/post.ts +++ /dev/null @@ -1,50 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); -import exceptions = require("../../../Exceptions"); -import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../types/AuthenticationSession"; -import ErrorReplies = require("../../../ErrorReplies"); -import UserMessages = require("../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../ServerVariables"; - -import Constants = require("./../constants"); - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const newPassword = objectPath.get(req, "body.password"); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.identity_check) { - reject(new Error("No identity check initiated")); - return; - } - - vars.logger.info(req, "User %s wants to reset his/her password.", - authSession.identity_check.userid); - vars.logger.debug(req, "Challenge %s", authSession.identity_check.challenge); - - if (authSession.identity_check.challenge != Constants.CHALLENGE) { - reject(new Error("Bad challenge.")); - return; - } - resolve(); - }) - .then(function () { - return vars.usersDatabase.updatePassword(authSession.identity_check.userid, newPassword); - }) - .then(function () { - vars.logger.info(req, "Password reset for user '%s'", - authSession.identity_check.userid); - AuthenticationSessionHandler.reset(req); - res.status(204); - res.send(); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.RESET_PASSWORD_FAILED)); - }; -} diff --git a/themes/squares/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts b/themes/squares/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts deleted file mode 100644 index ac6a41754..000000000 --- a/themes/squares/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts +++ /dev/null @@ -1,92 +0,0 @@ - -import PasswordResetHandler - from "./PasswordResetHandler"; -import { UserDataStore } from "../../../storage/UserDataStore"; -import Sinon = require("sinon"); -import winston = require("winston"); -import assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../ServerVariables"; - -describe("routes/password-reset/identity/PasswordResetHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = { - originalUrl: "/non-api/xxx", - query: { - userid: "user" - }, - session: { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false - } - }, - headers: { - host: "localhost" - } - }; - - const options = { - inMemoryOnly: true - }; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - mocks.userDataStore.saveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - res = ExpressMock.ResponseMock(); - }); - - describe("test reset password identity pre check", () => { - it("should fail when no userid is provided", function () { - req.query.userid = undefined; - const handler = new PasswordResetHandler(vars.logger, - vars.usersDatabase); - return handler.preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject("It should fail"); - }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if ldap fail", function () { - mocks.usersDatabase.getEmailsStub - .returns(BluebirdPromise.reject("Internal error")); - new PasswordResetHandler(vars.logger, vars.usersDatabase) - .preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should returns identity when ldap replies", function () { - mocks.usersDatabase.getEmailsStub - .returns(BluebirdPromise.resolve(["test@example.com"])); - return new PasswordResetHandler(vars.logger, vars.usersDatabase) - .preValidationInit(req as any); - }); - }); -}); diff --git a/themes/squares/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts b/themes/squares/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts deleted file mode 100644 index 42ae92cda..000000000 --- a/themes/squares/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts +++ /dev/null @@ -1,69 +0,0 @@ -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); - -import exceptions = require("../../../Exceptions"); -import { Identity } from "../../../../../types/Identity"; -import { IdentityValidable } from "../../../IdentityValidable"; -import { PRE_VALIDATION_TEMPLATE } from "../../../IdentityCheckPreValidationTemplate"; -import Constants = require("../constants"); -import { IRequestLogger } from "../../../logging/IRequestLogger"; -import { IUsersDatabase } from "../../../authentication/backends/IUsersDatabase"; - -export const TEMPLATE_NAME = "password-reset-form"; - -export default class PasswordResetHandler implements IdentityValidable { - private logger: IRequestLogger; - private usersDatabase: IUsersDatabase; - - constructor(logger: IRequestLogger, usersDatabase: IUsersDatabase) { - this.logger = logger; - this.usersDatabase = usersDatabase; - } - - challenge(): string { - return Constants.CHALLENGE; - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - const userid: string = - objectPath.get(req, "query.userid"); - return BluebirdPromise.resolve() - .then(function () { - that.logger.debug(req, "User '%s' requested a password reset", userid); - if (!userid) - return BluebirdPromise.reject( - new exceptions.AccessDeniedError("No user id provided")); - - return that.usersDatabase.getEmails(userid); - }) - .then(function (emails: string[]) { - if (!emails && emails.length <= 0) throw new Error("No email found"); - const identity = { - email: emails[0], - userid: userid - }; - return BluebirdPromise.resolve(identity); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject(new exceptions.IdentityError(err.message)); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return BluebirdPromise.resolve(); - } - - postValidationResponse(req: express.Request, res: express.Response) { - res.render(TEMPLATE_NAME); - } - - mailSubject(): string { - return "Reset your password"; - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/password-reset/request/get.ts b/themes/squares/server/src/lib/routes/password-reset/request/get.ts deleted file mode 100644 index 8f3ae2b4b..000000000 --- a/themes/squares/server/src/lib/routes/password-reset/request/get.ts +++ /dev/null @@ -1,13 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); -import exceptions = require("../../../Exceptions"); - -import Constants = require("./../constants"); - -const TEMPLATE_NAME = "password-reset-request"; - -export default function (req: express.Request, res: express.Response) { - res.render(TEMPLATE_NAME); -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/secondfactor/get.spec.ts b/themes/squares/server/src/lib/routes/secondfactor/get.spec.ts deleted file mode 100644 index 6c77e1f69..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/get.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import SecondFactorGet from "./get"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; -import Sinon = require("sinon"); -import ExpressMock = require("../../stubs/express.spec"); -import Assert = require("assert"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); - -describe("routes/secondfactor/get", function () { - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false - } - }; - }); - - describe("test rendering", function () { - it("should render second factor page", function () { - req.session.auth.second_factor = false; - return SecondFactorGet(vars)(req as any, res as any) - .then(function () { - Assert(res.render.calledWith("secondfactor")); - return BluebirdPromise.resolve(); - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/secondfactor/get.ts b/themes/squares/server/src/lib/routes/secondfactor/get.ts deleted file mode 100644 index 9f6deb4c6..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/get.ts +++ /dev/null @@ -1,28 +0,0 @@ - -import Express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; - -const TEMPLATE_NAME = "secondfactor"; - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response) - : BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - - res.render(TEMPLATE_NAME, { - username: authSession.userid, - totp_identity_start_endpoint: - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - u2f_identity_start_endpoint: - Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET - }); - resolve(); - }); - } - return handler; -} diff --git a/themes/squares/server/src/lib/routes/secondfactor/redirect.spec.ts b/themes/squares/server/src/lib/routes/secondfactor/redirect.spec.ts deleted file mode 100644 index ea66e6dca..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/redirect.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import Redirect from "./redirect"; -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariablesMockBuilder, ServerVariablesMock } -from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; -import Assert = require("assert"); - -describe("routes/secondfactor/redirect", function() { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - }); - - it("should redirect to default_redirection_url", function() { - vars.config.default_redirection_url = "http://default_redirection_url"; - Redirect(vars)(req as any, res as any) - .then(function() { - Assert(res.json.calledWith({ - redirect: "http://default_redirection_url" - })); - }); - }); - - it("should redirect to /", function() { - Redirect(vars)(req as any, res as any) - .then(function() { - Assert(res.json.calledWith({ - redirect: "/" - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/secondfactor/redirect.ts b/themes/squares/server/src/lib/routes/secondfactor/redirect.ts deleted file mode 100644 index 5d84d9ebf..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/redirect.ts +++ /dev/null @@ -1,30 +0,0 @@ - -import express = require("express"); -import objectPath = require("object-path"); -import Endpoints = require("../../../../../shared/api"); -import { ServerVariables } from "../../ServerVariables"; -import BluebirdPromise = require("bluebird"); -import ErrorReplies = require("../../ErrorReplies"); -import UserMessages = require("../../../../../shared/UserMessages"); -import { RedirectionMessage } from "../../../../../shared/RedirectionMessage"; -import Constants = require("../../../../../shared/constants"); - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response) - : BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - let redirectUrl: string = "/"; - if (vars.config.default_redirection_url) { - redirectUrl = vars.config.default_redirection_url; - } - vars.logger.debug(req, "Request redirection to \"%s\".", redirectUrl); - res.json({ - redirect: redirectUrl - } as RedirectionMessage); - return resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - }; -} diff --git a/themes/squares/server/src/lib/routes/secondfactor/totp/constants.ts b/themes/squares/server/src/lib/routes/secondfactor/totp/constants.ts deleted file mode 100644 index 7b5a1dcfe..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/totp/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export const CHALLENGE = "totp-register"; -export const TEMPLATE_NAME = "totp-register"; - diff --git a/themes/squares/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts b/themes/squares/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts deleted file mode 100644 index 78b8ea3ea..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts +++ /dev/null @@ -1,116 +0,0 @@ -import Sinon = require("sinon"); -import RegistrationHandler from "./RegistrationHandler"; -import { Identity } from "../../../../../../types/Identity"; -import { UserDataStore } from "../../../../storage/UserDataStore"; -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; -import Assert = require("assert"); - -describe("routes/secondfactor/totp/identity/RegistrationHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - req.session = { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false, - identity_check: { - userid: "user", - challenge: "totp-register" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.saveTOTPSecretStub - .returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - }); - - describe("test totp registration pre validation", function () { - it("should fail if first_factor has not been passed", function () { - req.session.auth.first_factor = false; - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("It should fail")); - }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if userid is missing", function (done) { - req.session.auth.first_factor = false; - req.session.auth.userid = undefined; - - new RegistrationHandler(vars.logger, vars.userDataStore, vars.totpHandler, - vars.config.totp) - .preValidationInit(req as any) - .catch(function (err: Error) { - done(); - }); - }); - - it("should fail if email is missing", function (done) { - req.session.auth.first_factor = false; - req.session.auth.email = undefined; - - new RegistrationHandler(vars.logger, vars.userDataStore, vars.totpHandler, - vars.config.totp) - .preValidationInit(req as any) - .catch(function (err: Error) { - done(); - }); - }); - - it("should succeed if first factor passed, userid and email are provided", - function () { - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .preValidationInit(req as any); - }); - }); - - describe("test totp registration post validation", function () { - it("should generate a secret using userId as label and issuer defined in config", function () { - vars.config.totp = { - issuer: "issuer" - }; - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .postValidationResponse(req as any, res as any) - .then(function() { - Assert(mocks.totpHandler.generateStub.calledWithExactly("user", "issuer")); - }); - }); - }); -}); diff --git a/themes/squares/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts b/themes/squares/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts deleted file mode 100644 index b39b6d045..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts +++ /dev/null @@ -1,112 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); - -import { Identity } from "../../../../../../types/Identity"; -import { IdentityValidable } from "../../../../IdentityValidable"; -import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate"; -import Constants = require("../constants"); -import Endpoints = require("../../../../../../../shared/api"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import FirstFactorValidator = require("../../../../FirstFactorValidator"); -import { IRequestLogger } from "../../../../logging/IRequestLogger"; -import { IUserDataStore } from "../../../../storage/IUserDataStore"; -import { ITotpHandler } from "../../../../authentication/totp/ITotpHandler"; -import { TOTPSecret } from "../../../../../../types/TOTPSecret"; -import { TotpConfiguration } from "../../../../configuration/schema/TotpConfiguration"; - - -export default class RegistrationHandler implements IdentityValidable { - private logger: IRequestLogger; - private userDataStore: IUserDataStore; - private totp: ITotpHandler; - private configuration: TotpConfiguration; - - constructor(logger: IRequestLogger, - userDataStore: IUserDataStore, - totp: ITotpHandler, configuration: TotpConfiguration) { - this.logger = logger; - this.userDataStore = userDataStore; - this.totp = totp; - this.configuration = configuration; - } - - challenge(): string { - return Constants.CHALLENGE; - } - - private retrieveIdentity(req: express.Request): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - const userid = authSession.userid; - const email = authSession.email; - - if (!(userid && email)) { - return reject(new Error("User ID or email is missing")); - } - - const identity = { - email: email, - userid: userid - }; - return resolve(identity); - }); - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - return FirstFactorValidator.validate(req, this.logger) - .then(function () { - return that.retrieveIdentity(req); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return FirstFactorValidator.validate(req, this.logger); - } - - postValidationResponse(req: express.Request, res: express.Response) - : BluebirdPromise { - const that = this; - let secret: TOTPSecret; - let userId: string; - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - userId = authSession.userid; - - if (authSession.identity_check.challenge != Constants.CHALLENGE - || !userId) - return reject(new Error("Bad challenge.")); - - resolve(); - }) - .then(function () { - secret = that.totp.generate(userId, - that.configuration.issuer); - that.logger.debug(req, "Save the TOTP secret in DB"); - return that.userDataStore.saveTOTPSecret(userId, secret); - }) - .then(function () { - AuthenticationSessionHandler.reset(req); - - res.render(Constants.TEMPLATE_NAME, { - base32_secret: secret.base32, - otpauth_url: secret.otpauth_url, - login_endpoint: Endpoints.FIRST_FACTOR_GET - }); - }) - .catch(ErrorReplies.replyWithError200(req, res, that.logger, UserMessages.OPERATION_FAILED)); - } - - mailSubject(): string { - return "Set up Authelia's one-time password"; - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts b/themes/squares/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts deleted file mode 100644 index 70a20d39d..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import Assert = require("assert"); -import Exceptions = require("../../../../Exceptions"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import SignPost = require("./post"); -import { ServerVariables } from "../../../../ServerVariables"; - -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { Level } from "../../../../authentication/Level"; - -describe("routes/secondfactor/totp/sign/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let authSession: AuthenticationSession; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - const app_get = Sinon.stub(); - req = { - originalUrl: "/api/totp-register", - app: {}, - body: { - token: "abc" - }, - session: {}, - query: { - redirect: "http://redirect" - } - }; - res = ExpressMock.ResponseMock(); - - const doc = { - userid: "user", - secret: { - base32: "ABCDEF" - } - }; - mocks.userDataStore.retrieveTOTPSecretStub.returns(BluebirdPromise.resolve(doc)); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - authSession.userid = "user"; - authSession.authentication_level = Level.ONE_FACTOR; - }); - - - it("should send status code 200 when totp is valid", function () { - mocks.totpHandler.validateStub.returns(true); - return SignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(authSession.authentication_level, Level.TWO_FACTOR); - return BluebirdPromise.resolve(); - }); - }); - - it("should send error message when totp is not valid", function () { - mocks.totpHandler.validateStub.returns(false); - return SignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.notEqual(authSession.authentication_level, Level.TWO_FACTOR); - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); -}); - diff --git a/themes/squares/server/src/lib/routes/secondfactor/totp/sign/post.ts b/themes/squares/server/src/lib/routes/secondfactor/totp/sign/post.ts deleted file mode 100644 index 34a276d12..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/totp/sign/post.ts +++ /dev/null @@ -1,42 +0,0 @@ -import Bluebird = require("bluebird"); -import Express = require("express"); - -import { TOTPSecretDocument } from "../../../../storage/TOTPSecretDocument"; -import Endpoints = require("../../../../../../../shared/api"); -import Redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; -import { Level } from "../../../../authentication/Level"; - -const UNAUTHORIZED_MESSAGE = "Unauthorized access"; - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response): Bluebird { - let authSession: AuthenticationSession; - const token = req.body.token; - - return new Bluebird(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - vars.logger.info(req, "Initiate TOTP validation for user \"%s\".", authSession.userid); - resolve(); - }) - .then(function () { - return vars.userDataStore.retrieveTOTPSecret(authSession.userid); - }) - .then(function (doc: TOTPSecretDocument) { - if (!vars.totpHandler.validate(token, doc.secret.base32)) - return Bluebird.reject(new Error("Invalid TOTP token.")); - - vars.logger.debug(req, "TOTP validation succeeded."); - authSession.authentication_level = Level.TWO_FACTOR; - Redirect(vars)(req, res); - return Bluebird.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/squares/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts b/themes/squares/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts deleted file mode 100644 index 7f16c0ee8..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts +++ /dev/null @@ -1,11 +0,0 @@ - -import util = require("util"); -import express = require("express"); - -function extract_app_id(req: express.Request): string { - return util.format("https://%s", req.headers.host); -} - -export = { - extract_app_id: extract_app_id -}; \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts b/themes/squares/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts deleted file mode 100644 index a54bfbfe6..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); - -import { Identity } from "../../../../../../types/Identity"; -import RegistrationHandler from "./RegistrationHandler"; -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -describe("routes/secondfactor/u2f/identity/RegistrationHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - req.app = {}; - req.session = { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = Sinon.spy(); - res.json = Sinon.spy(); - res.status = Sinon.spy(); - }); - - describe("test u2f registration check", test_registration_check); - - function test_registration_check() { - it("should fail if first_factor has not been passed", function () { - req.session.auth.first_factor = false; - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if userid is missing", function () { - req.session.auth.first_factor = false; - req.session.auth.userid = undefined; - - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if email is missing", function () { - req.session.auth.first_factor = false; - req.session.auth.email = undefined; - - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should succeed if first factor passed, userid and email are provided", function () { - req.session.auth.first_factor = true; - req.session.auth.email = "admin@example.com"; - req.session.auth.userid = "user"; - return new RegistrationHandler(vars.logger).preValidationInit(req as any); - }); - } -}); diff --git a/themes/squares/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts b/themes/squares/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts deleted file mode 100644 index bc4713c77..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts +++ /dev/null @@ -1,73 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import objectPath = require("object-path"); - -import { IdentityValidable } from "../../../../IdentityValidable"; -import { Identity } from "../../../../../../types/Identity"; -import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate"; -import FirstFactorValidator = require("../../../../FirstFactorValidator"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { IRequestLogger } from "../../../../logging/IRequestLogger"; - -const CHALLENGE = "u2f-register"; -const MAIL_SUBJECT = "Register your security key with Authelia"; - -const POST_VALIDATION_TEMPLATE_NAME = "u2f-register"; - - -export default class RegistrationHandler implements IdentityValidable { - private logger: IRequestLogger; - - constructor(logger: IRequestLogger) { - this.logger = logger; - } - - challenge(): string { - return CHALLENGE; - } - - private retrieveIdentity(req: express.Request): BluebirdPromise { - const that = this; - return new BluebirdPromise(function(resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - const userid = authSession.userid; - const email = authSession.email; - - if (!(userid && email)) { - return reject(new Error("User ID or email is missing")); - } - - const identity = { - email: email, - userid: userid - }; - return resolve(identity); - }); - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - return FirstFactorValidator.validate(req, this.logger) - .then(function () { - return that.retrieveIdentity(req); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return FirstFactorValidator.validate(req, this.logger); - } - - postValidationResponse(req: express.Request, res: express.Response) { - res.render(POST_VALIDATION_TEMPLATE_NAME); - } - - mailSubject(): string { - return MAIL_SUBJECT; - } -} - diff --git a/themes/squares/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts b/themes/squares/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts deleted file mode 100644 index de3347a21..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts +++ /dev/null @@ -1,146 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import assert = require("assert"); -import U2FRegisterPost = require("./post"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - - -describe("routes/secondfactor/u2f/register/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - describe("test registration", test_registration); - - - function test_registration() { - it("should save u2f meta and return status code 200", function () { - const expectedStatus = { - keyHandle: "keyHandle", - publicKey: "pbk", - certificate: "cert" - }; - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve(expectedStatus)); - - authSession.register_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { - assert.equal("user", mocks.userDataStore.saveU2FRegistrationStub.getCall(0).args[0]); - assert.equal(authSession.identity_check, undefined); - }); - }); - - it("should return error message on finishRegistration error", function () { - mocks.u2f.checkRegistrationStub.returns({ errorCode: 500 }); - - authSession.register_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when register_request is not provided", function () { - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve()); - authSession.register_request = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when no auth request has been initiated", function () { - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve()); - authSession.register_request = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when identity has not been verified", function () { - authSession.identity_check = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - } -}); - diff --git a/themes/squares/server/src/lib/routes/secondfactor/u2f/register/post.ts b/themes/squares/server/src/lib/routes/secondfactor/u2f/register/post.ts deleted file mode 100644 index 7296ccbe5..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/u2f/register/post.ts +++ /dev/null @@ -1,64 +0,0 @@ - -import { UserDataStore } from "../../../../storage/UserDataStore"; -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import U2f = require("u2f"); -import { U2FRegistration } from "../../../../../../types/U2FRegistration"; -import redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; - - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appid = u2f_common.extract_app_id(req); - const registrationResponse: U2f.RegistrationData = req.body; - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - const registrationRequest = authSession.register_request; - - if (!registrationRequest) { - return reject(new Error("No registration request")); - } - - if (!authSession.identity_check - || authSession.identity_check.challenge != "u2f-register") { - return reject(new Error("Bad challenge for registration request")); - } - - vars.logger.info(req, "Finishing registration"); - vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest)); - vars.logger.debug(req, "RegistrationResponse = %s", JSON.stringify(registrationResponse)); - - return resolve(vars.u2f.checkRegistration(registrationRequest, registrationResponse)); - }) - .then(function (u2fResult: U2f.RegistrationResult | U2f.Error): BluebirdPromise { - if (objectPath.has(u2fResult, "errorCode")) - return BluebirdPromise.reject(new Error("Error while registering.")); - - const registrationResult: U2f.RegistrationResult = u2fResult as U2f.RegistrationResult; - vars.logger.info(req, "Store registration and reply"); - vars.logger.debug(req, "RegistrationResult = %s", JSON.stringify(registrationResult)); - const registration: U2FRegistration = { - keyHandle: registrationResult.keyHandle, - publicKey: registrationResult.publicKey - }; - return vars.userDataStore.saveU2FRegistration(authSession.userid, appid, registration); - }) - .then(function () { - authSession.identity_check = undefined; - redirect(vars)(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/squares/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts b/themes/squares/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts deleted file mode 100644 index a207c9109..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts +++ /dev/null @@ -1,86 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import U2FRegisterRequestGet = require("./get"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -describe("routes/secondfactor/u2f/register_request/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - describe("test registration request", () => { - it("should send back the registration request and save it in the session", function () { - const expectedRequest = { - test: "abc" - }; - mocks.u2f.requestStub.returns(BluebirdPromise.resolve(expectedRequest)); - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.deepEqual(expectedRequest, res.json.getCall(0).args[0]); - }); - }); - - it("should return internal error on registration request", function () { - res.send = sinon.spy(); - const user_key_container = {}; - mocks.u2f.requestStub.returns(BluebirdPromise.reject("Internal error")); - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); - - it("should return forbidden if identity has not been verified", function () { - req.session.auth.identity_check = undefined; - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(403, res.status.getCall(0).args[0]); - }); - }); - }); -}); - diff --git a/themes/squares/server/src/lib/routes/secondfactor/u2f/register_request/get.ts b/themes/squares/server/src/lib/routes/secondfactor/u2f/register_request/get.ts deleted file mode 100644 index f611af933..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/u2f/register_request/get.ts +++ /dev/null @@ -1,43 +0,0 @@ - -import { UserDataStore } from "../../../../storage/UserDataStore"; - -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import U2f = require("u2f"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appid: string = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.identity_check - || authSession.identity_check.challenge != "u2f-register") { - res.status(403); - res.send(); - return reject(new Error("Bad challenge.")); - } - - vars.logger.info(req, "Starting registration for appId '%s'", appid); - return resolve(vars.u2f.request(appid)); - }) - .then(function (registrationRequest: U2f.Request) { - vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest)); - authSession.register_request = registrationRequest; - res.json(registrationRequest); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - - return handler; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts b/themes/squares/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts deleted file mode 100644 index 9b137e66d..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts +++ /dev/null @@ -1,101 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import U2FSignPost = require("./post"); -import { ServerVariables } from "../../../../ServerVariables"; -import winston = require("winston"); - -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import ExpressMock = require("../../../../stubs/express.spec"); -import U2FMock = require("../../../../stubs/u2f.spec"); -import U2f = require("u2f"); -import { Level } from "../../../../authentication/Level"; - -describe("routes/secondfactor/u2f/sign/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.app = {}; - req.originalUrl = "/api/xxxx"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req.session = { - auth: { - userid: "user", - authentication_level: Level.ONE_FACTOR, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - it("should return status code 204", function () { - const expectedStatus = { - keyHandle: "keyHandle", - publicKey: "pbk", - certificate: "cert" - }; - mocks.u2f.checkSignatureStub.returns(expectedStatus); - - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({ - registration: { - publicKey: "PUBKEY" - } - })); - - req.session.auth.sign_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FSignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(req.session.auth.authentication_level, Level.TWO_FACTOR); - }); - }); - - it("should return unauthorized error on registration request internal error", function () { - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({ - registration: { - publicKey: "PUBKEY" - } - })); - mocks.u2f.checkSignatureStub.returns({ errorCode: 500 }); - - req.session.auth.sign_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FSignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], - { error: "Operation failed." }); - }); - }); -}); - diff --git a/themes/squares/server/src/lib/routes/secondfactor/u2f/sign/post.ts b/themes/squares/server/src/lib/routes/secondfactor/u2f/sign/post.ts deleted file mode 100644 index 7ee711c2c..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/u2f/sign/post.ts +++ /dev/null @@ -1,57 +0,0 @@ - -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import { UserDataStore } from "../../../../storage/UserDataStore"; -import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocument"; -import { Winston } from "../../../../../../types/Dependencies"; -import U2f = require("u2f"); -import exceptions = require("../../../../Exceptions"); -import redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import { Level } from "../../../../authentication/Level"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appId = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.sign_request) { - const err = new Error("No sign request"); - ErrorReplies.replyWithError401(req, res, vars.logger)(err); - return reject(err); - } - resolve(); - }) - .then(function () { - const userid = authSession.userid; - return vars.userDataStore.retrieveU2FRegistration(userid, appId); - }) - .then(function (doc: U2FRegistrationDocument): BluebirdPromise { - const signRequest = authSession.sign_request; - const signData: U2f.SignatureData = req.body; - vars.logger.info(req, "Finish authentication"); - return BluebirdPromise.resolve(vars.u2f.checkSignature(signRequest, signData, doc.registration.publicKey)); - }) - .then(function (result: U2f.SignatureResult | U2f.Error): BluebirdPromise { - if (objectPath.has(result, "errorCode")) - return BluebirdPromise.reject(new Error("Error while signing")); - vars.logger.info(req, "Successful authentication"); - authSession.authentication_level = Level.TWO_FACTOR; - redirect(vars)(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - - return handler; -} - diff --git a/themes/squares/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts b/themes/squares/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts deleted file mode 100644 index dd52b27e0..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import assert = require("assert"); -import U2FSignRequestGet = require("./get"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { Request } from "u2f"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -import { SignMessage } from "../../../../../../../shared/SignMessage"; - -describe("routes/secondfactor/u2f/sign_request/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - it("should send back the sign request and save it in the session", function () { - const expectedRequest: Request = { - version: "U2F_V2", - appId: 'app', - challenge: 'challenge!' - }; - mocks.u2f.requestStub.returns(expectedRequest); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({ - registration: { - keyHandle: "KeyHandle" - } - })); - - return U2FSignRequestGet.default(vars)(req as any, res as any) - .then(() => { - assert.deepEqual(expectedRequest, req.session.auth.sign_request); - assert.deepEqual(expectedRequest, res.json.getCall(0).args[0]); - }); - }); -}); - diff --git a/themes/squares/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts b/themes/squares/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts deleted file mode 100644 index 9e93dde06..000000000 --- a/themes/squares/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts +++ /dev/null @@ -1,42 +0,0 @@ - -import u2f_common = require("../../../secondfactor/u2f/U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocument"; -import exceptions = require("../../../../Exceptions"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appId = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - resolve(); - }) - .then(function () { - return vars.userDataStore.retrieveU2FRegistration(authSession.userid, appId); - }) - .then(function (doc: U2FRegistrationDocument): BluebirdPromise { - if (!doc) - return BluebirdPromise.reject(new exceptions.AccessDeniedError("No U2F registration document found.")); - - const appId: string = u2f_common.extract_app_id(req); - vars.logger.info(req, "Start authentication of app '%s'", appId); - vars.logger.debug(req, "AppId = %s, keyHandle = %s", appId, JSON.stringify(doc.registration.keyHandle)); - - const request = vars.u2f.request(appId, doc.registration.keyHandle); - authSession.sign_request = request; - res.json(request); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/squares/server/src/lib/routes/verify/access_control.ts b/themes/squares/server/src/lib/routes/verify/access_control.ts deleted file mode 100644 index 136239aeb..000000000 --- a/themes/squares/server/src/lib/routes/verify/access_control.ts +++ /dev/null @@ -1,51 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import Util = require("util"); - -import Exceptions = require("../../Exceptions"); - -import { Level as AuthorizationLevel } from "../../authorization/Level"; -import { Level as AuthenticationLevel } from "../../authentication/Level"; -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; - -function isAuthorized( - authorization: AuthorizationLevel, - authentication: AuthenticationLevel): boolean { - - if (authorization == AuthorizationLevel.BYPASS) { - return true; - } else if (authorization == AuthorizationLevel.ONE_FACTOR && - authentication >= AuthenticationLevel.ONE_FACTOR) { - return true; - } else if (authorization == AuthorizationLevel.TWO_FACTOR && - authentication >= AuthenticationLevel.TWO_FACTOR) { - return true; - } - return false; -} - -export default function ( - req: Express.Request, - vars: ServerVariables, - domain: string, resource: string, - user: string, groups: string[], - authenticationLevel: AuthenticationLevel) { - - return new BluebirdPromise(function (resolve, reject) { - const authorizationLevel = vars.authorizer - .authorization({domain, resource}, {user, groups}); - - if (!isAuthorized(authorizationLevel, authenticationLevel)) { - if (authorizationLevel == AuthorizationLevel.DENY) { - reject(new Exceptions.NotAuthorizedError( - Util.format("User %s is not authorized to access %s%s", user, domain, resource))); - return; - } - reject(new Exceptions.NotAuthenticatedError(Util.format( - "User '%s' is not sufficiently authorized to access %s%s.", user, domain, resource))); - return; - } - resolve(); - }); -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/verify/get.spec.ts b/themes/squares/server/src/lib/routes/verify/get.spec.ts deleted file mode 100644 index 67cf19fb6..000000000 --- a/themes/squares/server/src/lib/routes/verify/get.spec.ts +++ /dev/null @@ -1,320 +0,0 @@ - -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import Express = require("express"); -import Sinon = require("sinon"); -import winston = require("winston"); - -import VerifyGet = require("./get"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariables } from "../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../ServerVariablesMockBuilder.spec"; -import { Level } from "../../authentication/Level"; -import { Level as AuthorizationLevel } from "../../authorization/Level"; - -describe("routes/verify/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - req.originalUrl = "/api/xxxx"; - req.query = { - redirect: "undefined" - }; - AuthenticationSessionHandler.reset(req as any); - req.headers["x-original-url"] = "https://secret.example.com/"; - const s = ServerVariablesMockBuilder.build(false); - mocks = s.mocks; - vars = s.variables; - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - describe("with session cookie", function () { - it("should be already authenticated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser"); - Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup"); - Assert.equal(204, res.status.getCall(0).args[0]); - }); - }); - - function test_session(_authSession: AuthenticationSession, status_code: number) { - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert.equal(status_code, res.status.getCall(0).args[0]); - }); - } - - function test_non_authenticated_401(authSession: AuthenticationSession) { - return test_session(authSession, 401); - } - - function test_unauthorized_403(authSession: AuthenticationSession) { - return test_session(authSession, 403); - } - - function test_authorized(authSession: AuthenticationSession) { - return test_session(authSession, 204); - } - - describe("given user tries to access a 2-factor endpoint", function () { - before(function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - }); - - describe("given different cases of session", function () { - it("should not be authenticated when second factor is missing", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: "user", - authentication_level: Level.ONE_FACTOR, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when userid is missing", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: undefined, - authentication_level: Level.TWO_FACTOR, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when level is insufficient", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: "user", - authentication_level: Level.NOT_AUTHENTICATED, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when session has not be initiated", function () { - return test_non_authenticated_401(undefined); - }); - - it("should not be authenticated when domain is not allowed for user", function () { - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - req.headers["x-original-url"] = "https://test.example.com/"; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.DENY); - - return test_unauthorized_403({ - keep_me_logged_in: false, - authentication_level: Level.TWO_FACTOR, - userid: "user", - groups: ["group1", "group2"], - email: undefined, - last_activity_datetime: new Date().getTime() - }); - }); - }); - }); - - describe("given user tries to access a single factor endpoint", function () { - beforeEach(function () { - req.headers["x-original-url"] = "https://redirect.url/"; - }); - - it("should be authenticated when first factor is validated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - authSession.authentication_level = Level.ONE_FACTOR; - authSession.userid = "user1"; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWith(204)); - Assert(res.send.calledOnce); - }); - }); - - it("should be rejected with 401 when not authenticated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - authSession.authentication_level = Level.NOT_AUTHENTICATED; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWith(401)); - }); - }); - }); - - describe("inactivity period", function () { - it("should update last inactivity period on requests on /api/verify", function () { - mocks.config.session.inactivity = 200000; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - const currentTime = new Date().getTime() - 1000; - AuthenticationSessionHandler.reset(req as any); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - authSession.last_activity_datetime = currentTime; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }) - .then(function (authSession) { - Assert(authSession.last_activity_datetime > currentTime); - }); - }); - - it("should reset session when max inactivity period has been reached", function () { - mocks.config.session.inactivity = 1; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - const currentTime = new Date().getTime() - 1000; - AuthenticationSessionHandler.reset(req as any); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - authSession.last_activity_datetime = currentTime; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }) - .then(function (authSession) { - Assert.equal(authSession.authentication_level, Level.NOT_AUTHENTICATED); - Assert.equal(authSession.userid, undefined); - }); - }); - }); - }); - - describe("response type 401 | 302", function() { - it("should return error code 401", function() { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should redirect to provided redirection url", function() { - const REDIRECT_URL = "http://redirection_url.com"; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - req.query["rd"] = REDIRECT_URL; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.redirect.calledWithExactly(REDIRECT_URL)); - }); - }); - }); - - describe("with basic auth", function () { - it("should authenticate correctly", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.returns({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "john"); - Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup"); - Assert.equal(204, res.status.getCall(0).args[0]); - }); - }); - - it("should fail when endpoint is protected by two factors", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.config.access_control.rules = [{ - domain: "secret.example.com", - policy: "two_factor" - }]; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when base64 token is not valid", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic i_m*not_a_base64*token"; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when base64 token has not format user:psswd", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzOmJhZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when bad user password is provided", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when resource is restricted", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - }); -}); - diff --git a/themes/squares/server/src/lib/routes/verify/get.ts b/themes/squares/server/src/lib/routes/verify/get.ts deleted file mode 100644 index f73861696..000000000 --- a/themes/squares/server/src/lib/routes/verify/get.ts +++ /dev/null @@ -1,91 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Express = require("express"); -import Exceptions = require("../../Exceptions"); -import ErrorReplies = require("../../ErrorReplies"); -import { ServerVariables } from "../../ServerVariables"; -import GetWithSessionCookieMethod from "./get_session_cookie"; -import GetWithBasicAuthMethod from "./get_basic_auth"; -import Constants = require("../../../../../shared/constants"); -import ObjectPath = require("object-path"); - -import { AuthenticationSessionHandler } - from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; - -const REMOTE_USER = "Remote-User"; -const REMOTE_GROUPS = "Remote-Groups"; - - -function verifyWithSelectedMethod(req: Express.Request, res: Express.Response, - vars: ServerVariables, authSession: AuthenticationSession) - : () => BluebirdPromise<{ username: string, groups: string[] }> { - return function () { - const authorization: string = "" + req.headers["proxy-authorization"]; - if (authorization && authorization.startsWith("Basic ")) - return GetWithBasicAuthMethod(req, res, vars, authorization); - - return GetWithSessionCookieMethod(req, res, vars, authSession); - }; -} - -function setRedirectHeader(req: Express.Request, res: Express.Response) { - return function () { - const originalUrl = ObjectPath.get( - req, "headers.x-original-url"); - res.set("Redirect", originalUrl); - return BluebirdPromise.resolve(); - }; -} - -function setUserAndGroupsHeaders(res: Express.Response) { - return function (u: { username: string, groups: string[] }) { - res.setHeader(REMOTE_USER, u.username); - res.setHeader(REMOTE_GROUPS, u.groups.join(",")); - return BluebirdPromise.resolve(); - }; -} - -function replyWith200(res: Express.Response) { - return function () { - res.status(204); - res.send(); - }; -} - -function getRedirectParam(req: Express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -export default function (vars: ServerVariables) { - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - let authSession: AuthenticationSession; - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - resolve(); - }) - .then(setRedirectHeader(req, res)) - .then(verifyWithSelectedMethod(req, res, vars, authSession)) - .then(setUserAndGroupsHeaders(res)) - .then(replyWith200(res)) - // The user is authenticated but has restricted access -> 403 - .catch(Exceptions.NotAuthorizedError, - ErrorReplies.replyWithError403(req, res, vars.logger)) - .catch(Exceptions.NotAuthenticatedError, - ErrorReplies.replyWithError401(req, res, vars.logger)) - // The user is not yet authenticated -> 401 - .catch((err) => { - const redirectUrl = getRedirectParam(req); - if (redirectUrl) { - ErrorReplies.redirectTo(redirectUrl, req, res, vars.logger)(err); - } - else { - ErrorReplies.replyWithError401(req, res, vars.logger)(err); - } - }); - }; -} - diff --git a/themes/squares/server/src/lib/routes/verify/get_basic_auth.ts b/themes/squares/server/src/lib/routes/verify/get_basic_auth.ts deleted file mode 100644 index af23c76c9..000000000 --- a/themes/squares/server/src/lib/routes/verify/get_basic_auth.ts +++ /dev/null @@ -1,55 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import ObjectPath = require("object-path"); -import { ServerVariables } from "../../ServerVariables"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; -import AccessControl from "./access_control"; -import { URLDecomposer } from "../../utils/URLDecomposer"; -import { Level } from "../../authentication/Level"; - -export default function (req: Express.Request, res: Express.Response, - vars: ServerVariables, authorizationHeader: string) - : BluebirdPromise<{ username: string, groups: string[] }> { - let username: string; - const uri = ObjectPath.get(req, "headers.x-original-url"); - const urlDecomposition = URLDecomposer.fromUrl(uri); - - return BluebirdPromise.resolve() - .then(() => { - const base64Re = new RegExp("^Basic ((?:[A-Za-z0-9+/]{4})*" + - "(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)$"); - const isTokenValidBase64 = base64Re.test(authorizationHeader); - - if (!isTokenValidBase64) { - return BluebirdPromise.reject(new Error("No valid base64 token found in the header")); - } - - const tokenMatches = authorizationHeader.match(base64Re); - const base64Token = tokenMatches[1]; - const decodedToken = Buffer.from(base64Token, "base64").toString(); - const splittedToken = decodedToken.split(":"); - - if (splittedToken.length != 2) { - return BluebirdPromise.reject(new Error( - "The authorization token is invalid. Expecting 'userid:password'")); - } - - username = splittedToken[0]; - const password = splittedToken[1]; - return vars.usersDatabase.checkUserPassword(username, password); - }) - .then(function (groupsAndEmails) { - return AccessControl(req, vars, urlDecomposition.domain, urlDecomposition.path, - username, groupsAndEmails.groups, Level.ONE_FACTOR) - .then(() => BluebirdPromise.resolve({ - username: username, - groups: groupsAndEmails.groups - })); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject( - new Error("Unable to authenticate the user with basic auth. Cause: " - + err.message)); - }); -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/routes/verify/get_session_cookie.ts b/themes/squares/server/src/lib/routes/verify/get_session_cookie.ts deleted file mode 100644 index 070344812..000000000 --- a/themes/squares/server/src/lib/routes/verify/get_session_cookie.ts +++ /dev/null @@ -1,78 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import Util = require("util"); -import ObjectPath = require("object-path"); - -import Exceptions = require("../../Exceptions"); -import { Configuration } from "../../configuration/schema/Configuration"; -import { ServerVariables } from "../../ServerVariables"; -import { IRequestLogger } from "../../logging/IRequestLogger"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; -import { AuthenticationSessionHandler } - from "../../AuthenticationSessionHandler"; -import AccessControl from "./access_control"; -import { URLDecomposer } from "../../utils/URLDecomposer"; - -function verify_inactivity(req: Express.Request, - authSession: AuthenticationSession, - configuration: Configuration, logger: IRequestLogger) - : BluebirdPromise { - - // If inactivity is not specified, then inactivity timeout does not apply - if (!configuration.session.inactivity || authSession.keep_me_logged_in) { - return BluebirdPromise.resolve(); - } - - const lastActivityTime = authSession.last_activity_datetime; - const currentTime = new Date().getTime(); - authSession.last_activity_datetime = currentTime; - - const inactivityPeriodMs = currentTime - lastActivityTime; - logger.debug(req, "Inactivity period was %s s and max period was %s.", - inactivityPeriodMs / 1000, configuration.session.inactivity / 1000); - if (inactivityPeriodMs < configuration.session.inactivity) { - return BluebirdPromise.resolve(); - } - - logger.debug(req, "Session has been reset after too long inactivity period."); - AuthenticationSessionHandler.reset(req); - return BluebirdPromise.reject(new Error("Inactivity period exceeded.")); -} - -export default function (req: Express.Request, res: Express.Response, - vars: ServerVariables, authSession: AuthenticationSession) - : BluebirdPromise<{ username: string, groups: string[] }> { - - return BluebirdPromise.resolve() - .then(() => { - const username = authSession.userid; - const groups = authSession.groups; - - if (!authSession.userid) { - return BluebirdPromise.reject(new Exceptions.AccessDeniedError( - "userid is missing")); - } - - const originalUrl = ObjectPath.get( - req, "headers.x-original-url"); - const originalUri = - ObjectPath.get(req, "headers.x-original-uri"); - - const d = URLDecomposer.fromUrl(originalUrl); - vars.logger.debug(req, "domain=%s, path=%s, user=%s, groups=%s", d.domain, - d.path, username, groups.join(",")); - return AccessControl(req, vars, d.domain, d.path, username, groups, - authSession.authentication_level); - }) - .then(() => { - return verify_inactivity(req, authSession, - vars.config, vars.logger); - }) - .then(() => { - return BluebirdPromise.resolve({ - username: authSession.userid, - groups: authSession.groups - }); - }); -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/AuthenticationTraceDocument.d.ts b/themes/squares/server/src/lib/storage/AuthenticationTraceDocument.d.ts deleted file mode 100644 index 69818c055..000000000 --- a/themes/squares/server/src/lib/storage/AuthenticationTraceDocument.d.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export interface AuthenticationTraceDocument { - userId: string; - date: Date; - isAuthenticationSuccessful: boolean; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/CollectionFactoryFactory.ts b/themes/squares/server/src/lib/storage/CollectionFactoryFactory.ts deleted file mode 100644 index 92b29abfa..000000000 --- a/themes/squares/server/src/lib/storage/CollectionFactoryFactory.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ICollectionFactory } from "./ICollectionFactory"; -import { NedbCollectionFactory } from "./nedb/NedbCollectionFactory"; -import { MongoCollectionFactory } from "./mongo/MongoCollectionFactory"; -import { IMongoClient } from "../connectors/mongo/IMongoClient"; - - -export class CollectionFactoryFactory { - static createNedb(options: Nedb.DataStoreOptions): ICollectionFactory { - return new NedbCollectionFactory(options); - } - - static createMongo(client: IMongoClient): ICollectionFactory { - return new MongoCollectionFactory(client); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/CollectionFactoryStub.spec.ts b/themes/squares/server/src/lib/storage/CollectionFactoryStub.spec.ts deleted file mode 100644 index 17f8bb021..000000000 --- a/themes/squares/server/src/lib/storage/CollectionFactoryStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import { ICollection } from "./ICollection"; -import { ICollectionFactory } from "./ICollectionFactory"; - -export class CollectionFactoryStub implements ICollectionFactory { - buildStub: Sinon.SinonStub; - - constructor() { - this.buildStub = Sinon.stub(); - } - - build(collectionName: string): ICollection { - return this.buildStub(collectionName); - } -} diff --git a/themes/squares/server/src/lib/storage/CollectionStub.spec.ts b/themes/squares/server/src/lib/storage/CollectionStub.spec.ts deleted file mode 100644 index 42895d672..000000000 --- a/themes/squares/server/src/lib/storage/CollectionStub.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import { ICollection } from "./ICollection"; - -export class CollectionStub implements ICollection { - findStub: Sinon.SinonStub; - findOneStub: Sinon.SinonStub; - updateStub: Sinon.SinonStub; - removeStub: Sinon.SinonStub; - insertStub: Sinon.SinonStub; - - constructor() { - this.findStub = Sinon.stub(); - this.findOneStub = Sinon.stub(); - this.updateStub = Sinon.stub(); - this.removeStub = Sinon.stub(); - this.insertStub = Sinon.stub(); - } - - find(filter: any, sortKeys: any, count: number): BluebirdPromise { - return this.findStub(filter, sortKeys, count); - } - - findOne(filter: any): BluebirdPromise { - return this.findOneStub(filter); - } - - update(filter: any, document: any, options: any): BluebirdPromise { - return this.updateStub(filter, document, options); - } - - remove(filter: any): BluebirdPromise { - return this.removeStub(filter); - } - - insert(document: any): BluebirdPromise { - return this.insertStub(document); - } -} diff --git a/themes/squares/server/src/lib/storage/ICollection.d.ts b/themes/squares/server/src/lib/storage/ICollection.d.ts deleted file mode 100644 index caa6c2a87..000000000 --- a/themes/squares/server/src/lib/storage/ICollection.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* istanbul ignore next */ -import BluebirdPromise = require("bluebird"); - -/* istanbul ignore next */ -export interface ICollection { - find(query: any, sortKeys: any, count: number): BluebirdPromise; - findOne(query: any): BluebirdPromise; - update(query: any, updateQuery: any, options?: any): BluebirdPromise; - remove(query: any): BluebirdPromise; - insert(document: any): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/ICollectionFactory.d.ts b/themes/squares/server/src/lib/storage/ICollectionFactory.d.ts deleted file mode 100644 index 39eb42c77..000000000 --- a/themes/squares/server/src/lib/storage/ICollectionFactory.d.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import { ICollection } from "./ICollection"; - -export interface ICollectionFactory { - build(collectionName: string): ICollection; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/IUserDataStore.d.ts b/themes/squares/server/src/lib/storage/IUserDataStore.d.ts deleted file mode 100644 index 81df482aa..000000000 --- a/themes/squares/server/src/lib/storage/IUserDataStore.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; - -export interface IUserDataStore { - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise; - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise; - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise; - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise; - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise; - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise; - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise; - retrieveTOTPSecret(userId: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/IdentityValidationDocument.d.ts b/themes/squares/server/src/lib/storage/IdentityValidationDocument.d.ts deleted file mode 100644 index e7fd7b3f4..000000000 --- a/themes/squares/server/src/lib/storage/IdentityValidationDocument.d.ts +++ /dev/null @@ -1,7 +0,0 @@ - -export interface IdentityValidationDocument { - userId: string; - token: string; - challenge: string; - maxDate: Date; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/TOTPSecretDocument.d.ts b/themes/squares/server/src/lib/storage/TOTPSecretDocument.d.ts deleted file mode 100644 index a6c0bf9e6..000000000 --- a/themes/squares/server/src/lib/storage/TOTPSecretDocument.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { TOTPSecret } from "../../../types/TOTPSecret"; - -export interface TOTPSecretDocument { - userid: string; - secret: TOTPSecret; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/U2FRegistrationDocument.d.ts b/themes/squares/server/src/lib/storage/U2FRegistrationDocument.d.ts deleted file mode 100644 index efec6cb1d..000000000 --- a/themes/squares/server/src/lib/storage/U2FRegistrationDocument.d.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import { U2FRegistration } from "../../../types/U2FRegistration"; - -export interface U2FRegistrationDocument { - userId: string; - appId: string; - registration: U2FRegistration; -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/UserDataStore.spec.ts b/themes/squares/server/src/lib/storage/UserDataStore.spec.ts deleted file mode 100644 index 66fb85461..000000000 --- a/themes/squares/server/src/lib/storage/UserDataStore.spec.ts +++ /dev/null @@ -1,264 +0,0 @@ - -import * as Assert from "assert"; -import * as Sinon from "sinon"; -import * as MockDate from "mockdate"; -import BluebirdPromise = require("bluebird"); - -import { UserDataStore } from "./UserDataStore"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { CollectionStub } from "./CollectionStub.spec"; -import { CollectionFactoryStub } from "./CollectionFactoryStub.spec"; - -describe("storage/UserDataStore", function () { - let factory: CollectionFactoryStub; - let collection: CollectionStub; - let userId: string; - let appId: string; - let totpSecret: TOTPSecret; - let u2fRegistration: U2FRegistration; - - beforeEach(function () { - factory = new CollectionFactoryStub(); - collection = new CollectionStub(); - - userId = "user"; - appId = "https://myappId"; - - totpSecret = { - ascii: "abc", - base32: "ABCDKZLEFZGREJK", - otpauth_url: "totp://test", - google_auth_qr: "dummy", - hex: "dummy", - qr_code_ascii: "dummy", - qr_code_base32: "dummy", - qr_code_hex: "dummy" - }; - - u2fRegistration = { - keyHandle: "KEY_HANDLE", - publicKey: "publickey" - }; - }); - - it("should correctly creates collections", function () { - new UserDataStore(factory); - - Assert.equal(4, factory.buildStub.callCount); - Assert(factory.buildStub.calledWith("authentication_traces")); - Assert(factory.buildStub.calledWith("identity_validation_tokens")); - Assert(factory.buildStub.calledWith("u2f_registrations")); - Assert(factory.buildStub.calledWith("totp_secrets")); - }); - - describe("TOTP secrets collection", function () { - it("should save a totp secret", function () { - factory.buildStub.returns(collection); - collection.updateStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveTOTPSecret(userId, totpSecret) - .then(function (doc) { - Assert(collection.updateStub.calledOnce); - Assert(collection.updateStub.calledWith({ userId: userId }, { - userId: userId, - secret: totpSecret - }, { upsert: true })); - return BluebirdPromise.resolve(); - }); - }); - - it("should retrieve a totp secret", function () { - factory.buildStub.returns(collection); - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveTOTPSecret(userId) - .then(function (doc) { - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ userId: userId })); - return BluebirdPromise.resolve(); - }); - }); - }); - - describe("U2F secrets collection", function () { - it("should save a U2F secret", function () { - factory.buildStub.returns(collection); - collection.updateStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveU2FRegistration(userId, appId, u2fRegistration) - .then(function (doc) { - Assert(collection.updateStub.calledOnce); - Assert(collection.updateStub.calledWith({ - userId: userId, - appId: appId - }, { - userId: userId, - appId: appId, - registration: u2fRegistration - }, { upsert: true })); - return BluebirdPromise.resolve(); - }); - }); - - it("should retrieve a U2F secret", function () { - factory.buildStub.returns(collection); - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveU2FRegistration(userId, appId) - .then(function (doc) { - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - userId: userId, - appId: appId - })); - return BluebirdPromise.resolve(); - }); - }); - }); - - - describe("Regulator traces collection", function () { - it("should save a trace", function () { - factory.buildStub.returns(collection); - collection.insertStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveAuthenticationTrace(userId, true) - .then(function (doc) { - Assert(collection.insertStub.calledOnce); - Assert(collection.insertStub.calledWith({ - userId: userId, - date: Sinon.match.date, - isAuthenticationSuccessful: true - })); - return BluebirdPromise.resolve(); - }); - }); - - function should_retrieve_latest_authentication_traces(count: number) { - factory.buildStub.returns(collection); - collection.findStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveLatestAuthenticationTraces(userId, count) - .then(function (doc: AuthenticationTraceDocument[]) { - Assert(collection.findStub.calledOnce); - Assert(collection.findStub.calledWith({ - userId: userId, - }, { date: -1 }, count)); - return BluebirdPromise.resolve(); - }); - } - - it("should retrieve 3 latest failed authentication traces", function () { - should_retrieve_latest_authentication_traces(3); - }); - }); - - - describe("Identity validation collection", function () { - it("should save a identity validation token", function () { - factory.buildStub.returns(collection); - collection.insertStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - const maxAge = 400; - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - return dataStore.produceIdentityValidationToken(userId, token, challenge, maxAge) - .then(function (doc) { - Assert(collection.insertStub.calledOnce); - Assert(collection.insertStub.calledWith({ - userId: userId, - token: token, - challenge: challenge, - maxDate: Sinon.match.date - })); - return BluebirdPromise.resolve(); - }); - }); - - it("should consume an identity token successfully", function () { - factory.buildStub.returns(collection); - - MockDate.set(100); - - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve({ - userId: "USER", - token: token, - challenge: challenge, - maxDate: new Date() - })); - collection.removeStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - MockDate.set(80); - - return dataStore.consumeIdentityValidationToken(token, challenge) - .then(function (doc) { - MockDate.reset(); - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - token: token, - challenge: challenge - })); - - Assert(collection.removeStub.calledOnce); - Assert(collection.removeStub.calledWith({ - token: token, - challenge: challenge - })); - return BluebirdPromise.resolve(); - }); - }); - - it("should consume an expired identity token", function () { - factory.buildStub.returns(collection); - - MockDate.set(0); - - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve({ - userId: "USER", - token: token, - challenge: challenge, - maxDate: new Date() - })); - - const dataStore = new UserDataStore(factory); - - MockDate.set(80000); - - return dataStore.consumeIdentityValidationToken(token, challenge) - .then(function () { return BluebirdPromise.reject(new Error("should not be here")); }) - .catch(function () { - MockDate.reset(); - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - token: token, - challenge: challenge - })); - return BluebirdPromise.resolve(); - }); - }); - }); -}); diff --git a/themes/squares/server/src/lib/storage/UserDataStore.ts b/themes/squares/server/src/lib/storage/UserDataStore.ts deleted file mode 100644 index 27b0cddbd..000000000 --- a/themes/squares/server/src/lib/storage/UserDataStore.ts +++ /dev/null @@ -1,143 +0,0 @@ -import * as BluebirdPromise from "bluebird"; -import * as path from "path"; -import { IUserDataStore } from "./IUserDataStore"; -import { ICollection } from "./ICollection"; -import { ICollectionFactory } from "./ICollectionFactory"; -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; - -// Constants - -const IDENTITY_VALIDATION_TOKENS_COLLECTION_NAME = "identity_validation_tokens"; -const AUTHENTICATION_TRACES_COLLECTION_NAME = "authentication_traces"; - -const U2F_REGISTRATIONS_COLLECTION_NAME = "u2f_registrations"; -const TOTP_SECRETS_COLLECTION_NAME = "totp_secrets"; - - -export interface U2FRegistrationKey { - userId: string; - appId: string; -} - -// Source - -export class UserDataStore implements IUserDataStore { - private u2fSecretCollection: ICollection; - private identityCheckTokensCollection: ICollection; - private authenticationTracesCollection: ICollection; - private totpSecretCollection: ICollection; - - private collectionFactory: ICollectionFactory; - - constructor(collectionFactory: ICollectionFactory) { - this.collectionFactory = collectionFactory; - - this.u2fSecretCollection = this.collectionFactory.build(U2F_REGISTRATIONS_COLLECTION_NAME); - this.identityCheckTokensCollection = this.collectionFactory.build(IDENTITY_VALIDATION_TOKENS_COLLECTION_NAME); - this.authenticationTracesCollection = this.collectionFactory.build(AUTHENTICATION_TRACES_COLLECTION_NAME); - this.totpSecretCollection = this.collectionFactory.build(TOTP_SECRETS_COLLECTION_NAME); - } - - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise { - const newDocument: U2FRegistrationDocument = { - userId: userId, - appId: appId, - registration: registration - }; - - const filter: U2FRegistrationKey = { - userId: userId, - appId: appId - }; - - return this.u2fSecretCollection.update(filter, newDocument, { upsert: true }); - } - - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise { - const filter: U2FRegistrationKey = { - userId: userId, - appId: appId - }; - return this.u2fSecretCollection.findOne(filter); - } - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - const newDocument: AuthenticationTraceDocument = { - userId: userId, - date: new Date(), - isAuthenticationSuccessful: isAuthenticationSuccessful, - }; - - return this.authenticationTracesCollection.insert(newDocument); - } - - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise { - const q = { - userId: userId - }; - - return this.authenticationTracesCollection.find(q, { date: -1 }, count); - } - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise { - const newDocument: IdentityValidationDocument = { - userId: userId, - token: token, - challenge: challenge, - maxDate: new Date(new Date().getTime() + maxAge) - }; - - return this.identityCheckTokensCollection.insert(newDocument); - } - - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise { - const that = this; - const filter = { - token: token, - challenge: challenge - }; - - let identityValidationDocument: IdentityValidationDocument; - - return this.identityCheckTokensCollection.findOne(filter) - .then(function (doc: IdentityValidationDocument) { - if (!doc) { - return BluebirdPromise.reject(new Error("Registration token does not exist")); - } - - identityValidationDocument = doc; - const current_date = new Date(); - if (current_date > doc.maxDate) - return BluebirdPromise.reject(new Error("Registration token is not valid anymore")); - - return that.identityCheckTokensCollection.remove(filter); - }) - .then(() => { - return BluebirdPromise.resolve(identityValidationDocument); - }); - } - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise { - const doc = { - userId: userId, - secret: secret - }; - - const filter = { - userId: userId - }; - return this.totpSecretCollection.update(filter, doc, { upsert: true }); - } - - retrieveTOTPSecret(userId: string): BluebirdPromise { - const filter = { - userId: userId - }; - return this.totpSecretCollection.findOne(filter); - } -} diff --git a/themes/squares/server/src/lib/storage/UserDataStoreStub.spec.ts b/themes/squares/server/src/lib/storage/UserDataStoreStub.spec.ts deleted file mode 100644 index 5ea27a2de..000000000 --- a/themes/squares/server/src/lib/storage/UserDataStoreStub.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); - -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; -import { IUserDataStore } from "./IUserDataStore"; - -export class UserDataStoreStub implements IUserDataStore { - saveU2FRegistrationStub: Sinon.SinonStub; - retrieveU2FRegistrationStub: Sinon.SinonStub; - saveAuthenticationTraceStub: Sinon.SinonStub; - retrieveLatestAuthenticationTracesStub: Sinon.SinonStub; - produceIdentityValidationTokenStub: Sinon.SinonStub; - consumeIdentityValidationTokenStub: Sinon.SinonStub; - saveTOTPSecretStub: Sinon.SinonStub; - retrieveTOTPSecretStub: Sinon.SinonStub; - - constructor() { - this.saveU2FRegistrationStub = Sinon.stub(); - this.retrieveU2FRegistrationStub = Sinon.stub(); - this.saveAuthenticationTraceStub = Sinon.stub(); - this.retrieveLatestAuthenticationTracesStub = Sinon.stub(); - this.produceIdentityValidationTokenStub = Sinon.stub(); - this.consumeIdentityValidationTokenStub = Sinon.stub(); - this.saveTOTPSecretStub = Sinon.stub(); - this.retrieveTOTPSecretStub = Sinon.stub(); - } - - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise { - return this.saveU2FRegistrationStub(userId, appId, registration); - } - - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise { - return this.retrieveU2FRegistrationStub(userId, appId); - } - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - return this.saveAuthenticationTraceStub(userId, isAuthenticationSuccessful); - } - - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise { - return this.retrieveLatestAuthenticationTracesStub(userId, count); - } - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise { - return this.produceIdentityValidationTokenStub(userId, token, challenge, maxAge); - } - - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise { - return this.consumeIdentityValidationTokenStub(token, challenge); - } - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise { - return this.saveTOTPSecretStub(userId, secret); - } - - retrieveTOTPSecret(userId: string): BluebirdPromise { - return this.retrieveTOTPSecretStub(userId); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/mongo/MongoCollection.spec.ts b/themes/squares/server/src/lib/storage/mongo/MongoCollection.spec.ts deleted file mode 100644 index 74a773a1e..000000000 --- a/themes/squares/server/src/lib/storage/mongo/MongoCollection.spec.ts +++ /dev/null @@ -1,110 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import MongoDB = require("mongodb"); -import BluebirdPromise = require("bluebird"); -import { MongoClientStub } from "../../connectors/mongo/MongoClientStub.spec"; -import { MongoCollection } from "./MongoCollection"; - -describe("storage/mongo/MongoCollection", function () { - let mongoCollectionStub: any; - let mongoClientStub: MongoClientStub; - let findStub: Sinon.SinonStub; - let findOneStub: Sinon.SinonStub; - let insertOneStub: Sinon.SinonStub; - let updateStub: Sinon.SinonStub; - let removeStub: Sinon.SinonStub; - let countStub: Sinon.SinonStub; - const COLLECTION_NAME = "collection"; - - before(function () { - mongoClientStub = new MongoClientStub(); - mongoCollectionStub = Sinon.createStubInstance(require("mongodb").Collection as any); - findStub = mongoCollectionStub.find as Sinon.SinonStub; - findOneStub = mongoCollectionStub.findOne as Sinon.SinonStub; - insertOneStub = mongoCollectionStub.insertOne as Sinon.SinonStub; - updateStub = mongoCollectionStub.update as Sinon.SinonStub; - removeStub = mongoCollectionStub.remove as Sinon.SinonStub; - countStub = mongoCollectionStub.count as Sinon.SinonStub; - mongoClientStub.collectionStub.returns( - BluebirdPromise.resolve(mongoCollectionStub) - ); - }); - - describe("find", function () { - it("should find a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - findStub.returns({ - sort: Sinon.stub().returns({ - limit: Sinon.stub().returns({ - toArray: Sinon.stub().returns(BluebirdPromise.resolve([])) - }) - }) - }); - - return collection.find({ key: "KEY" }) - .then(function () { - Assert(findStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("findOne", function () { - it("should find one document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - findOneStub.returns(BluebirdPromise.resolve({})); - - return collection.findOne({ key: "KEY" }) - .then(function () { - Assert(findOneStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("insert", function () { - it("should insert a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - insertOneStub.returns(BluebirdPromise.resolve({})); - - return collection.insert({ key: "KEY" }) - .then(function () { - Assert(insertOneStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("update", function () { - it("should update a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - updateStub.returns(BluebirdPromise.resolve({})); - - return collection.update({ key: "KEY" }, { key: "KEY", value: 1 }) - .then(function () { - Assert(updateStub.calledWith({ key: "KEY" }, { key: "KEY", value: 1 })); - }); - }); - }); - - describe("remove", function () { - it("should remove a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - removeStub.returns(BluebirdPromise.resolve({})); - - return collection.remove({ key: "KEY" }) - .then(function () { - Assert(removeStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("count", function () { - it("should count documents in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - countStub.returns(BluebirdPromise.resolve({})); - - return collection.count({ key: "KEY" }) - .then(function () { - Assert(countStub.calledWith({ key: "KEY" })); - }); - }); - }); -}); diff --git a/themes/squares/server/src/lib/storage/mongo/MongoCollection.ts b/themes/squares/server/src/lib/storage/mongo/MongoCollection.ts deleted file mode 100644 index 9771389f1..000000000 --- a/themes/squares/server/src/lib/storage/mongo/MongoCollection.ts +++ /dev/null @@ -1,50 +0,0 @@ -import Bluebird = require("bluebird"); -import { ICollection } from "../ICollection"; -import MongoDB = require("mongodb"); -import { IMongoClient } from "../../connectors/mongo/IMongoClient"; - - -export class MongoCollection implements ICollection { - private mongoClient: IMongoClient; - private collectionName: string; - - constructor(collectionName: string, mongoClient: IMongoClient) { - this.collectionName = collectionName; - this.mongoClient = mongoClient; - } - - private collection(): Bluebird { - return this.mongoClient.collection(this.collectionName); - } - - find(query: any, sortKeys?: any, count?: number): Bluebird { - return this.collection() - .then((collection) => collection.find(query).sort(sortKeys).limit(count)) - .then((query) => query.toArray()); - } - - findOne(query: any): Bluebird { - return this.collection() - .then((collection) => collection.findOne(query)); - } - - update(query: any, updateQuery: any, options?: any): Bluebird { - return this.collection() - .then((collection) => collection.update(query, updateQuery, options)); - } - - remove(query: any): Bluebird { - return this.collection() - .then((collection) => collection.remove(query)); - } - - insert(document: any): Bluebird { - return this.collection() - .then((collection) => collection.insertOne(document)); - } - - count(query: any): Bluebird { - return this.collection() - .then((collection) => collection.count(query)); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts b/themes/squares/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts deleted file mode 100644 index bd959cacb..000000000 --- a/themes/squares/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import { MongoClientStub } from "../../connectors/mongo/MongoClientStub.spec"; -import { MongoCollectionFactory } from "./MongoCollectionFactory"; - -describe("storage/mongo/MongoCollectionFactory", function () { - let mongoClient: MongoClientStub; - - before(function() { - mongoClient = new MongoClientStub(); - }); - - describe("create", function () { - it("should create a collection", function () { - const COLLECTION_NAME = "COLLECTION_NAME"; - - const factory = new MongoCollectionFactory(mongoClient); - Assert(factory.build(COLLECTION_NAME)); - }); - }); -}); diff --git a/themes/squares/server/src/lib/storage/mongo/MongoCollectionFactory.ts b/themes/squares/server/src/lib/storage/mongo/MongoCollectionFactory.ts deleted file mode 100644 index 14a8262c9..000000000 --- a/themes/squares/server/src/lib/storage/mongo/MongoCollectionFactory.ts +++ /dev/null @@ -1,19 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ICollection } from "../ICollection"; -import { ICollectionFactory } from "../ICollectionFactory"; -import { MongoCollection } from "./MongoCollection"; -import path = require("path"); -import MongoDB = require("mongodb"); -import { IMongoClient } from "../../connectors/mongo/IMongoClient"; - -export class MongoCollectionFactory implements ICollectionFactory { - private mongoClient: IMongoClient; - - constructor(mongoClient: IMongoClient) { - this.mongoClient = mongoClient; - } - - build(collectionName: string): ICollection { - return new MongoCollection(collectionName, this.mongoClient); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/nedb/NedbCollection.spec.ts b/themes/squares/server/src/lib/storage/nedb/NedbCollection.spec.ts deleted file mode 100644 index a69962b67..000000000 --- a/themes/squares/server/src/lib/storage/nedb/NedbCollection.spec.ts +++ /dev/null @@ -1,136 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); - -import { NedbCollection } from "./NedbCollection"; - -describe("storage/nedb/NedbCollection", function () { - describe("insert", function () { - it("should insert one entry", function () { - const nedbOptions = { - inMemoryOnly: true - }; - const collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - - return collection.count({}).then(function (count: number) { - Assert.equal(1, count); - }); - }); - - it("should insert three entries", function () { - const nedbOptions = { - inMemoryOnly: true - }; - const collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - collection.insert({ key: "hello" }); - collection.insert({ key: "hey" }); - - return collection.count({}).then(function (count: number) { - Assert.equal(3, count); - }); - }); - }); - - describe("find", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "hello" }); - collection.insert({ key: "hey" }); - collection.insert({ key: "coucou", value: 2 }); - }); - - it("should find one hello", function () { - return collection.find({ key: "hello" }, { key: 1 }) - .then(function (docs: { key: string }[]) { - Assert.equal(1, docs.length); - Assert(docs[0].key == "hello"); - }); - }); - - it("should find two coucou", function () { - return collection.find({ key: "coucou" }, { value: 1 }) - .then(function (docs: { value: number }[]) { - Assert.equal(2, docs.length); - }); - }); - }); - - describe("findOne", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - }); - - it("should find two coucou", function () { - const doc = { key: "coucou", value: 1 }; - return collection.count(doc) - .then(function (count: number) { - Assert.equal(4, count); - return collection.findOne(doc); - }); - }); - }); - - describe("update", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - }); - - it("should update the value", function () { - return collection.update({ key: "coucou" }, { key: "coucou", value: 2 }, { multi: true }) - .then(function () { - return collection.find({ key: "coucou" }); - }) - .then(function (docs: { key: string, value: number }[]) { - Assert.equal(1, docs.length); - Assert.equal(2, docs[0].value); - }); - }); - }); - - describe("update", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - collection.insert({ key: "hello" }); - }); - - it("should update the value", function () { - return collection.remove({ key: "coucou" }) - .then(function () { - return collection.count({}); - }) - .then(function (count: number) { - Assert.equal(1, count); - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/nedb/NedbCollection.ts b/themes/squares/server/src/lib/storage/nedb/NedbCollection.ts deleted file mode 100644 index 88a93ad05..000000000 --- a/themes/squares/server/src/lib/storage/nedb/NedbCollection.ts +++ /dev/null @@ -1,47 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ICollection } from "../ICollection"; -import Nedb = require("nedb"); - -declare module "nedb" { - export class NedbAsync extends Nedb { - constructor(pathOrOptions?: string | Nedb.DataStoreOptions); - updateAsync(query: any, updateQuery: any, options?: Nedb.UpdateOptions): BluebirdPromise; - findOneAsync(query: any): BluebirdPromise; - insertAsync(newDoc: T): BluebirdPromise; - removeAsync(query: any): BluebirdPromise; - countAsync(query: any): BluebirdPromise; - } -} - -export class NedbCollection implements ICollection { - private collection: Nedb.NedbAsync; - - constructor(options: Nedb.DataStoreOptions) { - this.collection = BluebirdPromise.promisifyAll(new Nedb(options)) as Nedb.NedbAsync; - } - - find(query: any, sortKeys?: any, count?: number): BluebirdPromise { - const q = this.collection.find(query).sort(sortKeys).limit(count); - return BluebirdPromise.promisify(q.exec, { context: q })(); - } - - findOne(query: any): BluebirdPromise { - return this.collection.findOneAsync(query); - } - - update(query: any, updateQuery: any, options?: any): BluebirdPromise { - return this.collection.updateAsync(query, updateQuery, options); - } - - remove(query: any): BluebirdPromise { - return this.collection.removeAsync(query); - } - - insert(document: any): BluebirdPromise { - return this.collection.insertAsync(document); - } - - count(query: any): BluebirdPromise { - return this.collection.countAsync(query); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts b/themes/squares/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts deleted file mode 100644 index da90c661f..000000000 --- a/themes/squares/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); - -import { NedbCollectionFactory } from "./NedbCollectionFactory"; - -describe("storage/nedb/NedbCollectionFactory", function() { - it("should create a nedb collection", function() { - const nedbOptions = { - inMemoryOnly: true - }; - const factory = new NedbCollectionFactory(nedbOptions); - - const collection = factory.build("mycollection"); - Assert(collection); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/storage/nedb/NedbCollectionFactory.ts b/themes/squares/server/src/lib/storage/nedb/NedbCollectionFactory.ts deleted file mode 100644 index 49c4dc853..000000000 --- a/themes/squares/server/src/lib/storage/nedb/NedbCollectionFactory.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ICollection } from "../ICollection"; -import { ICollectionFactory } from "../ICollectionFactory"; -import { NedbCollection } from "./NedbCollection"; -import path = require("path"); -import Nedb = require("nedb"); - -export interface NedbOptions { - inMemoryOnly?: boolean; - directory?: string; -} - -export class NedbCollectionFactory implements ICollectionFactory { - private options: Nedb.DataStoreOptions; - - constructor(options: Nedb.DataStoreOptions) { - this.options = options; - } - - build(collectionName: string): ICollection { - const datastoreOptions: Nedb.DataStoreOptions = { - inMemoryOnly: this.options.inMemoryOnly || false, - autoload: true, - filename: (this.options.filename) ? path.resolve(this.options.filename, collectionName) : undefined - }; - - return new NedbCollection(datastoreOptions); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/stubs/express.spec.ts b/themes/squares/server/src/lib/stubs/express.spec.ts deleted file mode 100644 index 48f15d7e1..000000000 --- a/themes/squares/server/src/lib/stubs/express.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ - -import sinon = require("sinon"); -import express = require("express"); - -export interface RequestMock { - app?: any; - body?: any; - session?: any; - headers?: any; - get?: any; - query?: any; - originalUrl: string; -} - -export interface ResponseMock { - send: sinon.SinonStub | sinon.SinonSpy; - sendStatus: sinon.SinonStub; - sendFile: sinon.SinonStub; - sendfile: sinon.SinonStub; - status: sinon.SinonStub | sinon.SinonSpy; - json: sinon.SinonStub | sinon.SinonSpy; - links: sinon.SinonStub; - jsonp: sinon.SinonStub; - download: sinon.SinonStub; - contentType: sinon.SinonStub; - type: sinon.SinonStub; - format: sinon.SinonStub; - attachment: sinon.SinonStub; - set: sinon.SinonStub; - header: sinon.SinonStub; - headersSent: boolean; - get: sinon.SinonStub; - clearCookie: sinon.SinonStub; - cookie: sinon.SinonStub; - location: sinon.SinonStub; - redirect: sinon.SinonStub | sinon.SinonSpy; - render: sinon.SinonStub | sinon.SinonSpy; - locals: sinon.SinonStub; - charset: string; - vary: sinon.SinonStub; - app: any; - write: sinon.SinonStub; - writeContinue: sinon.SinonStub; - writeHead: sinon.SinonStub; - statusCode: number; - statusMessage: string; - setHeader: sinon.SinonStub; - setTimeout: sinon.SinonStub; - sendDate: boolean; - getHeader: sinon.SinonStub; -} - -export function RequestMock(): RequestMock { - return { - originalUrl: "/non-api/xxx", - app: { - get: sinon.stub() - }, - headers: { - "x-forwarded-for": "127.0.0.1" - }, - session: {} - }; -} -export function ResponseMock(): ResponseMock { - return { - send: sinon.stub(), - status: sinon.stub(), - json: sinon.stub(), - sendStatus: sinon.stub(), - links: sinon.stub(), - jsonp: sinon.stub(), - sendFile: sinon.stub(), - sendfile: sinon.stub(), - download: sinon.stub(), - contentType: sinon.stub(), - type: sinon.stub(), - format: sinon.stub(), - attachment: sinon.stub(), - set: sinon.stub(), - header: sinon.stub(), - headersSent: true, - get: sinon.stub(), - clearCookie: sinon.stub(), - cookie: sinon.stub(), - location: sinon.stub(), - redirect: sinon.stub(), - render: sinon.stub(), - locals: sinon.stub(), - charset: "utf-8", - vary: sinon.stub(), - app: sinon.stub(), - write: sinon.stub(), - writeContinue: sinon.stub(), - writeHead: sinon.stub(), - statusCode: 200, - statusMessage: "message", - setHeader: sinon.stub(), - setTimeout: sinon.stub(), - sendDate: true, - getHeader: sinon.stub() - }; -} diff --git a/themes/squares/server/src/lib/stubs/ldapjs.spec.ts b/themes/squares/server/src/lib/stubs/ldapjs.spec.ts deleted file mode 100644 index 045c0e11b..000000000 --- a/themes/squares/server/src/lib/stubs/ldapjs.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ - -import Sinon = require("sinon"); - -export class LdapjsMock { - createClientStub: sinon.SinonStub; - - constructor() { - this.createClientStub = Sinon.stub(); - } - - createClient(params: any) { - return this.createClientStub(params); - } -} - -export class LdapjsClientMock { - bindStub: sinon.SinonStub; - unbindStub: sinon.SinonStub; - searchStub: sinon.SinonStub; - modifyStub: sinon.SinonStub; - onStub: sinon.SinonStub; - - constructor() { - this.bindStub = Sinon.stub(); - this.unbindStub = Sinon.stub(); - this.searchStub = Sinon.stub(); - this.modifyStub = Sinon.stub(); - this.onStub = Sinon.stub(); - } - - bind() { - return this.bindStub(); - } - - unbind() { - return this.unbindStub(); - } - - search() { - return this.searchStub(); - } - - modify() { - return this.modifyStub(); - } - - on() { - return this.onStub(); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/stubs/speakeasy.spec.ts b/themes/squares/server/src/lib/stubs/speakeasy.spec.ts deleted file mode 100644 index 023614dcd..000000000 --- a/themes/squares/server/src/lib/stubs/speakeasy.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import sinon = require("sinon"); - -export = { - totp: sinon.stub(), - generateSecret: sinon.stub() -}; diff --git a/themes/squares/server/src/lib/stubs/u2f.spec.ts b/themes/squares/server/src/lib/stubs/u2f.spec.ts deleted file mode 100644 index 234b28c11..000000000 --- a/themes/squares/server/src/lib/stubs/u2f.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ - -import sinon = require("sinon"); - -export interface U2FMock { - request: sinon.SinonStub; - checkSignature: sinon.SinonStub; - checkRegistration: sinon.SinonStub; -} - -export function U2FMock(): U2FMock { - return { - request: sinon.stub(), - checkSignature: sinon.stub(), - checkRegistration: sinon.stub() - }; -} diff --git a/themes/squares/server/src/lib/utils/HashGenerator.spec.ts b/themes/squares/server/src/lib/utils/HashGenerator.spec.ts deleted file mode 100644 index f19619a65..000000000 --- a/themes/squares/server/src/lib/utils/HashGenerator.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Assert = require("assert"); -import { HashGenerator } from "./HashGenerator"; - -describe("utils/HashGenerator", function () { - it("should compute correct ssha512 (password)", function () { - return HashGenerator.ssha512("password", 500000, "jgiCMRyGXzoqpxS3") - .then(function (hash: string) { - Assert.equal(hash, "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"); - }); - }); - - it("should compute correct ssha512 (test)", function () { - return HashGenerator.ssha512("test", 500000, "abcdefghijklmnop") - .then(function (hash: string) { - Assert.equal(hash, "{CRYPT}$6$rounds=500000$abcdefghijklmnop$sTlNGf0VO/HTQIOXemmaBbV28HUch/qhWOA1/4dsDj6CDQYhUgXbYSPL6gccAsWMr2zD5fFWwhKmPdG.yxphs."); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/utils/HashGenerator.ts b/themes/squares/server/src/lib/utils/HashGenerator.ts deleted file mode 100644 index e67de32b7..000000000 --- a/themes/squares/server/src/lib/utils/HashGenerator.ts +++ /dev/null @@ -1,23 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import RandomString = require("randomstring"); -import Util = require("util"); -const crypt = require("crypt3"); - -export class HashGenerator { - static ssha512( - password: string, - rounds: number = 500000, - salt?: string): BluebirdPromise { - const saltSize = 16; - // $6 means SHA512 - const _salt = Util.format("$6$rounds=%d$%s", rounds, - (salt) ? salt : RandomString.generate(16)); - - const cryptAsync = BluebirdPromise.promisify(crypt); - - return cryptAsync(password, _salt) - .then(function (hash: string) { - return BluebirdPromise.resolve(Util.format("{CRYPT}%s", hash)); - }); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/utils/ObjectCloner.ts b/themes/squares/server/src/lib/utils/ObjectCloner.ts deleted file mode 100644 index 3e125d749..000000000 --- a/themes/squares/server/src/lib/utils/ObjectCloner.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export class ObjectCloner { - static clone(obj: any): any { - return JSON.parse(JSON.stringify(obj)); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/utils/SafeRedirection.spec.ts b/themes/squares/server/src/lib/utils/SafeRedirection.spec.ts deleted file mode 100644 index 4126949fd..000000000 --- a/themes/squares/server/src/lib/utils/SafeRedirection.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import { SafeRedirector } from "./SafeRedirection"; - -describe("web_server/middlewares/SafeRedirection", () => { - describe("Url is in protected domain", () => { - before(() => { - this.redirector = new SafeRedirector("example.com"); - this.res = {redirect: Sinon.stub()}; - }); - - it("should redirect to provided url", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.example.com:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://mysubdomain.example.com:8080/abc")); - }); - - it("should redirect to default url when wrong domain", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.domain.rtf:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://authelia.example.com")); - }); - - it("should redirect to default url when not terminating by domain", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.example.com.rtf:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://authelia.example.com")); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/utils/SafeRedirection.ts b/themes/squares/server/src/lib/utils/SafeRedirection.ts deleted file mode 100644 index 9e6a32e0c..000000000 --- a/themes/squares/server/src/lib/utils/SafeRedirection.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Express = require("express"); -import { DomainExtractor } from "../../../../shared/DomainExtractor"; -import { BelongToDomain } from "../../../../shared/BelongToDomain"; - - -export class SafeRedirector { - private domain: string; - - constructor(domain: string) { - this.domain = domain; - } - - redirectOrElse( - res: Express.Response, - url: string, - defaultUrl: string): void { - if (BelongToDomain(url, this.domain)) { - res.redirect(url); - } - res.redirect(defaultUrl); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/utils/URLDecomposer.spec.ts b/themes/squares/server/src/lib/utils/URLDecomposer.spec.ts deleted file mode 100644 index cbb038738..000000000 --- a/themes/squares/server/src/lib/utils/URLDecomposer.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { URLDecomposer } from "./URLDecomposer"; -import Assert = require("assert"); - -describe("utils/URLDecomposer", function () { - describe("test fromUrl", function () { - it("should return domain from https url", function () { - const d = URLDecomposer.fromUrl("https://www.example.com/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return domain from http url", function () { - const d = URLDecomposer.fromUrl("http://www.example.com/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return domain when url contains port", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return default path when no path provided", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/"); - }); - - it("should return default path when provided", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080/"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/"); - }); - - it("should return undefined when does not match", function () { - const d = URLDecomposer.fromUrl("https:///abc/test"); - Assert.equal(d, undefined); - }); - - it("should return undefined when does not match", function () { - const d = URLDecomposer.fromUrl("https:///abc/test"); - Assert.equal(d, undefined); - }); - }); -}); \ No newline at end of file diff --git a/themes/squares/server/src/lib/utils/URLDecomposer.ts b/themes/squares/server/src/lib/utils/URLDecomposer.ts deleted file mode 100644 index 9bdf2e9d2..000000000 --- a/themes/squares/server/src/lib/utils/URLDecomposer.ts +++ /dev/null @@ -1,15 +0,0 @@ -export class URLDecomposer { - static fromUrl(url: string): {domain: string, path: string} { - if (!url) return; - const match = url.match(/https?:\/\/([a-z0-9_.-]+)(:[0-9]+)?(.*)/); - - if (!match) return; - - if (match[1] && !match[3]) { - return {domain: match[1], path: "/"}; - } else if (match[1] && match[3]) { - return {domain: match[1], path: match[3]}; - } - return; - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/web_server/Configurator.ts b/themes/squares/server/src/lib/web_server/Configurator.ts deleted file mode 100644 index 6e404874a..000000000 --- a/themes/squares/server/src/lib/web_server/Configurator.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Configuration } from "../configuration/schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; -import { SessionConfigurationBuilder } from - "../configuration/SessionConfigurationBuilder"; -import Path = require("path"); -import Express = require("express"); -import * as BodyParser from "body-parser"; -import { RestApi } from "./RestApi"; -import { WithHeadersLogged } from "./middlewares/WithHeadersLogged"; -import { ServerVariables } from "../ServerVariables"; -import Helmet = require("helmet"); - -const addRequestId = require("express-request-id")(); - -// Constants -const TRUST_PROXY = "trust proxy"; -const X_POWERED_BY = "x-powered-by"; -const VIEWS = "views"; -const VIEW_ENGINE = "view engine"; -const PUG = "pug"; - -export class Configurator { - static configure(config: Configuration, - app: Express.Application, - vars: ServerVariables, - deps: GlobalDependencies): void { - const viewsDirectory = Path.resolve(__dirname, "../../views"); - const publicHtmlDirectory = Path.resolve(__dirname, "../../public_html"); - - const expressSessionOptions = SessionConfigurationBuilder.build(config, deps); - - app.use(Express.static(publicHtmlDirectory)); - app.use(BodyParser.urlencoded({ extended: false })); - app.use(BodyParser.json()); - app.use(deps.session(expressSessionOptions)); - app.use(addRequestId); - app.use(WithHeadersLogged.middleware(vars.logger)); - app.disable(X_POWERED_BY); - app.enable(TRUST_PROXY); - app.use(Helmet()); - - app.set(VIEWS, viewsDirectory); - app.set(VIEW_ENGINE, PUG); - - RestApi.setup(app, vars); - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/web_server/RestApi.ts b/themes/squares/server/src/lib/web_server/RestApi.ts deleted file mode 100644 index 9144a15b9..000000000 --- a/themes/squares/server/src/lib/web_server/RestApi.ts +++ /dev/null @@ -1,125 +0,0 @@ -import Express = require("express"); - -import FirstFactorGet = require("../routes/firstfactor/get"); -import SecondFactorGet = require("../routes/secondfactor/get"); - -import FirstFactorPost = require("../routes/firstfactor/post"); -import LogoutGet = require("../routes/logout/get"); -import VerifyGet = require("../routes/verify/get"); -import TOTPSignGet = require("../routes/secondfactor/totp/sign/post"); - -import IdentityCheckMiddleware = require("../IdentityCheckMiddleware"); - -import TOTPRegistrationIdentityHandler from "../routes/secondfactor/totp/identity/RegistrationHandler"; -import U2FRegistrationIdentityHandler from "../routes/secondfactor/u2f/identity/RegistrationHandler"; -import ResetPasswordIdentityHandler from "../routes/password-reset/identity/PasswordResetHandler"; - -import U2FSignPost = require("../routes/secondfactor/u2f/sign/post"); -import U2FSignRequestGet = require("../routes/secondfactor/u2f/sign_request/get"); - -import U2FRegisterPost = require("../routes/secondfactor/u2f/register/post"); -import U2FRegisterRequestGet = require("../routes/secondfactor/u2f/register_request/get"); - -import ResetPasswordFormPost = require("../routes/password-reset/form/post"); -import ResetPasswordRequestPost = require("../routes/password-reset/request/get"); - -import Error401Get = require("../routes/error/401/get"); -import Error403Get = require("../routes/error/403/get"); -import Error404Get = require("../routes/error/404/get"); - -import LoggedIn = require("../routes/loggedin/get"); - -import { ServerVariables } from "../ServerVariables"; -import Endpoints = require("../../../../shared/api"); -import { RequireValidatedFirstFactor } from "./middlewares/RequireValidatedFirstFactor"; - -function setupTotp(app: Express.Application, vars: ServerVariables) { - app.post(Endpoints.SECOND_FACTOR_TOTP_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - TOTPSignGet.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - app.get(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - IdentityCheckMiddleware.register(app, - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET, - new TOTPRegistrationIdentityHandler(vars.logger, - vars.userDataStore, vars.totpHandler, vars.config.totp), - vars); -} - -function setupU2f(app: Express.Application, vars: ServerVariables) { - app.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FSignRequestGet.default(vars)); - - app.post(Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FSignPost.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FRegisterRequestGet.default(vars)); - - app.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FRegisterPost.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - app.get(Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - IdentityCheckMiddleware.register(app, - Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, - Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, - new U2FRegistrationIdentityHandler(vars.logger), vars); -} - -function setupResetPassword(app: Express.Application, vars: ServerVariables) { - IdentityCheckMiddleware.register(app, - Endpoints.RESET_PASSWORD_IDENTITY_START_GET, - Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET, - new ResetPasswordIdentityHandler(vars.logger, vars.usersDatabase), - vars); - - app.get(Endpoints.RESET_PASSWORD_REQUEST_GET, - ResetPasswordRequestPost.default); - app.post(Endpoints.RESET_PASSWORD_FORM_POST, - ResetPasswordFormPost.default(vars)); -} - -function setupErrors(app: Express.Application, vars: ServerVariables) { - app.get(Endpoints.ERROR_401_GET, Error401Get.default(vars)); - app.get(Endpoints.ERROR_403_GET, Error403Get.default(vars)); - app.get(Endpoints.ERROR_404_GET, Error404Get.default); -} - -export class RestApi { - static setup(app: Express.Application, vars: ServerVariables): void { - app.get(Endpoints.FIRST_FACTOR_GET, FirstFactorGet.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - SecondFactorGet.default(vars)); - - app.get(Endpoints.LOGOUT_GET, LogoutGet.default(vars)); - - app.get(Endpoints.VERIFY_GET, VerifyGet.default(vars)); - app.post(Endpoints.FIRST_FACTOR_POST, FirstFactorPost.default(vars)); - - setupTotp(app, vars); - setupU2f(app, vars); - setupResetPassword(app, vars); - setupErrors(app, vars); - - app.get(Endpoints.LOGGED_IN, - RequireValidatedFirstFactor.middleware(vars.logger), - LoggedIn.default(vars)); - } -} diff --git a/themes/squares/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts b/themes/squares/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts deleted file mode 100644 index ecfd75765..000000000 --- a/themes/squares/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts +++ /dev/null @@ -1,27 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import ErrorReplies = require("../../ErrorReplies"); -import { IRequestLogger } from "../../logging/IRequestLogger"; -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Exceptions = require("../../Exceptions"); -import { Level } from "../../authentication/Level"; - -export class RequireValidatedFirstFactor { - static middleware(logger: IRequestLogger) { - return function (req: Express.Request, res: Express.Response, - next: Express.NextFunction): BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, logger); - if (!authSession.userid || authSession.authentication_level < Level.ONE_FACTOR) - return reject( - new Exceptions.FirstFactorValidationError( - "First factor has not been validated yet.")); - - next(); - resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, logger)); - }; - } -} \ No newline at end of file diff --git a/themes/squares/server/src/lib/web_server/middlewares/WithHeadersLogged.ts b/themes/squares/server/src/lib/web_server/middlewares/WithHeadersLogged.ts deleted file mode 100644 index 139db1141..000000000 --- a/themes/squares/server/src/lib/web_server/middlewares/WithHeadersLogged.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Express = require("express"); -import { IRequestLogger } from "../../logging/IRequestLogger"; - -export class WithHeadersLogged { - static middleware(logger: IRequestLogger) { - return function (req: Express.Request, res: Express.Response, - next: Express.NextFunction): void { - logger.debug(req, "Headers = %s", JSON.stringify(req.headers)); - next(); - }; - } -} \ No newline at end of file diff --git a/themes/squares/server/src/resources/email-template.ejs b/themes/squares/server/src/resources/email-template.ejs old mode 100644 new mode 100755 diff --git a/themes/squares/server/src/views/already-logged-in.pug b/themes/squares/server/src/views/already-logged-in.pug old mode 100644 new mode 100755 diff --git a/themes/squares/server/src/views/errors/.directory b/themes/squares/server/src/views/errors/.directory old mode 100644 new mode 100755 diff --git a/themes/squares/server/src/views/errors/401.pug b/themes/squares/server/src/views/errors/401.pug old mode 100644 new mode 100755 diff --git a/themes/squares/server/src/views/errors/403.pug b/themes/squares/server/src/views/errors/403.pug old mode 100644 new mode 100755 diff --git a/themes/squares/server/src/views/errors/404.pug b/themes/squares/server/src/views/errors/404.pug old mode 100644 new mode 100755 diff --git a/themes/squares/server/src/views/firstfactor.pug b/themes/squares/server/src/views/firstfactor.pug old mode 100644 new mode 100755 diff --git a/themes/squares/server/src/views/layout/layout.pug b/themes/squares/server/src/views/layout/layout.pug old mode 100644 new mode 100755 diff --git a/themes/squares/server/src/views/need-identity-validation.pug b/themes/squares/server/src/views/need-identity-validation.pug old mode 100644 new mode 100755 diff --git a/themes/squares/server/src/views/password-reset-form.pug b/themes/squares/server/src/views/password-reset-form.pug old mode 100644 new mode 100755 diff --git a/themes/squares/server/src/views/password-reset-request.pug b/themes/squares/server/src/views/password-reset-request.pug old mode 100644 new mode 100755 diff --git a/themes/squares/server/src/views/secondfactor.pug b/themes/squares/server/src/views/secondfactor.pug old mode 100644 new mode 100755 diff --git a/themes/squares/server/src/views/totp-register.pug b/themes/squares/server/src/views/totp-register.pug old mode 100644 new mode 100755 diff --git a/themes/squares/server/src/views/u2f-register.pug b/themes/squares/server/src/views/u2f-register.pug old mode 100644 new mode 100755 diff --git a/themes/squares/server/test/requests.ts b/themes/squares/server/test/requests.ts deleted file mode 100644 index 93fa0de47..000000000 --- a/themes/squares/server/test/requests.ts +++ /dev/null @@ -1,94 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import request = require("request"); -import assert = require("assert"); -import express = require("express"); -import nodemailer = require("nodemailer"); -import Endpoints = require("../../shared/api"); - -declare module "request" { - export interface RequestAPI { - getAsync(uri: string, options?: RequiredUriUrl): BluebirdPromise; - getAsync(uri: string): BluebirdPromise; - getAsync(options: RequiredUriUrl & CoreOptions): BluebirdPromise; - - postAsync(uri: string, options?: CoreOptions): BluebirdPromise; - postAsync(uri: string): BluebirdPromise; - postAsync(options: RequiredUriUrl & CoreOptions): BluebirdPromise; - } -} - -const requestAsync: typeof request = BluebirdPromise.promisifyAll(request) as typeof request; - -export = function (port: number) { - const PORT = port; - const BASE_URL = "http://localhost:" + PORT; - - function execute_totp(jar: request.CookieJar, token: string) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_TOTP_POST, - jar: jar, - form: { - token: token - } - }); - } - - function execute_u2f_authentication(jar: request.CookieJar) { - return requestAsync.getAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, - jar: jar - }) - .then(function (res: request.RequestResponse) { - assert.equal(res.statusCode, 200); - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - jar: jar, - form: { - } - }); - }); - } - - function execute_verification(jar: request.CookieJar) { - return requestAsync.getAsync({ url: BASE_URL + Endpoints.VERIFY_GET, jar: jar }); - } - - function execute_login(jar: request.CookieJar) { - return requestAsync.getAsync({ url: BASE_URL + Endpoints.FIRST_FACTOR_GET, jar: jar }); - } - - function execute_first_factor(jar: request.CookieJar) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.FIRST_FACTOR_POST, - jar: jar, - form: { - username: "test_ok", - password: "password" - } - }); - } - - function execute_failing_first_factor(jar: request.CookieJar) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.FIRST_FACTOR_POST, - jar: jar, - form: { - username: "test_nok", - password: "password" - } - }); - } - - return { - login: execute_login, - verify: execute_verification, - u2f_authentication: execute_u2f_authentication, - first_factor: execute_first_factor, - failing_first_factor: execute_failing_first_factor, - totp: execute_totp, - }; -}; - diff --git a/themes/squares/server/tsconfig.json b/themes/squares/server/tsconfig.json deleted file mode 100644 index ebe98c5ed..000000000 --- a/themes/squares/server/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "moduleResolution": "node", - "noImplicitAny": true, - "sourceMap": true, - "removeComments": true, - "outDir": "../dist", - "baseUrl": ".", - "paths": { - "*": [ - "./types/*", - "../shared/types/*" - ] - } - }, - "exclude": [ - "src/**/*.spec.ts" - ] -} diff --git a/themes/squares/server/tslint.json b/themes/squares/server/tslint.json deleted file mode 100644 index c2c1b7501..000000000 --- a/themes/squares/server/tslint.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "no-var-keyword": true, - "quotemark": [ - true, - "double", - "avoid-escape" - ], - "semicolon": [ - true, - "always", - "ignore-bound-class-methods" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-module", - "check-separator", - "check-type" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "onespace", - "index-signature": "onespace", - "parameter": "onespace", - "property-declaration": "onespace", - "variable-declaration": "onespace" - } - ], - "no-internal-module": true, - "no-trailing-whitespace": true, - "no-null-keyword": true, - "prefer-const": true, - "jsdoc-format": true - } -} diff --git a/themes/squares/server/types/.directory b/themes/squares/server/types/.directory deleted file mode 100644 index 1e65000ea..000000000 --- a/themes/squares/server/types/.directory +++ /dev/null @@ -1,4 +0,0 @@ -[Dolphin] -Timestamp=2018,12,17,20,58,27 -Version=3 -ViewMode=1 diff --git a/themes/squares/server/types/AuthenticationSession.ts b/themes/squares/server/types/AuthenticationSession.ts deleted file mode 100644 index bbed0e715..000000000 --- a/themes/squares/server/types/AuthenticationSession.ts +++ /dev/null @@ -1,18 +0,0 @@ -import U2f = require("u2f"); -import { Level } from "../src/lib/authentication/Level"; - -export interface AuthenticationSession { - userid: string; - authentication_level: Level; - keep_me_logged_in: boolean; - last_activity_datetime: number; - identity_check?: { - challenge: string; - userid: string; - }; - register_request?: U2f.Request; - sign_request?: U2f.Request; - email: string; - groups: string[]; - redirect?: string; -} \ No newline at end of file diff --git a/themes/squares/server/types/Dependencies.ts b/themes/squares/server/types/Dependencies.ts deleted file mode 100644 index f20404dbc..000000000 --- a/themes/squares/server/types/Dependencies.ts +++ /dev/null @@ -1,29 +0,0 @@ -import winston = require("winston"); -import speakeasy = require("speakeasy"); -import nodemailer = require("nodemailer"); -import session = require("express-session"); -import nedb = require("nedb"); -import ldapjs = require("ldapjs"); -import u2f = require("u2f"); -import RedisSession = require("connect-redis"); -import Redis = require("redis"); - -export type Speakeasy = typeof speakeasy; -export type Winston = typeof winston; -export type Session = typeof session; -export type Nedb = typeof nedb; -export type Ldapjs = typeof ldapjs; -export type U2f = typeof u2f; -export type ConnectRedis = typeof RedisSession; -export type Redis = typeof Redis; - -export interface GlobalDependencies { - u2f: U2f; - ldapjs: Ldapjs; - session: Session; - Redis: Redis; - ConnectRedis: ConnectRedis; - winston: Winston; - speakeasy: Speakeasy; - nedb: Nedb; -} \ No newline at end of file diff --git a/themes/squares/server/types/Identity.ts b/themes/squares/server/types/Identity.ts deleted file mode 100644 index e985984e2..000000000 --- a/themes/squares/server/types/Identity.ts +++ /dev/null @@ -1,6 +0,0 @@ - - -export interface Identity { - userid: string; - email: string; -} \ No newline at end of file diff --git a/themes/squares/server/types/TOTPSecret.ts b/themes/squares/server/types/TOTPSecret.ts deleted file mode 100644 index d6775f2f0..000000000 --- a/themes/squares/server/types/TOTPSecret.ts +++ /dev/null @@ -1,11 +0,0 @@ - -export interface TOTPSecret { - ascii: string; - hex: string; - base32: string; - qr_code_ascii: string; - qr_code_hex: string; - qr_code_base32: string; - google_auth_qr: string; - otpauth_url: string; - } \ No newline at end of file diff --git a/themes/squares/server/types/U2FRegistration.ts b/themes/squares/server/types/U2FRegistration.ts deleted file mode 100644 index b6080af07..000000000 --- a/themes/squares/server/types/U2FRegistration.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface U2FRegistration { - keyHandle: string; - publicKey: string; -} \ No newline at end of file diff --git a/themes/squares/server/types/dovehash.d.ts b/themes/squares/server/types/dovehash.d.ts deleted file mode 100644 index c354609c0..000000000 --- a/themes/squares/server/types/dovehash.d.ts +++ /dev/null @@ -1,4 +0,0 @@ - -declare module "dovehash" { - function encode(algo: string, text: string): string; -} \ No newline at end of file diff --git a/themes/squares/server/types/speakeasy.d.ts b/themes/squares/server/types/speakeasy.d.ts deleted file mode 100644 index 6ea06948b..000000000 --- a/themes/squares/server/types/speakeasy.d.ts +++ /dev/null @@ -1,96 +0,0 @@ -declare module "speakeasy" { - export = speakeasy - - interface SharedOptions { - encoding?: string - algorithm?: string - } - - interface DigestOptions extends SharedOptions { - secret: string - counter: number - } - - interface HOTPOptions extends SharedOptions { - secret: string - counter: number - digest?: Buffer - digits?: number - } - - interface HOTPVerifyOptions extends SharedOptions { - secret: string - token: string - counter: number - digits?: number - window?: number - } - - interface TOTPOptions extends SharedOptions { - secret: string - time?: number - step?: number - epoch?: number - counter?: number - digits?: number - } - - interface TOTPVerifyOptions extends SharedOptions { - secret: string - token: string - time?: number - step?: number - epoch?: number - counter?: number - digits?: number - window?: number - } - - interface GenerateSecretOptions { - length?: number - symbols?: boolean - otpauth_url?: boolean - name?: string - issuer?: string - } - - interface GeneratedSecret { - ascii: string - hex: string - base32: string - qr_code_ascii: string - qr_code_hex: string - qr_code_base32: string - google_auth_qr: string - otpauth_url: string - } - - interface OTPAuthURLOptions extends SharedOptions { - secret: string - label: string - type?: string - counter?: number - issuer?: string - digits?: number - period?: number - } - - interface Speakeasy { - digest: (options: DigestOptions) => Buffer - hotp: { - (options: HOTPOptions): string, - verifyDelta: (options: HOTPVerifyOptions) => boolean, - verify: (options: HOTPVerifyOptions) => boolean, - } - totp: { - (options: TOTPOptions): string - verifyDelta: (options: TOTPVerifyOptions) => boolean, - verify: (options: TOTPVerifyOptions) => boolean, - } - generateSecret: (options?: GenerateSecretOptions) => GeneratedSecret - generateSecretASCII: (length?: number, symbols?: boolean) => string - otpauthURL: (options: OTPAuthURLOptions) => string - } - - const speakeasy: Speakeasy -} \ No newline at end of file diff --git a/themes/triangles/client/src/.directory b/themes/triangles/client/src/.directory old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/css/.directory b/themes/triangles/client/src/css/.directory old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/css/00-bootstrap.min.css b/themes/triangles/client/src/css/00-bootstrap.min.css old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/css/01-main.css b/themes/triangles/client/src/css/01-main.css old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/css/02-login.css b/themes/triangles/client/src/css/02-login.css old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/css/03-errors.css b/themes/triangles/client/src/css/03-errors.css old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/css/03-password-reset-form.css b/themes/triangles/client/src/css/03-password-reset-form.css old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/css/03-password-reset-request.css b/themes/triangles/client/src/css/03-password-reset-request.css old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/css/03-totp-register.css b/themes/triangles/client/src/css/03-totp-register.css old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/css/03-u2f-register.css b/themes/triangles/client/src/css/03-u2f-register.css old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/LargeTriangles.svg b/themes/triangles/client/src/img/LargeTriangles.svg old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/background.jpg b/themes/triangles/client/src/img/background.jpg old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/icon.png b/themes/triangles/client/src/img/icon.png old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/mail.png b/themes/triangles/client/src/img/mail.png old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/matrix_circle_128x128.png b/themes/triangles/client/src/img/matrix_circle_128x128.png old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/notifications/.directory b/themes/triangles/client/src/img/notifications/.directory old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/notifications/error.png b/themes/triangles/client/src/img/notifications/error.png old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/notifications/info.png b/themes/triangles/client/src/img/notifications/info.png old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/notifications/success.png b/themes/triangles/client/src/img/notifications/success.png old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/notifications/warning.png b/themes/triangles/client/src/img/notifications/warning.png old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/padlock.png b/themes/triangles/client/src/img/padlock.png old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/password_white.png b/themes/triangles/client/src/img/password_white.png old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/pendrive.png b/themes/triangles/client/src/img/pendrive.png old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/sharingan.png b/themes/triangles/client/src/img/sharingan.png old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/stores/.directory b/themes/triangles/client/src/img/stores/.directory old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/stores/applestore-badge.svg b/themes/triangles/client/src/img/stores/applestore-badge.svg old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/stores/googleplay-badge.svg b/themes/triangles/client/src/img/stores/googleplay-badge.svg old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/success.png b/themes/triangles/client/src/img/success.png old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/user.png b/themes/triangles/client/src/img/user.png old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/img/warning.png b/themes/triangles/client/src/img/warning.png old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/index.ts b/themes/triangles/client/src/index.ts deleted file mode 100644 index 802004a8f..000000000 --- a/themes/triangles/client/src/index.ts +++ /dev/null @@ -1,34 +0,0 @@ - -import FirstFactorValidator = require("./lib/firstfactor/FirstFactorValidator"); - -import FirstFactor from "./lib/firstfactor/index"; -import SecondFactor from "./lib/secondfactor/index"; -import TOTPRegister from "./lib/totp-register/totp-register"; -import U2fRegister from "./lib/u2f-register/u2f-register"; -import ResetPasswordRequest from "./lib/reset-password/reset-password-request"; -import ResetPasswordForm from "./lib/reset-password/reset-password-form"; -import jslogger = require("js-logger"); -import jQuery = require("jquery"); -import Endpoints = require("../../shared/api"); - -jslogger.useDefaults(); -jslogger.setLevel(jslogger.INFO); - -(function () { - (window).jQuery = jQuery; - require("bootstrap"); - - jQuery('[data-toggle="tooltip"]').tooltip(); - if (window.location.pathname == Endpoints.FIRST_FACTOR_GET) - FirstFactor(window, jQuery, FirstFactorValidator, jslogger); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_GET) - SecondFactor(window, jQuery, (global as any).u2f); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET) - TOTPRegister(window, jQuery); - else if (window.location.pathname == Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET) - U2fRegister(window, jQuery, (global as any).u2f); - else if (window.location.pathname == Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET) - ResetPasswordForm(window, jQuery); - else if (window.location.pathname == Endpoints.RESET_PASSWORD_REQUEST_GET) - ResetPasswordRequest(window, jQuery); -})(); diff --git a/themes/triangles/client/src/lib/GetPromised.ts b/themes/triangles/client/src/lib/GetPromised.ts deleted file mode 100644 index 779139654..000000000 --- a/themes/triangles/client/src/lib/GetPromised.ts +++ /dev/null @@ -1,14 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -export default function ($: JQueryStatic, url: string, data: Object, fn: any, - dataType: string): BluebirdPromise { - return new BluebirdPromise((resolve, reject) => { - $.get(url, {}, undefined, dataType) - .done((data: any) => { - resolve(data); - }) - .fail((xhr: JQueryXHR, textStatus: string) => { - reject(textStatus); - }); - }); -} \ No newline at end of file diff --git a/themes/triangles/client/src/lib/INotifier.ts b/themes/triangles/client/src/lib/INotifier.ts deleted file mode 100644 index df947538f..000000000 --- a/themes/triangles/client/src/lib/INotifier.ts +++ /dev/null @@ -1,14 +0,0 @@ - -declare type Handler = () => void; - -export interface Handlers { - onFadedIn: Handler; - onFadedOut: Handler; -} - -export interface INotifier { - success(msg: string, handlers?: Handlers): void; - error(msg: string, handlers?: Handlers): void; - warning(msg: string, handlers?: Handlers): void; - info(msg: string, handlers?: Handlers): void; -} \ No newline at end of file diff --git a/themes/triangles/client/src/lib/Notifier.ts b/themes/triangles/client/src/lib/Notifier.ts deleted file mode 100644 index c0252b9b9..000000000 --- a/themes/triangles/client/src/lib/Notifier.ts +++ /dev/null @@ -1,83 +0,0 @@ - - -import util = require("util"); -import { INotifier, Handlers } from "./INotifier"; - -class NotificationEvent { - private element: JQuery; - private message: string; - private statusType: string; - private timeoutId: any; - - constructor(element: JQuery, msg: string, statusType: string) { - this.message = msg; - this.statusType = statusType; - this.element = element; - } - - private clearNotification() { - this.element.removeClass(this.statusType); - this.element.html(""); - } - - start(handlers?: Handlers) { - const that = this; - const FADE_TIME = 500; - const html = util.format('status %s\ - %s', this.statusType, this.statusType, this.message); - this.element.html(html); - this.element.addClass(this.statusType); - this.element.fadeIn(FADE_TIME, function () { - if (handlers) - handlers.onFadedIn(); - }); - - this.timeoutId = setTimeout(function () { - that.element.fadeOut(FADE_TIME, function () { - that.clearNotification(); - if (handlers) - handlers.onFadedOut(); - }); - }, 4000); - } - - interrupt() { - this.clearNotification(); - this.element.hide(); - clearTimeout(this.timeoutId); - } -} - -export class Notifier implements INotifier { - private element: JQuery; - private onGoingEvent: NotificationEvent; - - constructor(selector: string, $: JQueryStatic) { - this.element = $(selector); - this.onGoingEvent = undefined; - } - - private displayAndFadeout(msg: string, statusType: string, handlers?: Handlers): void { - if (this.onGoingEvent) - this.onGoingEvent.interrupt(); - - this.onGoingEvent = new NotificationEvent(this.element, msg, statusType); - this.onGoingEvent.start(handlers); - } - - success(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "success", handlers); - } - - error(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "error", handlers); - } - - warning(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "warning", handlers); - } - - info(msg: string, handlers?: Handlers) { - this.displayAndFadeout(msg, "info", handlers); - } -} \ No newline at end of file diff --git a/themes/triangles/client/src/lib/QueryParametersRetriever.ts b/themes/triangles/client/src/lib/QueryParametersRetriever.ts deleted file mode 100644 index a529adb6e..000000000 --- a/themes/triangles/client/src/lib/QueryParametersRetriever.ts +++ /dev/null @@ -1,12 +0,0 @@ - -export class QueryParametersRetriever { - static get(name: string, url?: string): string { - if (!url) url = window.location.href; - name = name.replace(/[\[\]]/g, "\\$&"); - const regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), - results = regex.exec(url); - if (!results) return undefined; - if (!results[2]) return ""; - return decodeURIComponent(results[2].replace(/\+/g, " ")); - } -} \ No newline at end of file diff --git a/themes/triangles/client/src/lib/SafeRedirect.ts b/themes/triangles/client/src/lib/SafeRedirect.ts deleted file mode 100644 index 7e7684b8a..000000000 --- a/themes/triangles/client/src/lib/SafeRedirect.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BelongToDomain } from "../../../shared/BelongToDomain"; - -export function SafeRedirect(url: string, cb: () => void): void { - const domain = window.location.hostname.split(".").slice(-2).join("."); - if (url.startsWith("/") || BelongToDomain(url, domain)) { - window.location.href = url; - return; - } - cb(); -} \ No newline at end of file diff --git a/themes/triangles/client/src/lib/firstfactor/FirstFactorValidator.ts b/themes/triangles/client/src/lib/firstfactor/FirstFactorValidator.ts deleted file mode 100644 index eaa496fdd..000000000 --- a/themes/triangles/client/src/lib/firstfactor/FirstFactorValidator.ts +++ /dev/null @@ -1,46 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import Constants = require("../../../../shared/constants"); -import Util = require("util"); -import UserMessages = require("../../../../shared/UserMessages"); - -export function validate(username: string, password: string, - keepMeLoggedIn: boolean, redirectUrl: string, $: JQueryStatic) - : BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - let url: string; - if (redirectUrl != undefined) { - const redirectParam = Util.format("%s=%s", Constants.REDIRECT_QUERY_PARAM, redirectUrl); - url = Util.format("%s?%s", Endpoints.FIRST_FACTOR_POST, redirectParam); - } - else { - url = Util.format("%s", Endpoints.FIRST_FACTOR_POST); - } - - const data: any = { - username: username, - password: password, - }; - - if (keepMeLoggedIn) { - data.keepMeLoggedIn = "true"; - } - - $.ajax({ - method: "POST", - url: url, - data: data - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(body.redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(UserMessages.AUTHENTICATION_FAILED)); - }); - }); -} diff --git a/themes/triangles/client/src/lib/firstfactor/UISelectors.ts b/themes/triangles/client/src/lib/firstfactor/UISelectors.ts deleted file mode 100644 index 0e971b3c3..000000000 --- a/themes/triangles/client/src/lib/firstfactor/UISelectors.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export const USERNAME_FIELD_ID = "#username"; -export const PASSWORD_FIELD_ID = "#password"; -export const SIGN_IN_BUTTON_ID = "#signin"; -export const KEEP_ME_LOGGED_IN_ID = "#keep_me_logged_in"; diff --git a/themes/triangles/client/src/lib/firstfactor/index.ts b/themes/triangles/client/src/lib/firstfactor/index.ts deleted file mode 100644 index 24affee2d..000000000 --- a/themes/triangles/client/src/lib/firstfactor/index.ts +++ /dev/null @@ -1,49 +0,0 @@ -import FirstFactorValidator = require("./FirstFactorValidator"); -import JSLogger = require("js-logger"); -import UISelectors = require("./UISelectors"); -import { Notifier } from "../Notifier"; -import { QueryParametersRetriever } from "../QueryParametersRetriever"; -import Constants = require("../../../../shared/constants"); -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic, - firstFactorValidator: typeof FirstFactorValidator, jslogger: typeof JSLogger) { - - const notifier = new Notifier(".notification", $); - - function onFormSubmitted() { - const username: string = $(UISelectors.USERNAME_FIELD_ID).val() as string; - const password: string = $(UISelectors.PASSWORD_FIELD_ID).val() as string; - const keepMeLoggedIn: boolean = $(UISelectors.KEEP_ME_LOGGED_IN_ID).is(":checked"); - - $("form").css("opacity", 0.5); - $("input,button").attr("disabled", "true"); - $(UISelectors.SIGN_IN_BUTTON_ID).text("Please wait..."); - - const redirectUrl = QueryParametersRetriever.get(Constants.REDIRECT_QUERY_PARAM); - firstFactorValidator.validate(username, password, keepMeLoggedIn, redirectUrl, $) - .then(onFirstFactorSuccess, onFirstFactorFailure); - return false; - } - - function onFirstFactorSuccess(redirectUrl: string) { - SafeRedirect(redirectUrl, () => { - notifier.error("Cannot redirect to an external domain."); - }); - } - - function onFirstFactorFailure(err: Error) { - $("input,button").removeAttr("disabled"); - $("form").css("opacity", 1); - notifier.error(UserMessages.AUTHENTICATION_FAILED); - $(UISelectors.PASSWORD_FIELD_ID).select(); - $(UISelectors.SIGN_IN_BUTTON_ID).text("Sign in"); - } - - $(window.document).ready(function () { - $("form").on("submit", onFormSubmitted); - }); -} - diff --git a/themes/triangles/client/src/lib/reset-password/constants.ts b/themes/triangles/client/src/lib/reset-password/constants.ts deleted file mode 100644 index d48d4e67d..000000000 --- a/themes/triangles/client/src/lib/reset-password/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const FORM_SELECTOR = ".form-signin"; \ No newline at end of file diff --git a/themes/triangles/client/src/lib/reset-password/reset-password-form.ts b/themes/triangles/client/src/lib/reset-password/reset-password-form.ts deleted file mode 100644 index b94279cde..000000000 --- a/themes/triangles/client/src/lib/reset-password/reset-password-form.ts +++ /dev/null @@ -1,57 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); - -import Constants = require("./constants"); -import { Notifier } from "../Notifier"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function modifyPassword(newPassword: string) { - return new BluebirdPromise(function (resolve, reject) { - $.post(Endpoints.RESET_PASSWORD_FORM_POST, { - password: newPassword, - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(body); - }) - .fail(function (xhr, status) { - reject(status); - }); - }); - } - - function onFormSubmitted() { - const password1 = $("#password1").val() as string; - const password2 = $("#password2").val() as string; - - if (!password1 || !password2) { - notifier.warning(UserMessages.MISSING_PASSWORD); - return false; - } - - if (password1 != password2) { - notifier.warning(UserMessages.DIFFERENT_PASSWORDS); - return false; - } - - modifyPassword(password1) - .then(function () { - window.location.href = Endpoints.FIRST_FACTOR_GET; - }) - .error(function () { - notifier.error(UserMessages.RESET_PASSWORD_FAILED); - }); - return false; - } - - $(document).ready(function () { - $(Constants.FORM_SELECTOR).on("submit", onFormSubmitted); - }); -} diff --git a/themes/triangles/client/src/lib/reset-password/reset-password-request.ts b/themes/triangles/client/src/lib/reset-password/reset-password-request.ts deleted file mode 100644 index 846226d75..000000000 --- a/themes/triangles/client/src/lib/reset-password/reset-password-request.ts +++ /dev/null @@ -1,56 +0,0 @@ - -import BluebirdPromise = require("bluebird"); - -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import Constants = require("./constants"); -import jslogger = require("js-logger"); -import { Notifier } from "../Notifier"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function requestPasswordReset(username: string) { - return new BluebirdPromise(function (resolve, reject) { - $.get(Endpoints.RESET_PASSWORD_IDENTITY_START_GET, { - userid: username, - }) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); - return; - } - resolve(); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); - } - - function onFormSubmitted() { - const username = $("#username").val() as string; - - if (!username) { - notifier.warning(UserMessages.MISSING_USERNAME); - return; - } - - requestPasswordReset(username) - .then(function () { - notifier.success(UserMessages.MAIL_SENT); - setTimeout(function () { - window.location.replace(Endpoints.FIRST_FACTOR_GET); - }, 1000); - }) - .error(function () { - notifier.error(UserMessages.MAIL_NOT_SENT); - }); - return false; - } - - $(document).ready(function () { - $(Constants.FORM_SELECTOR).on("submit", onFormSubmitted); - }); -} - diff --git a/themes/triangles/client/src/lib/secondfactor/TOTPValidator.ts b/themes/triangles/client/src/lib/secondfactor/TOTPValidator.ts deleted file mode 100644 index 5394139a6..000000000 --- a/themes/triangles/client/src/lib/secondfactor/TOTPValidator.ts +++ /dev/null @@ -1,28 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; - -export function validate(token: string, $: JQueryStatic): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.ajax({ - url: Endpoints.SECOND_FACTOR_TOTP_POST, - data: { - token: token, - }, - method: "POST", - dataType: "json" - } as JQueryAjaxSettings) - .done(function (body: RedirectionMessage | ErrorMessage) { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); -} \ No newline at end of file diff --git a/themes/triangles/client/src/lib/secondfactor/U2FValidator.ts b/themes/triangles/client/src/lib/secondfactor/U2FValidator.ts deleted file mode 100644 index 5812922f7..000000000 --- a/themes/triangles/client/src/lib/secondfactor/U2FValidator.ts +++ /dev/null @@ -1,42 +0,0 @@ -import U2f = require("u2f"); -import U2fApi from "u2f-api"; -import BluebirdPromise = require("bluebird"); -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { INotifier } from "../INotifier"; -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; -import GetPromised from "../GetPromised"; - -function finishU2fAuthentication(responseData: U2fApi.SignResponse, - $: JQueryStatic): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.ajax({ - url: Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - data: responseData, - method: "POST", - dataType: "json" - } as JQueryAjaxSettings) - .done(function (body: RedirectionMessage | ErrorMessage) { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail(function (xhr: JQueryXHR, textStatus: string) { - reject(new Error(textStatus)); - }); - }); -} - -export function validate($: JQueryStatic): BluebirdPromise { - return GetPromised($, Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, {}, - undefined, "json") - .then(function (signRequest: U2f.Request) { - return U2fApi.sign(signRequest, 60); - }) - .then(function (signResponse: U2fApi.SignResponse) { - return finishU2fAuthentication(signResponse, $); - }); -} diff --git a/themes/triangles/client/src/lib/secondfactor/constants.ts b/themes/triangles/client/src/lib/secondfactor/constants.ts deleted file mode 100644 index 50bba7571..000000000 --- a/themes/triangles/client/src/lib/secondfactor/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ - -export const TOTP_FORM_SELECTOR = ".form-signin.totp"; -export const TOTP_TOKEN_SELECTOR = ".form-signin #token"; diff --git a/themes/triangles/client/src/lib/secondfactor/index.ts b/themes/triangles/client/src/lib/secondfactor/index.ts deleted file mode 100644 index 279723dce..000000000 --- a/themes/triangles/client/src/lib/secondfactor/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import TOTPValidator = require("./TOTPValidator"); -import U2FValidator = require("./U2FValidator"); -import ClientConstants = require("./constants"); -import { Notifier } from "../Notifier"; -import { QueryParametersRetriever } from "../QueryParametersRetriever"; -import UserMessages = require("../../../../shared/UserMessages"); -import SharedConstants = require("../../../../shared/constants"); -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function onAuthenticationSuccess(serverRedirectUrl: string) { - const queryRedirectUrl = QueryParametersRetriever.get(SharedConstants.REDIRECT_QUERY_PARAM); - if (queryRedirectUrl) { - SafeRedirect(queryRedirectUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - } else if (serverRedirectUrl) { - SafeRedirect(serverRedirectUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - } else { - notifier.success(UserMessages.AUTHENTICATION_SUCCEEDED); - } - } - - function onSecondFactorTotpSuccess(redirectUrl: string) { - onAuthenticationSuccess(redirectUrl); - } - - function onSecondFactorTotpFailure(err: Error) { - notifier.error(UserMessages.AUTHENTICATION_TOTP_FAILED); - } - - function onU2fAuthenticationSuccess(redirectUrl: string) { - onAuthenticationSuccess(redirectUrl); - } - - function onU2fAuthenticationFailure() { - // TODO(clems4ever): we should not display this error message until a device - // is registered. - // notifier.error(UserMessages.AUTHENTICATION_U2F_FAILED); - } - - function onTOTPFormSubmitted(): boolean { - const token = $(ClientConstants.TOTP_TOKEN_SELECTOR).val() as string; - TOTPValidator.validate(token, $) - .then(onSecondFactorTotpSuccess) - .catch(onSecondFactorTotpFailure); - return false; - } - - $(window.document).ready(function () { - $(ClientConstants.TOTP_FORM_SELECTOR).on("submit", onTOTPFormSubmitted); - U2FValidator.validate($) - .then(onU2fAuthenticationSuccess, onU2fAuthenticationFailure); - }); -} \ No newline at end of file diff --git a/themes/triangles/client/src/lib/totp-register/totp-register.ts b/themes/triangles/client/src/lib/totp-register/totp-register.ts deleted file mode 100644 index 6a9aa7ee0..000000000 --- a/themes/triangles/client/src/lib/totp-register/totp-register.ts +++ /dev/null @@ -1,11 +0,0 @@ - -import jslogger = require("js-logger"); -import UISelector = require("./ui-selector"); - -export default function(window: Window, $: JQueryStatic) { - jslogger.debug("Creating QRCode from OTPAuth url"); - const qrcode = $(UISelector.QRCODE_ID_SELECTOR); - const val = qrcode.text(); - qrcode.empty(); - new (window as any).QRCode(qrcode.get(0), val); -} diff --git a/themes/triangles/client/src/lib/totp-register/ui-selector.ts b/themes/triangles/client/src/lib/totp-register/ui-selector.ts deleted file mode 100644 index 9d43fabea..000000000 --- a/themes/triangles/client/src/lib/totp-register/ui-selector.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const QRCODE_ID_SELECTOR = "#qrcode"; \ No newline at end of file diff --git a/themes/triangles/client/src/lib/u2f-register/u2f-register.ts b/themes/triangles/client/src/lib/u2f-register/u2f-register.ts deleted file mode 100644 index abf40ee05..000000000 --- a/themes/triangles/client/src/lib/u2f-register/u2f-register.ts +++ /dev/null @@ -1,56 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import U2f = require("u2f"); -import * as U2fApi from "u2f-api"; -import { Notifier } from "../Notifier"; -import GetPromised from "../GetPromised"; -import Endpoints = require("../../../../shared/api"); -import UserMessages = require("../../../../shared/UserMessages"); -import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; -import { ErrorMessage } from "../../../../shared/ErrorMessage"; -import { SafeRedirect } from "../SafeRedirect"; - -export default function (window: Window, $: JQueryStatic) { - const notifier = new Notifier(".notification", $); - - function checkRegistration(regResponse: U2fApi.RegisterResponse): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - $.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, regResponse, undefined, "json") - .done((body: RedirectionMessage | ErrorMessage) => { - if (body && "error" in body) { - reject(new Error((body as ErrorMessage).error)); - return; - } - resolve((body as RedirectionMessage).redirect); - }) - .fail((xhr, status) => { - reject(new Error("Failed to register device.")); - }); - }); - } - - function requestRegistration(): BluebirdPromise { - return GetPromised($, Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, {}, - undefined, "json") - .then((registrationRequest: U2f.Request) => { - return U2fApi.register(registrationRequest, [], 60); - }) - .then((res) => checkRegistration(res)); - } - - function onRegisterFailure(err: Error) { - notifier.error(UserMessages.REGISTRATION_U2F_FAILED); - } - - $(document).ready(function () { - requestRegistration() - .then((redirectionUrl: string) => { - SafeRedirect(redirectionUrl, () => { - notifier.error(UserMessages.CANNOT_REDIRECT_TO_EXTERNAL_DOMAIN); - }); - }) - .catch((err) => { - onRegisterFailure(err); - }); - }); -} diff --git a/themes/triangles/client/src/thirdparties/qrcode.min.js b/themes/triangles/client/src/thirdparties/qrcode.min.js old mode 100644 new mode 100755 diff --git a/themes/triangles/client/src/thirdparties/u2f-api.js b/themes/triangles/client/src/thirdparties/u2f-api.js old mode 100644 new mode 100755 diff --git a/themes/triangles/client/test/Notifier.test.ts b/themes/triangles/client/test/Notifier.test.ts deleted file mode 100644 index 70bfea146..000000000 --- a/themes/triangles/client/test/Notifier.test.ts +++ /dev/null @@ -1,71 +0,0 @@ - -import Assert = require("assert"); -import Sinon = require("sinon"); -import JQueryMock = require("./mocks/jquery"); - -import { Notifier } from "../src/lib/Notifier"; - -describe("test notifier", function() { - const SELECTOR = "dummy-selector"; - const MESSAGE = "This is a message"; - let jqueryMock: { jquery: JQueryMock.JQueryMock, element: JQueryMock.JQueryElementsMock }; - let clock: any; - - beforeEach(function() { - jqueryMock = JQueryMock.JQueryMock(); - clock = Sinon.useFakeTimers(); - }); - - afterEach(function() { - clock.restore(); - }); - - function should_fade_in_and_out_on_notification(notificationType: string): void { - const delayReturn = { - fadeOut: Sinon.stub() - }; - - jqueryMock.element.fadeIn.yields(); - - function onFadedInCallback() { - Assert(jqueryMock.element.fadeIn.calledOnce); - Assert(jqueryMock.element.addClass.calledWith(notificationType)); - Assert(!jqueryMock.element.removeClass.calledWith(notificationType)); - clock.tick(10 * 1000); - } - - function onFadedOutCallback() { - Assert(jqueryMock.element.removeClass.calledWith(notificationType)); - Assert(jqueryMock.element.fadeOut.calledOnce); - } - - const notifier = new Notifier(SELECTOR, jqueryMock.jquery as any); - - // Call the method by its name... Bad but allows code reuse. - (notifier as any)[notificationType](MESSAGE, { - onFadedIn: onFadedInCallback, - onFadedOut: onFadedOutCallback - }); - - clock.tick(510); - - Assert(jqueryMock.element.fadeIn.calledOnce); - } - - - it("should fade in and fade out an error message", function() { - should_fade_in_and_out_on_notification("error"); - }); - - it("should fade in and fade out an info message", function() { - should_fade_in_and_out_on_notification("info"); - }); - - it("should fade in and fade out an warning message", function() { - should_fade_in_and_out_on_notification("warning"); - }); - - it("should fade in and fade out an success message", function() { - should_fade_in_and_out_on_notification("success"); - }); -}); \ No newline at end of file diff --git a/themes/triangles/client/test/firstfactor/FirstFactorValidator.test.ts b/themes/triangles/client/test/firstfactor/FirstFactorValidator.test.ts deleted file mode 100644 index ac8353278..000000000 --- a/themes/triangles/client/test/firstfactor/FirstFactorValidator.test.ts +++ /dev/null @@ -1,44 +0,0 @@ - -import FirstFactorValidator = require("../../src/lib/firstfactor/FirstFactorValidator"); -import JQueryMock = require("../mocks/jquery"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -describe("test FirstFactorValidator", function () { - it("should validate first factor successfully", () => { - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.done.yields({ redirect: "http://redirect" }); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return FirstFactorValidator.validate("username", "password", "http://redirect", jqueryMock.jquery as any); - }); - - function should_fail_first_factor_validation(errorMessage: string) { - const xhr = { - status: 401 - }; - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.fail.yields(xhr, errorMessage); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return FirstFactorValidator.validate("username", "password", "http://redirect", jqueryMock.jquery as any) - .then(function () { - return BluebirdPromise.reject(new Error("First factor validation successfully finished while it should have not.")); - }, function (err: Error) { - Assert.equal(errorMessage, err.message); - return BluebirdPromise.resolve(); - }); - } - - describe("should fail first factor validation", () => { - it("should fail with error", () => { - return should_fail_first_factor_validation("Authentication failed. Please check your credentials."); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/client/test/mocks/NotifierStub.ts b/themes/triangles/client/test/mocks/NotifierStub.ts deleted file mode 100644 index 9c268d66d..000000000 --- a/themes/triangles/client/test/mocks/NotifierStub.ts +++ /dev/null @@ -1,33 +0,0 @@ - -import Sinon = require("sinon"); -import { INotifier } from "../../src/lib/INotifier"; - -export class NotifierStub implements INotifier { - successStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - warnStub: Sinon.SinonStub; - infoStub: Sinon.SinonStub; - - constructor() { - this.successStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - this.warnStub = Sinon.stub(); - this.infoStub = Sinon.stub(); - } - - success(msg: string) { - this.successStub(); - } - - error(msg: string) { - this.errorStub(); - } - - warning(msg: string) { - this.warnStub(); - } - - info(msg: string) { - this.infoStub(); - } -} \ No newline at end of file diff --git a/themes/triangles/client/test/mocks/jquery.ts b/themes/triangles/client/test/mocks/jquery.ts deleted file mode 100644 index 273f90861..000000000 --- a/themes/triangles/client/test/mocks/jquery.ts +++ /dev/null @@ -1,59 +0,0 @@ - -import sinon = require("sinon"); -import jquery = require("jquery"); - - -export interface JQueryMock extends sinon.SinonStub { - get: sinon.SinonStub; - post: sinon.SinonStub; - ajax: sinon.SinonStub; - notify: sinon.SinonStub; -} - -export interface JQueryElementsMock { - ready: sinon.SinonStub; - show: sinon.SinonStub; - hide: sinon.SinonStub; - html: sinon.SinonStub; - addClass: sinon.SinonStub; - removeClass: sinon.SinonStub; - fadeIn: sinon.SinonStub; - fadeOut: sinon.SinonStub; - on: sinon.SinonStub; -} - -export interface JQueryDeferredMock { - done: sinon.SinonStub; - fail: sinon.SinonStub; -} - -export function JQueryMock(): { jquery: JQueryMock, element: JQueryElementsMock } { - const jquery = sinon.stub() as any; - const jqueryInstance: JQueryElementsMock = { - ready: sinon.stub(), - show: sinon.stub(), - hide: sinon.stub(), - html: sinon.stub(), - addClass: sinon.stub(), - removeClass: sinon.stub(), - fadeIn: sinon.stub(), - fadeOut: sinon.stub(), - on: sinon.stub() - }; - jquery.ajax = sinon.stub(); - jquery.get = sinon.stub(); - jquery.post = sinon.stub(); - jquery.notify = sinon.stub(); - jquery.returns(jqueryInstance); - return { - jquery: jquery, - element: jqueryInstance - }; -} - -export function JQueryDeferredMock(): JQueryDeferredMock { - return { - done: sinon.stub(), - fail: sinon.stub() - }; -} diff --git a/themes/triangles/client/test/mocks/u2f-api.ts b/themes/triangles/client/test/mocks/u2f-api.ts deleted file mode 100644 index d123f6a95..000000000 --- a/themes/triangles/client/test/mocks/u2f-api.ts +++ /dev/null @@ -1,14 +0,0 @@ - -import sinon = require("sinon"); - -export interface U2FApiMock { - sign: sinon.SinonStub; - register: sinon.SinonStub; -} - -export function U2FApiMock(): U2FApiMock { - return { - sign: sinon.stub(), - register: sinon.stub() - }; -} \ No newline at end of file diff --git a/themes/triangles/client/test/secondfactor/TOTPValidator.test.ts b/themes/triangles/client/test/secondfactor/TOTPValidator.test.ts deleted file mode 100644 index 5dd6f15c3..000000000 --- a/themes/triangles/client/test/secondfactor/TOTPValidator.test.ts +++ /dev/null @@ -1,37 +0,0 @@ - -import TOTPValidator = require("../../src/lib/secondfactor/TOTPValidator"); -import JQueryMock = require("../mocks/jquery"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -describe("test TOTPValidator", function () { - it("should initiate an identity check successfully", () => { - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.done.yields({ redirect: "https://home.test.url" }); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return TOTPValidator.validate("totp_token", jqueryMock.jquery as any); - }); - - it("should fail validating TOTP token", () => { - const errorMessage = "Error while validating TOTP token"; - - const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.fail.yields(undefined, errorMessage); - postPromise.done.returns(postPromise); - - const jqueryMock = JQueryMock.JQueryMock(); - jqueryMock.jquery.ajax.returns(postPromise); - - return TOTPValidator.validate("totp_token", jqueryMock.jquery as any) - .then(function () { - return BluebirdPromise.reject(new Error("Registration successfully finished while it should have not.")); - }, function (err: Error) { - Assert.equal(errorMessage, err.message); - return BluebirdPromise.resolve(); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/client/test/totp-register/totp-register.test.ts b/themes/triangles/client/test/totp-register/totp-register.test.ts deleted file mode 100644 index 86fc455a1..000000000 --- a/themes/triangles/client/test/totp-register/totp-register.test.ts +++ /dev/null @@ -1,31 +0,0 @@ - -import sinon = require("sinon"); -import assert = require("assert"); - -import UISelector = require("../../src/lib/totp-register/ui-selector"); -import TOTPRegister = require("../../src/lib/totp-register/totp-register"); - -describe("test totp-register", function() { - let jqueryMock: any; - let windowMock: any; - before(function() { - jqueryMock = sinon.stub(); - windowMock = { - QRCode: sinon.spy() - }; - }); - - it("should create qrcode in page", function() { - const mock = { - text: sinon.stub(), - empty: sinon.stub(), - get: sinon.stub() - }; - jqueryMock.withArgs(UISelector.QRCODE_ID_SELECTOR).returns(mock); - - TOTPRegister.default(windowMock, jqueryMock); - - assert(mock.text.calledOnce); - assert(mock.empty.calledOnce); - }); -}); \ No newline at end of file diff --git a/themes/triangles/client/tsconfig.json b/themes/triangles/client/tsconfig.json deleted file mode 100644 index 0bb4d62ff..000000000 --- a/themes/triangles/client/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "moduleResolution": "node", - "noImplicitAny": true, - "sourceMap": true, - "removeComments": true, - "outDir": "../dist", - "baseUrl": ".", - "paths": { - "*": [ - "./types/*", - "../shared/types/*" - ] - } - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "test/**/*" - ] -} diff --git a/themes/triangles/client/tslint.json b/themes/triangles/client/tslint.json deleted file mode 100644 index c2c1b7501..000000000 --- a/themes/triangles/client/tslint.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "no-var-keyword": true, - "quotemark": [ - true, - "double", - "avoid-escape" - ], - "semicolon": [ - true, - "always", - "ignore-bound-class-methods" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-module", - "check-separator", - "check-type" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "onespace", - "index-signature": "onespace", - "parameter": "onespace", - "property-declaration": "onespace", - "variable-declaration": "onespace" - } - ], - "no-internal-module": true, - "no-trailing-whitespace": true, - "no-null-keyword": true, - "prefer-const": true, - "jsdoc-format": true - } -} diff --git a/themes/triangles/server/.directory b/themes/triangles/server/.directory old mode 100644 new mode 100755 diff --git a/themes/triangles/server/src/index.ts b/themes/triangles/server/src/index.ts deleted file mode 100755 index fcbf4d022..000000000 --- a/themes/triangles/server/src/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -#! /usr/bin/env node - -import Server from "./lib/Server"; -import { GlobalDependencies } from "../types/Dependencies"; -import YAML = require("yamljs"); - -const configurationFilepath = process.argv[2]; -if (!configurationFilepath) { - console.log("No config file has been provided."); - console.log("Usage: authelia "); - process.exit(0); -} - -const yamlContent = YAML.load(configurationFilepath); - -const deps: GlobalDependencies = { - u2f: require("u2f"), - ldapjs: require("ldapjs"), - session: require("express-session"), - winston: require("winston"), - speakeasy: require("speakeasy"), - nedb: require("nedb"), - ConnectRedis: require("connect-redis"), - Redis: require("redis") -}; - -const server = new Server(deps); -server.start(yamlContent, deps); diff --git a/themes/triangles/server/src/lib/.directory b/themes/triangles/server/src/lib/.directory deleted file mode 100644 index 006b379ad..000000000 --- a/themes/triangles/server/src/lib/.directory +++ /dev/null @@ -1,4 +0,0 @@ -[Dolphin] -Timestamp=2018,12,17,20,59,13 -Version=3 -ViewMode=1 diff --git a/themes/triangles/server/src/lib/AuthenticationSessionHandler.ts b/themes/triangles/server/src/lib/AuthenticationSessionHandler.ts deleted file mode 100644 index 57361bf8a..000000000 --- a/themes/triangles/server/src/lib/AuthenticationSessionHandler.ts +++ /dev/null @@ -1,45 +0,0 @@ - - -import express = require("express"); -import U2f = require("u2f"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { IRequestLogger } from "./logging/IRequestLogger"; -import { Level } from "./authentication/Level"; - -const INITIAL_AUTHENTICATION_SESSION: AuthenticationSession = { - keep_me_logged_in: false, - authentication_level: Level.NOT_AUTHENTICATED, - last_activity_datetime: undefined, - userid: undefined, - email: undefined, - groups: [], - register_request: undefined, - sign_request: undefined, - identity_check: undefined, - redirect: undefined -}; - -export class AuthenticationSessionHandler { - static reset(req: express.Request): void { - req.session.auth = Object.assign({}, INITIAL_AUTHENTICATION_SESSION, {}); - - // Initialize last activity with current time - req.session.auth.last_activity_datetime = new Date().getTime(); - } - - static get(req: express.Request, logger: IRequestLogger): AuthenticationSession { - if (!req.session) { - const errorMsg = "Something is wrong with session cookies. Please check Redis is running and Authelia can connect to it."; - logger.error(req, errorMsg); - throw new Error(errorMsg); - } - - if (!req.session.auth) { - logger.debug(req, "Authentication session %s was undefined. Resetting.", req.sessionID); - AuthenticationSessionHandler.reset(req); - } - - return req.session.auth; - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/ErrorReplies.ts b/themes/triangles/server/src/lib/ErrorReplies.ts deleted file mode 100644 index f1c5f4fd1..000000000 --- a/themes/triangles/server/src/lib/ErrorReplies.ts +++ /dev/null @@ -1,49 +0,0 @@ -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import { IRequestLogger } from "./logging/IRequestLogger"; - -function replyWithError(req: express.Request, res: express.Response, - code: number, logger: IRequestLogger, body?: Object): (err: Error) => void { - return function (err: Error): void { - if (req.originalUrl.startsWith("/api/") || code == 200) { - logger.error(req, "Reply with error %d: %s", code, err.message); - logger.debug(req, "%s", err.stack); - res.status(code); - res.send(body); - } - else { - logger.error(req, "Redirect to error %d: %s", code, err.message); - logger.debug(req, "%s", err.stack); - res.redirect("/error/" + code); - } - }; -} - -export function redirectTo(redirectUrl: string, req: express.Request, - res: express.Response, logger: IRequestLogger) { - return function(err: Error) { - logger.error(req, "Error: %s", err.message); - logger.debug(req, "Redirecting to %s", redirectUrl); - res.redirect(redirectUrl); - }; -} - -export function replyWithError400(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 400, logger); -} - -export function replyWithError401(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 401, logger); -} - -export function replyWithError403(req: express.Request, - res: express.Response, logger: IRequestLogger) { - return replyWithError(req, res, 403, logger); -} - -export function replyWithError200(req: express.Request, - res: express.Response, logger: IRequestLogger, message: string) { - return replyWithError(req, res, 200, logger, { error: message }); -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/Exceptions.ts b/themes/triangles/server/src/lib/Exceptions.ts deleted file mode 100644 index 83fa4eb6b..000000000 --- a/themes/triangles/server/src/lib/Exceptions.ts +++ /dev/null @@ -1,88 +0,0 @@ - -export class LdapSearchError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapSearchError"; - (Object).setPrototypeOf(this, LdapSearchError.prototype); - } -} - -export class LdapBindError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapBindError"; - (Object).setPrototypeOf(this, LdapBindError.prototype); - } -} - -export class LdapError extends Error { - constructor(message?: string) { - super(message); - this.name = "LdapError"; - (Object).setPrototypeOf(this, LdapError.prototype); - } -} - -export class IdentityError extends Error { - constructor(message?: string) { - super(message); - this.name = "IdentityError"; - (Object).setPrototypeOf(this, IdentityError.prototype); - } -} - -export class AccessDeniedError extends Error { - constructor(message?: string) { - super(message); - this.name = "AccessDeniedError"; - (Object).setPrototypeOf(this, AccessDeniedError.prototype); - } -} - -export class AuthenticationRegulationError extends Error { - constructor(message?: string) { - super(message); - this.name = "AuthenticationRegulationError"; - (Object).setPrototypeOf(this, AuthenticationRegulationError.prototype); - } -} - -export class InvalidTOTPError extends Error { - constructor(message?: string) { - super(message); - this.name = "InvalidTOTPError"; - (Object).setPrototypeOf(this, InvalidTOTPError.prototype); - } -} - -export class NotAuthenticatedError extends Error { - constructor(message?: string) { - super(message); - this.name = "NotAuthenticatedError"; - (Object).setPrototypeOf(this, NotAuthenticatedError.prototype); - } -} - -export class NotAuthorizedError extends Error { - constructor(message?: string) { - super(message); - this.name = "NotAuthanticatedError"; - (Object).setPrototypeOf(this, NotAuthorizedError.prototype); - } -} - -export class FirstFactorValidationError extends Error { - constructor(message?: string) { - super(message); - this.name = "FirstFactorValidationError"; - (Object).setPrototypeOf(this, FirstFactorValidationError.prototype); - } -} - -export class SecondFactorValidationError extends Error { - constructor(message?: string) { - super(message); - this.name = "SecondFactorValidationError"; - (Object).setPrototypeOf(this, FirstFactorValidationError.prototype); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/FirstFactorValidator.ts b/themes/triangles/server/src/lib/FirstFactorValidator.ts deleted file mode 100644 index 231060002..000000000 --- a/themes/triangles/server/src/lib/FirstFactorValidator.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import objectPath = require("object-path"); -import Exceptions = require("./Exceptions"); -import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler"; -import { IRequestLogger } from "./logging/IRequestLogger"; -import { Level } from "./authentication/Level"; - -export function validate(req: express.Request, logger: IRequestLogger): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, logger); - - if (!authSession.userid || authSession.authentication_level < Level.ONE_FACTOR) - return reject(new Exceptions.FirstFactorValidationError( - "First factor has not been validated yet.")); - - resolve(); - }); -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/IdentityCheckMiddleware.spec.ts b/themes/triangles/server/src/lib/IdentityCheckMiddleware.spec.ts deleted file mode 100644 index 842ed6bcb..000000000 --- a/themes/triangles/server/src/lib/IdentityCheckMiddleware.spec.ts +++ /dev/null @@ -1,176 +0,0 @@ - -import sinon = require("sinon"); -import IdentityValidator = require("./IdentityCheckMiddleware"); -import { AuthenticationSessionHandler } - from "./AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { UserDataStore } from "./storage/UserDataStore"; -import exceptions = require("./Exceptions"); -import { ServerVariables } from "./ServerVariables"; -import Assert = require("assert"); -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("./stubs/express.spec"); -import NotifierMock = require("./notifiers/NotifierStub.spec"); -import { IdentityValidableStub } from "./IdentityValidableStub.spec"; -import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "./ServerVariablesMockBuilder.spec"; -import { PRE_VALIDATION_TEMPLATE } - from "./IdentityCheckPreValidationTemplate"; - - -describe("IdentityCheckMiddleware", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let app: express.Application; - let app_get: sinon.SinonStub; - let app_post: sinon.SinonStub; - let identityValidable: IdentityValidableStub; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - - req.headers = {}; - req.originalUrl = "/non-api/xxx"; - req.session = {}; - - req.query = {}; - req.app = {}; - - identityValidable = new IdentityValidableStub(); - - mocks.notifier.notifyStub.returns(BluebirdPromise.resolve()); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve()); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({ userId: "user" })); - - app = express(); - app_get = sinon.stub(app, "get"); - app_post = sinon.stub(app, "post"); - }); - - afterEach(function () { - app_get.restore(); - app_post.restore(); - }); - - describe("test start GET", function () { - it("should redirect to error 401 if pre validation initialization \ -throws a first factor error", function () { - identityValidable.preValidationInitStub.returns(BluebirdPromise.reject( - new exceptions.FirstFactorValidationError( - "Error during prevalidation"))); - const callback = IdentityValidator.get_start_validation( - identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(() => { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - - // In that case we answer with 200 to avoid user enumeration. - it("should send 200 if email is missing in provided identity", function () { - const identity = { userid: "abc" }; - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(identityValidable.preValidationResponseStub.called); - }); - }); - - // In that case we answer with 200 to avoid user enumeration. - it("should send 200 if userid is missing in provided identity", - function () { - const endpoint = "/protected"; - const identity = { email: "abc@example.com" }; - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(identityValidable.preValidationResponseStub.called); - }); - }); - - it("should issue a token, send an email and return 204", function () { - const endpoint = "/protected"; - const identity = { userid: "user", email: "abc@example.com" }; - req.get = sinon.stub().withArgs("Host").returns("localhost"); - - identityValidable.preValidationInitStub - .returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator - .get_start_validation(identityValidable, "/finish_endpoint", vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(mocks.notifier.notifyStub.calledOnce); - Assert(mocks.userDataStore.produceIdentityValidationTokenStub - .calledOnce); - Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub - .getCall(0).args[0], "user"); - Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub - .getCall(0).args[3], 240000); - }); - }); - }); - - - - describe("test finish GET", function () { - it("should send 401 if no identity_token is provided", () => { - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - - return callback(req as any, res as any, undefined) - .then(function () { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - - it("should call postValidation if identity_token is provided and still \ -valid", function () { - req.query.identity_token = "token"; - - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - return callback(req as any, res as any, undefined); - }); - - it("should return 401 if identity_token is provided but invalid", - function () { - req.query.identity_token = "token"; - - identityValidable.postValidationInitStub - .returns(BluebirdPromise.resolve()); - mocks.userDataStore.consumeIdentityValidationTokenStub.reset(); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.reject(new Error("Invalid token"))); - - const callback = IdentityValidator - .get_finish_validation(identityValidable, vars); - return callback(req as any, res as any, undefined) - .then(() => { - Assert(res.redirect.calledWith("/error/401")); - }); - }); - }); -}); diff --git a/themes/triangles/server/src/lib/IdentityCheckMiddleware.ts b/themes/triangles/server/src/lib/IdentityCheckMiddleware.ts deleted file mode 100644 index e72ea4db1..000000000 --- a/themes/triangles/server/src/lib/IdentityCheckMiddleware.ts +++ /dev/null @@ -1,138 +0,0 @@ -import objectPath = require("object-path"); -import randomstring = require("randomstring"); -import BluebirdPromise = require("bluebird"); -import util = require("util"); -import Exceptions = require("./Exceptions"); -import fs = require("fs"); -import ejs = require("ejs"); -import { IUserDataStore } from "./storage/IUserDataStore"; -import Express = require("express"); -import ErrorReplies = require("./ErrorReplies"); -import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../types/AuthenticationSession"; -import { ServerVariables } from "./ServerVariables"; -import { IdentityValidable } from "./IdentityValidable"; - -import Identity = require("../../types/Identity"); -import { IdentityValidationDocument } - from "./storage/IdentityValidationDocument"; - -const filePath = __dirname + "/../resources/email-template.ejs"; -const email_template = fs.readFileSync(filePath, "utf8"); - -function createAndSaveToken(userid: string, challenge: string, - userDataStore: IUserDataStore): BluebirdPromise { - - const five_minutes = 4 * 60 * 1000; - const token = randomstring.generate({ length: 64 }); - const that = this; - - return userDataStore.produceIdentityValidationToken(userid, token, challenge, - five_minutes) - .then(function () { - return BluebirdPromise.resolve(token); - }); -} - -function consumeToken(token: string, challenge: string, - userDataStore: IUserDataStore) - : BluebirdPromise { - return userDataStore.consumeIdentityValidationToken(token, challenge); -} - -export function register(app: Express.Application, - pre_validation_endpoint: string, - post_validation_endpoint: string, - handler: IdentityValidable, - vars: ServerVariables) { - - app.get(pre_validation_endpoint, - get_start_validation(handler, post_validation_endpoint, vars)); - app.get(post_validation_endpoint, - get_finish_validation(handler, vars)); -} - -function checkIdentityToken(req: Express.Request, identityToken: string) - : BluebirdPromise { - if (!identityToken) - return BluebirdPromise.reject( - new Exceptions.AccessDeniedError("No identity token provided")); - return BluebirdPromise.resolve(); -} - -export function get_finish_validation(handler: IdentityValidable, - vars: ServerVariables) - : Express.RequestHandler { - - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - - let authSession: AuthenticationSession; - const identityToken = objectPath.get( - req, "query.identity_token"); - vars.logger.debug(req, "Identity token provided is %s", identityToken); - - return checkIdentityToken(req, identityToken) - .then(() => { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - return handler.postValidationInit(req); - }) - .then(() => { - return consumeToken(identityToken, handler.challenge(), - vars.userDataStore); - }) - .then((doc: IdentityValidationDocument) => { - authSession.identity_check = { - challenge: handler.challenge(), - userid: doc.userId - }; - handler.postValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - }; -} - -export function get_start_validation(handler: IdentityValidable, - postValidationEndpoint: string, - vars: ServerVariables) - : Express.RequestHandler { - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - let identity: Identity.Identity; - - return handler.preValidationInit(req) - .then((id: Identity.Identity) => { - identity = id; - const email = identity.email; - const userid = identity.userid; - vars.logger.info(req, "Start identity validation of user \"%s\"", - userid); - - if (!(email && userid)) - return BluebirdPromise.reject(new Exceptions.IdentityError( - "Missing user id or email address")); - - return createAndSaveToken(userid, handler.challenge(), - vars.userDataStore); - }) - .then((token) => { - const host = req.get("Host"); - const link_url = util.format("https://%s%s?identity_token=%s", host, - postValidationEndpoint, token); - vars.logger.info(req, "Notification sent to user \"%s\"", - identity.userid); - return vars.notifier.notify(identity.email, handler.mailSubject(), - link_url); - }) - .then(() => { - handler.preValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(Exceptions.IdentityError, (err: Error) => { - handler.preValidationResponse(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - }; -} diff --git a/themes/triangles/server/src/lib/IdentityCheckPreValidationTemplate.ts b/themes/triangles/server/src/lib/IdentityCheckPreValidationTemplate.ts deleted file mode 100644 index 0161ce406..000000000 --- a/themes/triangles/server/src/lib/IdentityCheckPreValidationTemplate.ts +++ /dev/null @@ -1,3 +0,0 @@ - - -export const PRE_VALIDATION_TEMPLATE = "need-identity-validation"; \ No newline at end of file diff --git a/themes/triangles/server/src/lib/IdentityValidable.ts b/themes/triangles/server/src/lib/IdentityValidable.ts deleted file mode 100644 index 075580c9e..000000000 --- a/themes/triangles/server/src/lib/IdentityValidable.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Bluebird = require("bluebird"); -import Identity = require("../../types/Identity"); - -// IdentityValidator allows user to go through a identity validation process -// in two steps: -// - Request an operation to be performed (password reset, registration). -// - Confirm operation with email. - -export interface IdentityValidable { - challenge(): string; - preValidationInit(req: Express.Request): Bluebird; - postValidationInit(req: Express.Request): Bluebird; - - // Serves a page after identity check request - preValidationResponse(req: Express.Request, res: Express.Response): void; - // Serves the page if identity validated - postValidationResponse(req: Express.Request, res: Express.Response): void; - mailSubject(): string; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/IdentityValidableStub.spec.ts b/themes/triangles/server/src/lib/IdentityValidableStub.spec.ts deleted file mode 100644 index 20a977140..000000000 --- a/themes/triangles/server/src/lib/IdentityValidableStub.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ - -import Sinon = require("sinon"); -import { IdentityValidable } from "./IdentityValidable"; -import express = require("express"); -import Bluebird = require("bluebird"); -import { Identity } from "../../types/Identity"; - - -export class IdentityValidableStub implements IdentityValidable { - challengeStub: Sinon.SinonStub; - preValidationInitStub: Sinon.SinonStub; - postValidationInitStub: Sinon.SinonStub; - preValidationResponseStub: Sinon.SinonStub; - postValidationResponseStub: Sinon.SinonStub; - mailSubjectStub: Sinon.SinonStub; - - constructor() { - this.challengeStub = Sinon.stub(); - - this.preValidationInitStub = Sinon.stub(); - this.postValidationInitStub = Sinon.stub(); - - this.preValidationResponseStub = Sinon.stub(); - this.postValidationResponseStub = Sinon.stub(); - - this.mailSubjectStub = Sinon.stub(); - } - - challenge(): string { - return this.challengeStub(); - } - - preValidationInit(req: Express.Request): Bluebird { - return this.preValidationInitStub(req); - } - - postValidationInit(req: Express.Request): Bluebird { - return this.postValidationInitStub(req); - } - - preValidationResponse(req: Express.Request, res: Express.Response): void { - return this.preValidationResponseStub(req, res); - } - - postValidationResponse(req: Express.Request, res: Express.Response): void { - return this.postValidationResponseStub(req, res); - } - - mailSubject(): string { - return this.mailSubjectStub(); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/Server.spec.ts b/themes/triangles/server/src/lib/Server.spec.ts deleted file mode 100644 index 365163254..000000000 --- a/themes/triangles/server/src/lib/Server.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ - -import Assert = require("assert"); -import Sinon = require("sinon"); -import nedb = require("nedb"); -import express = require("express"); -import winston = require("winston"); -import speakeasy = require("speakeasy"); -import u2f = require("u2f"); -import session = require("express-session"); -import { Configuration } from "./configuration/schema/Configuration"; -import { GlobalDependencies } from "../../types/Dependencies"; -import Server from "./Server"; -import { LdapjsMock, LdapjsClientMock } from "./stubs/ldapjs.spec"; - - -describe("Server", function () { - let deps: GlobalDependencies; - let sessionMock: Sinon.SinonSpy; - let ldapjsMock: LdapjsMock; - - before(function () { - sessionMock = Sinon.spy(session); - ldapjsMock = new LdapjsMock(); - - deps = { - speakeasy: speakeasy, - u2f: u2f, - nedb: nedb, - winston: winston, - ldapjs: ldapjsMock as any, - session: sessionMock as any, - ConnectRedis: Sinon.spy(), - Redis: Sinon.spy() as any - }; - }); - - - it("should set cookie scope to domain set in the config", function () { - const config: Configuration = { - port: 8081, - session: { - domain: "example.com", - secret: "secret" - }, - authentication_backend: { - ldap: { - url: "http://ldap", - user: "user", - password: "password", - base_dn: "dc=example,dc=com" - }, - }, - notifier: { - email: { - username: "user@example.com", - password: "password", - sender: "test@authelia.com", - service: "gmail" - } - }, - regulation: { - max_retries: 3, - ban_time: 5 * 60, - find_time: 5 * 60 - }, - storage: { - local: { - in_memory: true - } - } - }; - - const server = new Server(deps); - server.start(config, deps) - .then(function () { - Assert(sessionMock.calledOnce); - Assert.equal(sessionMock.getCall(0).args[0].cookie.domain, "example.com"); - server.stop(); - }); - }); -}); diff --git a/themes/triangles/server/src/lib/Server.ts b/themes/triangles/server/src/lib/Server.ts deleted file mode 100644 index 4090f6294..000000000 --- a/themes/triangles/server/src/lib/Server.ts +++ /dev/null @@ -1,93 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import ObjectPath = require("object-path"); - -import { Configuration } from "./configuration/schema/Configuration"; -import { GlobalDependencies } from "../../types/Dependencies"; -import { UserDataStore } from "./storage/UserDataStore"; -import { ConfigurationParser } from "./configuration/ConfigurationParser"; -import { SessionConfigurationBuilder } from "./configuration/SessionConfigurationBuilder"; -import { GlobalLogger } from "./logging/GlobalLogger"; -import { RequestLogger } from "./logging/RequestLogger"; -import { ServerVariables } from "./ServerVariables"; -import { ServerVariablesInitializer } from "./ServerVariablesInitializer"; -import { Configurator } from "./web_server/Configurator"; - -import * as Express from "express"; -import * as Path from "path"; -import * as http from "http"; - -function clone(obj: any) { - return JSON.parse(JSON.stringify(obj)); -} - -export default class Server { - private httpServer: http.Server; - private globalLogger: GlobalLogger; - private requestLogger: RequestLogger; - - constructor(deps: GlobalDependencies) { - this.globalLogger = new GlobalLogger(deps.winston); - this.requestLogger = new RequestLogger(deps.winston); - } - - private displayConfigurations(configuration: Configuration) { - const displayableConfiguration: Configuration = clone(configuration); - const STARS = "*****"; - - if (displayableConfiguration.authentication_backend.ldap) { - displayableConfiguration.authentication_backend.ldap.password = STARS; - } - - displayableConfiguration.session.secret = STARS; - if (displayableConfiguration.notifier && displayableConfiguration.notifier.email) - displayableConfiguration.notifier.email.password = STARS; - if (displayableConfiguration.notifier && displayableConfiguration.notifier.smtp) - displayableConfiguration.notifier.smtp.password = STARS; - - this.globalLogger.debug("User configuration is %s", - JSON.stringify(displayableConfiguration, undefined, 2)); - } - - private setup(config: Configuration, app: Express.Application, deps: GlobalDependencies): BluebirdPromise { - const that = this; - return ServerVariablesInitializer.initialize( - config, this.globalLogger, this.requestLogger, deps) - .then(function (vars: ServerVariables) { - Configurator.configure(config, app, vars, deps); - return BluebirdPromise.resolve(); - }); - } - - private startServer(app: Express.Application, port: number) { - const that = this; - that.globalLogger.info("Starting Authelia..."); - return new BluebirdPromise((resolve, reject) => { - this.httpServer = app.listen(port, function (err: string) { - that.globalLogger.info("Listening on port %d...", port); - resolve(); - }); - }); - } - - start(configuration: Configuration, deps: GlobalDependencies) - : BluebirdPromise { - const that = this; - const app = Express(); - - const appConfiguration = ConfigurationParser.parse(configuration); - - // by default the level of logs is info - deps.winston.level = appConfiguration.logs_level; - this.displayConfigurations(appConfiguration); - - return this.setup(appConfiguration, app, deps) - .then(function () { - return that.startServer(app, appConfiguration.port); - }); - } - - stop() { - this.httpServer.close(); - } -} - diff --git a/themes/triangles/server/src/lib/ServerVariables.ts b/themes/triangles/server/src/lib/ServerVariables.ts deleted file mode 100644 index cd3dd6dc8..000000000 --- a/themes/triangles/server/src/lib/ServerVariables.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { IRequestLogger } from "./logging/IRequestLogger"; -import { ITotpHandler } from "./authentication/totp/ITotpHandler"; -import { IU2fHandler } from "./authentication/u2f/IU2fHandler"; -import { IUserDataStore } from "./storage/IUserDataStore"; -import { INotifier } from "./notifiers/INotifier"; -import { IRegulator } from "./regulation/IRegulator"; -import { Configuration } from "./configuration/schema/Configuration"; -import { IAuthorizer } from "./authorization/IAuthorizer"; -import { IUsersDatabase } from "./authentication/backends/IUsersDatabase"; - -export interface ServerVariables { - logger: IRequestLogger; - usersDatabase: IUsersDatabase; - totpHandler: ITotpHandler; - u2f: IU2fHandler; - userDataStore: IUserDataStore; - notifier: INotifier; - regulator: IRegulator; - config: Configuration; - authorizer: IAuthorizer; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/ServerVariablesInitializer.ts b/themes/triangles/server/src/lib/ServerVariablesInitializer.ts deleted file mode 100644 index df79238cc..000000000 --- a/themes/triangles/server/src/lib/ServerVariablesInitializer.ts +++ /dev/null @@ -1,116 +0,0 @@ - -import winston = require("winston"); -import BluebirdPromise = require("bluebird"); -import U2F = require("u2f"); -import Nodemailer = require("nodemailer"); - -import { IRequestLogger } from "./logging/IRequestLogger"; -import { RequestLogger } from "./logging/RequestLogger"; - -import { TotpHandler } from "./authentication/totp/TotpHandler"; -import { ITotpHandler } from "./authentication/totp/ITotpHandler"; -import { NotifierFactory } from "./notifiers/NotifierFactory"; -import { MailSenderBuilder } from "./notifiers/MailSenderBuilder"; -import { LdapUsersDatabase } from "./authentication/backends/ldap/LdapUsersDatabase"; -import { ConnectorFactory } from "./authentication/backends/ldap/connector/ConnectorFactory"; - -import { IUserDataStore } from "./storage/IUserDataStore"; -import { UserDataStore } from "./storage/UserDataStore"; -import { INotifier } from "./notifiers/INotifier"; -import { Regulator } from "./regulation/Regulator"; -import { IRegulator } from "./regulation/IRegulator"; -import Configuration = require("./configuration/schema/Configuration"); -import { CollectionFactoryFactory } from "./storage/CollectionFactoryFactory"; -import { ICollectionFactory } from "./storage/ICollectionFactory"; -import { MongoCollectionFactory } from "./storage/mongo/MongoCollectionFactory"; -import { IMongoClient } from "./connectors/mongo/IMongoClient"; - -import { GlobalDependencies } from "../../types/Dependencies"; -import { ServerVariables } from "./ServerVariables"; -import { MongoClient } from "./connectors/mongo/MongoClient"; -import { IGlobalLogger } from "./logging/IGlobalLogger"; -import { SessionFactory } from "./authentication/backends/ldap/SessionFactory"; -import { IUsersDatabase } from "./authentication/backends/IUsersDatabase"; -import { FileUsersDatabase } from "./authentication/backends/file/FileUsersDatabase"; -import { Authorizer } from "./authorization/Authorizer"; - -class UserDataStoreFactory { - static create(config: Configuration.Configuration, globalLogger: IGlobalLogger): BluebirdPromise { - if (config.storage.local) { - const nedbOptions: Nedb.DataStoreOptions = { - filename: config.storage.local.path, - inMemoryOnly: config.storage.local.in_memory - }; - const collectionFactory = CollectionFactoryFactory.createNedb(nedbOptions); - return BluebirdPromise.resolve(new UserDataStore(collectionFactory)); - } - else if (config.storage.mongo) { - const mongoClient = new MongoClient( - config.storage.mongo, - globalLogger); - const collectionFactory = CollectionFactoryFactory.createMongo(mongoClient); - return BluebirdPromise.resolve(new UserDataStore(collectionFactory)); - } - - return BluebirdPromise.reject(new Error("Storage backend incorrectly configured.")); - } -} - -export class ServerVariablesInitializer { - static createUsersDatabase( - config: Configuration.Configuration, - deps: GlobalDependencies) - : IUsersDatabase { - - if (config.authentication_backend.ldap) { - const ldapConfig = config.authentication_backend.ldap; - return new LdapUsersDatabase( - new SessionFactory( - ldapConfig, - new ConnectorFactory(ldapConfig, deps.ldapjs), - deps.winston - ), - ldapConfig - ); - } - else if (config.authentication_backend.file) { - return new FileUsersDatabase(config.authentication_backend.file); - } - } - - static initialize( - config: Configuration.Configuration, - globalLogger: IGlobalLogger, - requestLogger: IRequestLogger, - deps: GlobalDependencies) - : BluebirdPromise { - - const mailSenderBuilder = - new MailSenderBuilder(Nodemailer); - const notifier = NotifierFactory.build( - config.notifier, mailSenderBuilder); - const authorizer = new Authorizer(config.access_control, deps.winston); - const totpHandler = new TotpHandler(deps.speakeasy); - const usersDatabase = this.createUsersDatabase( - config, deps); - - return UserDataStoreFactory.create(config, globalLogger) - .then(function (userDataStore: UserDataStore) { - const regulator = new Regulator(userDataStore, config.regulation.max_retries, - config.regulation.find_time, config.regulation.ban_time); - - const variables: ServerVariables = { - authorizer: authorizer, - config: config, - usersDatabase: usersDatabase, - logger: requestLogger, - notifier: notifier, - regulator: regulator, - totpHandler: totpHandler, - u2f: deps.u2f, - userDataStore: userDataStore - }; - return BluebirdPromise.resolve(variables); - }); - } -} diff --git a/themes/triangles/server/src/lib/ServerVariablesMockBuilder.spec.ts b/themes/triangles/server/src/lib/ServerVariablesMockBuilder.spec.ts deleted file mode 100644 index 7874702a0..000000000 --- a/themes/triangles/server/src/lib/ServerVariablesMockBuilder.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { ServerVariables } from "./ServerVariables"; - -import { Configuration } from "./configuration/schema/Configuration"; -import { IUsersDatabaseStub } from "./authentication/backends/IUsersDatabaseStub.spec"; -import { AuthorizerStub } from "./authorization/AuthorizerStub.spec"; -import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec"; -import { NotifierStub } from "./notifiers/NotifierStub.spec"; -import { RegulatorStub } from "./regulation/RegulatorStub.spec"; -import { TotpHandlerStub } from "./authentication/totp/TotpHandlerStub.spec"; -import { UserDataStoreStub } from "./storage/UserDataStoreStub.spec"; -import { U2fHandlerStub } from "./authentication/u2f/U2fHandlerStub.spec"; - -export interface ServerVariablesMock { - authorizer: AuthorizerStub; - config: Configuration; - usersDatabase: IUsersDatabaseStub; - logger: RequestLoggerStub; - notifier: NotifierStub; - regulator: RegulatorStub; - totpHandler: TotpHandlerStub; - userDataStore: UserDataStoreStub; - u2f: U2fHandlerStub; -} - -export class ServerVariablesMockBuilder { - static build(enableLogging?: boolean): { variables: ServerVariables, mocks: ServerVariablesMock} { - const mocks: ServerVariablesMock = { - authorizer: new AuthorizerStub(), - config: { - access_control: {}, - totp: { - issuer: "authelia.com" - }, - authentication_backend: { - ldap: { - url: "ldap://ldap", - base_dn: "dc=example,dc=com", - user: "user", - password: "password", - mail_attribute: "mail", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn" - }, - }, - logs_level: "debug", - notifier: {}, - port: 8080, - regulation: { - ban_time: 50, - find_time: 50, - max_retries: 3 - }, - session: { - secret: "my_secret", - domain: "mydomain" - }, - storage: {} - }, - usersDatabase: new IUsersDatabaseStub(), - logger: new RequestLoggerStub(enableLogging), - notifier: new NotifierStub(), - regulator: new RegulatorStub(), - totpHandler: new TotpHandlerStub(), - userDataStore: new UserDataStoreStub(), - u2f: new U2fHandlerStub() - }; - const vars: ServerVariables = { - authorizer: mocks.authorizer, - config: mocks.config, - usersDatabase: mocks.usersDatabase, - logger: mocks.logger, - notifier: mocks.notifier, - regulator: mocks.regulator, - totpHandler: mocks.totpHandler, - userDataStore: mocks.userDataStore, - u2f: mocks.u2f - }; - - return { - variables: vars, - mocks: mocks - }; - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/Level.ts b/themes/triangles/server/src/lib/authentication/Level.ts deleted file mode 100644 index 57b6a2346..000000000 --- a/themes/triangles/server/src/lib/authentication/Level.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum Level { - NOT_AUTHENTICATED = 0, - ONE_FACTOR = 1, - TWO_FACTOR = 2 -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/GroupsAndEmails.ts b/themes/triangles/server/src/lib/authentication/backends/GroupsAndEmails.ts deleted file mode 100644 index 3434ba66f..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/GroupsAndEmails.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface GroupsAndEmails { - groups: string[]; - emails: string[]; -} diff --git a/themes/triangles/server/src/lib/authentication/backends/IUsersDatabase.ts b/themes/triangles/server/src/lib/authentication/backends/IUsersDatabase.ts deleted file mode 100644 index d7fa13b7a..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/IUsersDatabase.ts +++ /dev/null @@ -1,10 +0,0 @@ -import Bluebird = require("bluebird"); - -import { GroupsAndEmails } from "./GroupsAndEmails"; - -export interface IUsersDatabase { - checkUserPassword(username: string, password: string): Bluebird; - getEmails(username: string): Bluebird; - getGroups(username: string): Bluebird; - updatePassword(username: string, newPassword: string): Bluebird; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts b/themes/triangles/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts deleted file mode 100644 index 19341a5dd..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/IUsersDatabaseStub.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); - -import { IUsersDatabase } from "./IUsersDatabase"; -import { GroupsAndEmails } from "./GroupsAndEmails"; - -export class IUsersDatabaseStub implements IUsersDatabase { - checkUserPasswordStub: Sinon.SinonStub; - getEmailsStub: Sinon.SinonStub; - getGroupsStub: Sinon.SinonStub; - updatePasswordStub: Sinon.SinonStub; - - constructor() { - this.checkUserPasswordStub = Sinon.stub(); - this.getEmailsStub = Sinon.stub(); - this.getGroupsStub = Sinon.stub(); - this.updatePasswordStub = Sinon.stub(); - } - - checkUserPassword(username: string, password: string): Bluebird { - return this.checkUserPasswordStub(username, password); - } - - getEmails(username: string): Bluebird { - return this.getEmailsStub(username); - } - - getGroups(username: string): Bluebird { - return this.getGroupsStub(username); - } - - updatePassword(username: string, newPassword: string): Bluebird { - return this.updatePasswordStub(username, newPassword); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts b/themes/triangles/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts deleted file mode 100644 index a258a78f7..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/file/FileUsersDatabase.spec.ts +++ /dev/null @@ -1,224 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); -import Fs = require("fs"); -import Sinon = require("sinon"); -import Tmp = require("tmp"); - -import { FileUsersDatabase } from "./FileUsersDatabase"; -import { FileUsersDatabaseConfiguration } from "../../../configuration/schema/FileUsersDatabaseConfiguration"; -import { HashGenerator } from "../../../utils/HashGenerator"; - -const GOOD_DATABASE = ` -users: - john: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev - - harry: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - emails: harry.potter@authelia.com - groups: [] -`; - -const BAD_HASH = ` -users: - john: - password: "{CRYPT}$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev -`; - -const NO_PASSWORD_DATABASE = ` -users: - john: - email: john.doe@authelia.com - groups: - - admins - - dev -`; - -const NO_EMAIL_DATABASE = ` -users: - john: - password: "{CRYPT}$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - groups: - - admins - - dev -`; - -const SINGLE_USER_DATABASE = ` -users: - john: - password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" - email: john.doe@authelia.com - groups: - - admins - - dev -` - -function createTmpFileFrom(yaml: string) { - const tmpFileAsync = Bluebird.promisify(Tmp.file); - return tmpFileAsync() - .then((path: string) => { - Fs.writeFileSync(path, yaml, "utf-8"); - return Bluebird.resolve(path); - }); -} - -describe("authentication/backends/file/FileUsersDatabase", function() { - let configuration: FileUsersDatabaseConfiguration; - - describe("checkUserPassword", () => { - describe("good config", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then((groupsAndEmails) => { - Assert.deepEqual(groupsAndEmails.groups, ["admins", "dev"]); - Assert.deepEqual(groupsAndEmails.emails, ["john.doe@authelia.com"]); - }); - }); - - it("should fail when password is wrong", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "bad_password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("no_user", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("bad hash", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail when hash is wrong", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("no password", () => { - beforeEach(() => { - return createTmpFileFrom(NO_PASSWORD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.checkUserPassword("john", "password") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - }); - - describe("getEmails", () => { - describe("good config", () => { - beforeEach(() => { - return createTmpFileFrom(GOOD_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("john") - .then((emails) => { - Assert.deepEqual(emails, ["john.doe@authelia.com"]); - }); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("no_user") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - - describe("no email provided", () => { - beforeEach(() => { - return createTmpFileFrom(NO_EMAIL_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should fail", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.getEmails("john") - .then(() => Bluebird.reject(new Error("should not be here."))) - .catch((err) => { - return Bluebird.resolve(); - }); - }); - }); - }); - - describe("updatePassword", () => { - beforeEach(() => { - return createTmpFileFrom(SINGLE_USER_DATABASE) - .then((path: string) => configuration = { - path: path - }); - }); - - it("should succeed", () => { - const usersDatabase = new FileUsersDatabase(configuration); - const NEW_HASH = "{CRYPT}$6$rounds=500000$Qw6MhgADvLyYMEq9$ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const stub = Sinon.stub(HashGenerator, "ssha512").returns(Bluebird.resolve(NEW_HASH)); - return usersDatabase.updatePassword("john", "mypassword") - .then(() => { - const content = Fs.readFileSync(configuration.path, "utf-8"); - const matches = content.match(/password: '(.+)'/); - Assert.equal(matches[1], NEW_HASH); - }) - .finally(() => stub.restore()); - }); - - it("should fail when user does not exist", () => { - const usersDatabase = new FileUsersDatabase(configuration); - return usersDatabase.updatePassword("bad_user", "mypassword") - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => Bluebird.resolve()); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/file/FileUsersDatabase.ts b/themes/triangles/server/src/lib/authentication/backends/file/FileUsersDatabase.ts deleted file mode 100644 index d34dde21e..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/file/FileUsersDatabase.ts +++ /dev/null @@ -1,182 +0,0 @@ -import Bluebird = require("bluebird"); -import Fs = require("fs"); -import Yaml = require("yamljs"); - -import { FileUsersDatabaseConfiguration } - from "../../../configuration/schema/FileUsersDatabaseConfiguration"; -import { GroupsAndEmails } from "../GroupsAndEmails"; -import { IUsersDatabase } from "../IUsersDatabase"; -import { HashGenerator } from "../../../utils/HashGenerator"; -import { ReadWriteQueue } from "./ReadWriteQueue"; - -const loadAsync = Bluebird.promisify(Yaml.load); - -export class FileUsersDatabase implements IUsersDatabase { - private configuration: FileUsersDatabaseConfiguration; - private queue: ReadWriteQueue; - - constructor(configuration: FileUsersDatabaseConfiguration) { - this.configuration = configuration; - this.queue = new ReadWriteQueue(this.configuration.path); - } - - /** - * Read database from file. - * It enqueues the read task so that it is scheduled - * between other reads and writes. - */ - private readDatabase(): Bluebird { - return new Bluebird((resolve, reject) => { - this.queue.read((err: Error, data: string) => { - if (err) { - reject(err); - return; - } - resolve(data); - this.queue.next(); - }); - }) - .then((content) => { - const database = Yaml.parse(content); - if (!database) { - return Bluebird.reject(new Error("Unable to parse YAML file.")); - } - return Bluebird.resolve(database); - }); - } - - /** - * Checks the user exists in the database. - */ - private checkUserExists( - database: any, - username: string) - : Bluebird { - if (!(username in database.users)) { - return Bluebird.reject( - new Error(`User ${username} does not exist in database.`)); - } - return Bluebird.resolve(); - } - - /** - * Check the password of a given user. - */ - private checkPassword( - database: any, - username: string, - password: string) - : Bluebird { - const storedHash: string = database.users[username].password; - const matches = storedHash.match(/rounds=([0-9]+)\$([a-zA-z0-9]+)\$/); - if (!(matches && matches.length == 3)) { - return Bluebird.reject(new Error("Unable to detect the hash salt and rounds. " + - "Make sure the password is hashed with SSHA512.")); - } - - const rounds: number = parseInt(matches[1]); - const salt = matches[2]; - - return HashGenerator.ssha512(password, rounds, salt) - .then((hash: string) => { - if (hash !== storedHash) { - return Bluebird.reject(new Error("Wrong username/password.")); - } - return Bluebird.resolve(); - }); - } - - /** - * Retrieve email addresses of a given user. - */ - private retrieveEmails( - database: any, - username: string) - : Bluebird { - if (!("email" in database.users[username])) { - return Bluebird.reject( - new Error(`User ${username} has no email address.`)); - } - return Bluebird.resolve( - [database.users[username].email]); - } - - private retrieveGroups( - database: any, - username: string) - : Bluebird { - if (!("groups" in database.users[username])) { - return Bluebird.resolve([]); - } - return Bluebird.resolve( - database.users[username].groups); - } - - private replacePassword( - database: any, - username: string, - newPassword: string) - : Bluebird { - const that = this; - return HashGenerator.ssha512(newPassword) - .then((hash) => { - database.users[username].password = hash; - const str = Yaml.stringify(database, 4, 2); - return Bluebird.resolve(str); - }) - .then((content: string) => { - return new Bluebird((resolve, reject) => { - that.queue.write(content, (err) => { - if (err) { - return reject(err); - } - resolve(); - that.queue.next(); - }); - }); - }); - } - - checkUserPassword( - username: string, - password: string) - : Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.checkPassword(database, username, password)) - .then(() => { - return Bluebird.join( - this.retrieveEmails(database, username), - this.retrieveGroups(database, username) - ).spread((emails: string[], groups: string[]) => { - return { emails: emails, groups: groups }; - }); - }); - }); - } - - getEmails(username: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.retrieveEmails(database, username)); - }); - } - - getGroups(username: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.retrieveGroups(database, username)); - }); - } - - updatePassword(username: string, newPassword: string): Bluebird { - return this.readDatabase() - .then((database) => { - return this.checkUserExists(database, username) - .then(() => this.replacePassword(database, username, newPassword)); - }); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/file/ReadWriteQueue.ts b/themes/triangles/server/src/lib/authentication/backends/file/ReadWriteQueue.ts deleted file mode 100644 index 957ddaec5..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/file/ReadWriteQueue.ts +++ /dev/null @@ -1,60 +0,0 @@ -import Fs = require("fs"); - -type Callback = (err: Error, data?: string) => void; -type ContentAndCallback = [string, Callback] | [string, string, Callback]; - -/** - * WriteQueue is a queue synchronizing writes to a file. - * - * Example of use: - * - * queue.add(mycontent, (err) => { - * // do whatever you want here. - * queue.next(); - * }) - */ -export class ReadWriteQueue { - private filePath: string; - private queue: ContentAndCallback[]; - - constructor (filePath: string) { - this.queue = []; - this.filePath = filePath; - } - - next () { - if (this.queue.length === 0) - return; - - const task = this.queue[0]; - - if (task[0] == "write") { - Fs.writeFile(this.filePath, task[1], "utf-8", (err) => { - this.queue.shift(); - const cb = task[2] as Callback; - cb(err); - }); - } - else if (task[0] == "read") { - Fs.readFile(this.filePath, { encoding: "utf-8"} , (err, data) => { - this.queue.shift(); - const cb = task[1] as Callback; - cb(err, data); - }); - } - } - - write (content: string, cb: Callback) { - this.queue.push(["write", content, cb]); - if (this.queue.length === 1) { - this.next(); - } - } - - read (cb: Callback) { - this.queue.push(["read", cb]); - if (this.queue.length === 1) { - this.next(); - } - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/ISession.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/ISession.ts deleted file mode 100644 index da2c74433..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/ISession.ts +++ /dev/null @@ -1,12 +0,0 @@ - -import BluebirdPromise = require("bluebird"); - -export interface ISession { - open(): BluebirdPromise; - close(): BluebirdPromise; - - searchUserDn(username: string): BluebirdPromise; - searchEmails(username: string): BluebirdPromise; - searchGroups(username: string): BluebirdPromise; - modifyPassword(username: string, newPassword: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/ISessionFactory.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/ISessionFactory.ts deleted file mode 100644 index 014d1eea5..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/ISessionFactory.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import { ISession } from "./ISession"; - -export interface ISessionFactory { - create(userDN: string, password: string): ISession; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts deleted file mode 100644 index f4a6e6306..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.spec.ts +++ /dev/null @@ -1,386 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); - -import { LdapUsersDatabase } from "./LdapUsersDatabase"; - -import { SessionFactoryStub } from "./SessionFactoryStub.spec"; -import { SessionStub } from "./SessionStub.spec"; - -const ADMIN_USER_DN = "cn=admin,dc=example,dc=com"; -const ADMIN_PASSWORD = "password"; - -describe("ldap/connector/LdapUsersDatabase", function() { - let sessionFactory: SessionFactoryStub; - let usersDatabase: LdapUsersDatabase; - - const USERNAME = "user"; - const PASSWORD = "pass"; - const NEW_PASSWORD = "pass2"; - - const LDAP_CONFIG = { - url: "http://localhost:324", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={0}", - mail_attribute: "mail", - group_name_attribute: "cn", - user: ADMIN_USER_DN, - password: ADMIN_PASSWORD - }; - - beforeEach(function() { - sessionFactory = new SessionFactoryStub(); - usersDatabase = new LdapUsersDatabase(sessionFactory, LDAP_CONFIG); - }) - - describe("checkUserPassword", function() { - it("should return groups and emails when user/password matches", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME).returns(Bluebird.resolve(emails)); - adminSession.searchGroupsStub.withArgs(USERNAME).returns(Bluebird.resolve(groups)); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => { - Assert.deepEqual(groupsAndEmails.groups, groups); - Assert.deepEqual(groupsAndEmails.emails, emails); - }) - }); - - it("should fail when username/password is wrong", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - - userSession.openStub.returns(Bluebird.reject(new Error("Failed binding"))); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when admin binding fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.reject(new Error("Failed binding"))); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.notCalled); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when search for user dn fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.reject(new Error("Failed searching user dn"))); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.notCalled); - Assert(adminSession.closeStub.called); - return Bluebird.resolve(); - }) - }); - - it("should fail when groups retrieval fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME) - .returns(Bluebird.resolve(emails)); - adminSession.searchGroupsStub.withArgs(USERNAME) - .returns(Bluebird.reject(new Error("Failed retrieving groups"))); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - }) - }); - - it("should fail when emails retrieval fails", function() { - const USER_DN = `cn=${USERNAME},dc=example,dc=com`; - const emails = ["email1", "email2"]; - const groups = ["group1", "group2"]; - - const adminSession = new SessionStub(); - const userSession = new SessionStub(); - - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession); - sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession); - - adminSession.openStub.returns(Bluebird.resolve()); - adminSession.closeStub.returns(Bluebird.resolve()); - adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN)); - adminSession.searchEmailsStub.withArgs(USERNAME) - .returns(Bluebird.reject(new Error("Emails retrieval failed"))); - adminSession.searchGroupsStub.withArgs(USERNAME) - .returns(Bluebird.resolve(groups)); - - userSession.openStub.returns(Bluebird.resolve()); - userSession.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.checkUserPassword(USERNAME, PASSWORD) - .then((groupsAndEmails) => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(userSession.closeStub.called); - Assert(adminSession.closeStub.called); - }) - }); - }); - - describe("getEmails", function() { - it("should succefully retrieves email", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.resolve(emails)); - - return usersDatabase.getEmails(USERNAME) - .then((foundEmails) => { - Assert(session.closeStub.called); - Assert.deepEqual(foundEmails, emails); - }) - }); - - it("should fail when binding fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbinding fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.resolve(emails)); - session.closeStub.returns(Bluebird.reject(new Error("Unbinding failed"))); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when search fails", () => { - const emails = ["email1", "email2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchEmailsStub.returns(Bluebird.reject(new Error("Search failed"))); - session.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.getEmails(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - }); - - - describe("getGroups", function() { - it("should succefully retrieves groups", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.resolve(groups)); - - return usersDatabase.getGroups(USERNAME) - .then((foundGroups) => { - Assert(session.closeStub.called); - Assert.deepEqual(foundGroups, groups); - }) - }); - - it("should fail when binding fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbinding fails", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.resolve(groups)); - session.closeStub.returns(Bluebird.reject(new Error("Unbinding failed"))); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when search fails", () => { - const groups = ["group1", "group2"]; - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.searchGroupsStub.returns(Bluebird.reject(new Error("Search failed"))); - session.closeStub.returns(Bluebird.resolve()); - - return usersDatabase.getGroups(USERNAME) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch((err) => { - Assert(session.closeStub.called); - }) - }); - }); - - - describe("updatePassword", function() { - it("should successfully update password", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => { - Assert(session.modifyPasswordStub.calledWith(USERNAME, NEW_PASSWORD)); - Assert(session.closeStub.called); - }) - }); - - it("should fail when binding fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.reject(new Error("Binding failed"))); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when update fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.reject(new Error("Update failed"))); - session.modifyPasswordStub.returns(Bluebird.resolve()); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - - it("should fail when unbind fails", () => { - const session = new SessionStub(); - sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session); - - session.openStub.returns(Bluebird.resolve()); - session.closeStub.returns(Bluebird.resolve()); - session.modifyPasswordStub.returns(Bluebird.reject(new Error("Unbind failed"))); - - return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD) - .then(() => Bluebird.reject(new Error("should not be here"))) - .catch(() => { - Assert(session.closeStub.called); - }) - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts deleted file mode 100644 index edda62ec6..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/LdapUsersDatabase.ts +++ /dev/null @@ -1,107 +0,0 @@ -import Bluebird = require("bluebird"); -import { IUsersDatabase } from "../IUsersDatabase"; -import { ISessionFactory } from "./ISessionFactory"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { ISession } from "./ISession"; -import { GroupsAndEmails } from "../GroupsAndEmails"; -import Exceptions = require("../../../Exceptions"); - -type SessionCallback = (session: ISession) => Bluebird; - -export class LdapUsersDatabase implements IUsersDatabase { - private sessionFactory: ISessionFactory; - private configuration: LdapConfiguration; - - constructor( - sessionFactory: ISessionFactory, - configuration: LdapConfiguration) { - this.sessionFactory = sessionFactory; - this.configuration = configuration; - } - - private withSession( - username: string, - password: string, - cb: SessionCallback): Bluebird { - const session = this.sessionFactory.create(username, password); - return session.open() - .then(() => cb(session)) - .finally(() => session.close()); - } - - checkUserPassword(username: string, password: string): Bluebird { - const that = this; - function verifyUserPassword(userDN: string) { - return that.withSession( - userDN, - password, - (session) => Bluebird.resolve() - ); - } - - function getInfo(session: ISession) { - return Bluebird.join( - session.searchGroups(username), - session.searchEmails(username) - ) - .spread((groups: string[], emails: string[]) => { - return { groups: groups, emails: emails }; - }); - } - - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchUserDn(username) - .then(verifyUserPassword) - .then(() => getInfo(session)); - }) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError(err.message))); - } - - getEmails(username: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchEmails(username); - } - ) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError("Failed during email retrieval: " + err.message)) - ); - } - - getGroups(username: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.searchGroups(username); - } - ) - .catch((err) => - Bluebird.reject(new Exceptions.LdapError("Failed during email retrieval: " + err.message)) - ); - } - - updatePassword(username: string, newPassword: string): Bluebird { - const that = this; - return that.withSession( - that.configuration.user, - that.configuration.password, - (session) => { - return session.modifyPassword(username, newPassword); - } - ) - .catch(function (err: Error) { - return Bluebird.reject( - new Exceptions.LdapError( - "Error while updating password: " + err.message)); - }); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts deleted file mode 100644 index 9dedfcb7c..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/SafeSession.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { SessionStub } from "./SessionStub.spec"; -import { SafeSession } from "./SafeSession"; - -describe("ldap/SanitizedClient", function () { - let client: SafeSession; - - beforeEach(function () { - const clientStub = new SessionStub(); - clientStub.searchUserDnStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.searchGroupsStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.searchEmailsStub.onCall(0).returns(BluebirdPromise.resolve()); - clientStub.modifyPasswordStub.onCall(0).returns(BluebirdPromise.resolve()); - client = new SafeSession(clientStub); - }); - - describe("special chars are used", function () { - it("should fail when special chars are used in searchUserDn", function () { - // potential ldap injection"; - return client.searchUserDn("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in searchGroups", function () { - // potential ldap injection"; - return client.searchGroups("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in searchEmails", function () { - // potential ldap injection"; - return client.searchEmails("cn=dummy_user,ou=groupgs") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail when special chars are used in modifyPassword", function () { - // potential ldap injection"; - return client.modifyPassword("cn=dummy_user,ou=groupgs", "abc") - .then(function () { - return BluebirdPromise.reject(new Error("Should not be here.")); - }, function () { - return BluebirdPromise.resolve(); - }); - }); - }); - - describe("no special chars are used", function() { - it("should succeed when no special chars are used in searchUserDn", function () { - return client.searchUserDn("dummy_user"); - }); - - it("should succeed when no special chars are used in searchGroups", function () { - return client.searchGroups("dummy_user"); - }); - - it("should succeed when no special chars are used in searchEmails", function () { - return client.searchEmails("dummy_user"); - }); - - it("should succeed when no special chars are used in modifyPassword", function () { - return client.modifyPassword("dummy_user", "abc"); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/SafeSession.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/SafeSession.ts deleted file mode 100644 index 572209066..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/SafeSession.ts +++ /dev/null @@ -1,62 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ISession } from "./ISession"; -import { Sanitizer } from "./Sanitizer"; - -const SPECIAL_CHAR_USED_MESSAGE = "Special character used in LDAP query."; - - -export class SafeSession implements ISession { - private sesion: ISession; - - constructor(sesion: ISession) { - this.sesion = sesion; - } - - open(): BluebirdPromise { - return this.sesion.open(); - } - - close(): BluebirdPromise { - return this.sesion.close(); - } - - searchGroups(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchGroups(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - searchUserDn(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchUserDn(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - searchEmails(username: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.searchEmails(sanitizedUsername); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } - - modifyPassword(username: string, newPassword: string): BluebirdPromise { - try { - const sanitizedUsername = Sanitizer.sanitize(username); - return this.sesion.modifyPassword(sanitizedUsername, newPassword); - } - catch (e) { - return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE)); - } - } -} diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts deleted file mode 100644 index 9dd33fed4..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/Sanitizer.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import Assert = require("assert"); -import { Sanitizer } from "./Sanitizer"; - -describe("ldap/InputsSanitizer", function () { - it("should fail when special characters are used", function () { - Assert.throws(() => { Sanitizer.sanitize("ab,c"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a\\bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a'bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a#bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a+bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a { Sanitizer.sanitize("a>bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a;bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a\"bc"); }, Error); - Assert.throws(() => { Sanitizer.sanitize("a=bc"); }, Error); - }); - - it("should return original string", function () { - Assert.equal(Sanitizer.sanitize("abcdef"), "abcdef"); - }); - - it("should trim", function () { - Assert.throws(() => { Sanitizer.sanitize(" abc "); }, Error); - }); -}); diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/Sanitizer.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/Sanitizer.ts deleted file mode 100644 index be74132ad..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/Sanitizer.ts +++ /dev/null @@ -1,25 +0,0 @@ - -// returns true for 1 or more matches, where 'a' is an array and 'b' is a search string or an array of multiple search strings -function contains(a: string, character: string) { - // string match - return a.indexOf(character) > -1; -} - -function containsOneOf(s: string, characters: string[]) { - return characters - .map((character: string) => { return contains(s, character); }) - .reduce((acc: boolean, current: boolean) => { return acc || current; }, false); -} - -export class Sanitizer { - static sanitize(input: string): string { - const forbiddenChars = [",", "\\", "'", "#", "+", "<", ">", ";", "\"", "="]; - if (containsOneOf(input, forbiddenChars)) - throw new Error("Input containing unsafe characters."); - - if (input != input.trim()) - throw new Error("Input has unexpected spaces."); - - return input; - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/Session.spec.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/Session.spec.ts deleted file mode 100644 index d55f6a805..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/Session.spec.ts +++ /dev/null @@ -1,127 +0,0 @@ - -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Session } from "./Session"; -import { ConnectorFactoryStub } from "./connector/ConnectorFactoryStub.spec"; -import { ConnectorStub } from "./connector/ConnectorStub.spec"; - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import Winston = require("winston"); - -describe("ldap/Session", function () { - const USERNAME = "username"; - const ADMIN_USER_DN = "cn=admin,dc=example,dc=com"; - const ADMIN_PASSWORD = "password"; - - it("should replace {0} by username when searching for groups in LDAP", function () { - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member=cn={0},ou=users,dc=example,dc=com", - group_name_attribute: "cn", - mail_attribute: "mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const connectorStub = new ConnectorStub(); - connectorStub.searchAsyncStub.returns(BluebirdPromise.resolve([{ - cn: "group1" - }])); - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, connectorStub, Winston); - - return client.searchGroups("user1") - .then(function () { - Assert.equal(connectorStub.searchAsyncStub.getCall(0).args[1].filter, - "member=cn=user1,ou=users,dc=example,dc=com"); - }); - }); - - it("should replace {dn} by user DN when searching for groups in LDAP", function () { - const USER_DN = "cn=user1,ou=users,dc=example,dc=com"; - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn", - mail_attribute: "mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const ldapClient = new ConnectorStub(); - - // Retrieve user DN - ldapClient.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: "cn=user1" - }).returns(BluebirdPromise.resolve([{ - dn: USER_DN - }])); - - // Retrieve groups - ldapClient.searchAsyncStub.withArgs("ou=groups,dc=example,dc=com", { - scope: "sub", - attributes: ["cn"], - filter: "member=" + USER_DN - }).returns(BluebirdPromise.resolve([{ - cn: "group1" - }])); - - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, ldapClient, Winston); - - return client.searchGroups("user1") - .then(function (groups: string[]) { - Assert.deepEqual(groups, ["group1"]); - }); - }); - - it("should retrieve mail from custom attribute", function () { - const USER_DN = "cn=user1,ou=users,dc=example,dc=com"; - const options: LdapConfiguration = { - url: "ldap://ldap", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "cn={0}", - groups_filter: "member={dn}", - group_name_attribute: "cn", - mail_attribute: "custom_mail", - user: "cn=admin,dc=example,dc=com", - password: "password" - }; - const connector = new ConnectorStub(); - // Retrieve user DN - connector.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: "cn=user1" - }).returns(BluebirdPromise.resolve([{ - dn: USER_DN - }])); - - // Retrieve email - connector.searchAsyncStub.withArgs("cn=user1,ou=users,dc=example,dc=com", { - scope: "base", - sizeLimit: 1, - attributes: ["custom_mail"], - }).returns(BluebirdPromise.resolve([{ - custom_mail: "user1@example.com" - }])); - - const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, connector, Winston); - - return client.searchEmails("user1") - .then(function (emails: string[]) { - Assert.deepEqual(emails, ["user1@example.com"]); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/Session.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/Session.ts deleted file mode 100644 index e0284b3c4..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/Session.ts +++ /dev/null @@ -1,156 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import exceptions = require("../../../Exceptions"); -import { EventEmitter } from "events"; -import { ISession } from "./ISession"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Winston } from "../../../../../types/Dependencies"; -import Util = require("util"); -import { HashGenerator } from "../../../utils/HashGenerator"; -import { IConnector } from "./connector/IConnector"; - -export class Session implements ISession { - private userDN: string; - private password: string; - private connector: IConnector; - private logger: Winston; - private options: LdapConfiguration; - - private groupsSearchBase: string; - private usersSearchBase: string; - - constructor(userDN: string, password: string, options: LdapConfiguration, - connector: IConnector, logger: Winston) { - this.options = options; - this.logger = logger; - this.userDN = userDN; - this.password = password; - this.connector = connector; - - this.groupsSearchBase = (this.options.additional_groups_dn) - ? Util.format("%s,%s", this.options.additional_groups_dn, this.options.base_dn) - : this.options.base_dn; - - this.usersSearchBase = (this.options.additional_users_dn) - ? Util.format("%s,%s", this.options.additional_users_dn, this.options.base_dn) - : this.options.base_dn; - } - - open(): BluebirdPromise { - this.logger.debug("LDAP: Bind user '%s'", this.userDN); - return this.connector.bindAsync(this.userDN, this.password) - .error(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapBindError(err.message)); - }); - } - - close(): BluebirdPromise { - this.logger.debug("LDAP: Unbind user '%s'", this.userDN); - return this.connector.unbindAsync() - .error(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapBindError(err.message)); - }); - } - - private createGroupsFilter(userGroupsFilter: string, username: string): BluebirdPromise { - if (userGroupsFilter.indexOf("{0}") > 0) { - return BluebirdPromise.resolve(userGroupsFilter.replace("{0}", username)); - } - else if (userGroupsFilter.indexOf("{dn}") > 0) { - return this.searchUserDn(username) - .then(function (userDN: string) { - return BluebirdPromise.resolve(userGroupsFilter.replace("{dn}", userDN)); - }); - } - return BluebirdPromise.resolve(userGroupsFilter); - } - - searchGroups(username: string): BluebirdPromise { - const that = this; - return this.createGroupsFilter(this.options.groups_filter, username) - .then(function (groupsFilter: string) { - that.logger.debug("Computed groups filter is %s", groupsFilter); - const query = { - scope: "sub", - attributes: [that.options.group_name_attribute], - filter: groupsFilter - }; - return that.connector.searchAsync(that.groupsSearchBase, query); - }) - .then(function (docs: { cn: string }[]) { - const groups = docs.map((doc: any) => { return doc.cn; }); - that.logger.debug("LDAP: groups of user %s are [%s]", username, groups.join(",")); - return BluebirdPromise.resolve(groups); - }); - } - - searchUserDn(username: string): BluebirdPromise { - const that = this; - const filter = this.options.users_filter.replace("{0}", username); - this.logger.debug("Computed users filter is %s", filter); - const query = { - scope: "sub", - sizeLimit: 1, - attributes: ["dn"], - filter: filter - }; - - that.logger.debug("LDAP: searching for user dn of %s", username); - return that.connector.searchAsync(this.usersSearchBase, query) - .then(function (users: { dn: string }[]) { - if (users.length > 0) { - that.logger.debug("LDAP: retrieved user dn is %s", users[0].dn); - return BluebirdPromise.resolve(users[0].dn); - } - return BluebirdPromise.reject(new Error( - Util.format("No user DN found for user '%s'", username))); - }); - } - - searchEmails(username: string): BluebirdPromise { - const that = this; - const query = { - scope: "base", - sizeLimit: 1, - attributes: [this.options.mail_attribute] - }; - - return this.searchUserDn(username) - .then(function (userDN) { - return that.connector.searchAsync(userDN, query); - }) - .then(function (docs: { [mail_attribute: string]: string }[]) { - const emails: string[] = docs - .filter((d) => { return typeof d[that.options.mail_attribute] === "string"; }) - .map((d) => { return d[that.options.mail_attribute]; }); - that.logger.debug("LDAP: emails of user '%s' are %s", username, emails); - return BluebirdPromise.resolve(emails); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject(new exceptions.LdapError("Error while searching emails. " + err.stack)); - }); - } - - modifyPassword(username: string, newPassword: string): BluebirdPromise { - const that = this; - this.logger.debug("LDAP: update password of user '%s'", username); - return this.searchUserDn(username) - .then(function (userDN: string) { - return BluebirdPromise.join( - HashGenerator.ssha512(newPassword), - BluebirdPromise.resolve(userDN)); - }) - .then(function (res: string[]) { - const change = { - operation: "replace", - modification: { - userPassword: res[0] - } - }; - that.logger.debug("Password new='%s'", change.modification.userPassword); - return that.connector.modifyAsync(res[1], change); - }) - .then(function () { - return that.connector.unbindAsync(); - }); - } -} diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/SessionFactory.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/SessionFactory.ts deleted file mode 100644 index 0b6c4bff3..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/SessionFactory.ts +++ /dev/null @@ -1,37 +0,0 @@ -import Ldapjs = require("ldapjs"); -import Winston = require("winston"); - -import { IConnectorFactory } from "./connector/IConnectorFactory"; -import { ISessionFactory } from "./ISessionFactory"; -import { ISession } from "./ISession"; -import { LdapConfiguration } from "../../../configuration/schema/LdapConfiguration"; -import { Session } from "./Session"; -import { SafeSession } from "./SafeSession"; - - -export class SessionFactory implements ISessionFactory { - private config: LdapConfiguration; - private connectorFactory: IConnectorFactory; - private logger: typeof Winston; - - constructor(ldapConfiguration: LdapConfiguration, - connectorFactory: IConnectorFactory, - logger: typeof Winston) { - this.config = ldapConfiguration; - this.connectorFactory = connectorFactory; - this.logger = logger; - } - - create(userDN: string, password: string): ISession { - const connector = this.connectorFactory.create(); - return new SafeSession( - new Session( - userDN, - password, - this.config, - connector, - this.logger - ) - ); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts deleted file mode 100644 index face3930d..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/SessionFactoryStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); - -import { ISession } from "./ISession"; -import { ISessionFactory } from "./ISessionFactory"; - -export class SessionFactoryStub implements ISessionFactory { - createStub: Sinon.SinonStub; - - constructor() { - this.createStub = Sinon.stub(); - } - - create(userDN: string, password: string): ISession { - return this.createStub(userDN, password); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts deleted file mode 100644 index 5faf2ba18..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/SessionStub.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); - -import { ISession } from "./ISession"; - -export class SessionStub implements ISession { - openStub: Sinon.SinonStub; - closeStub: Sinon.SinonStub; - searchUserDnStub: Sinon.SinonStub; - searchEmailsStub: Sinon.SinonStub; - searchGroupsStub: Sinon.SinonStub; - modifyPasswordStub: Sinon.SinonStub; - - constructor() { - this.openStub = Sinon.stub(); - this.closeStub = Sinon.stub(); - this.searchUserDnStub = Sinon.stub(); - this.searchEmailsStub = Sinon.stub(); - this.searchGroupsStub = Sinon.stub(); - this.modifyPasswordStub = Sinon.stub(); - } - - open(): Bluebird { - return this.openStub(); - } - - close(): Bluebird { - return this.closeStub(); - } - - searchUserDn(username: string): Bluebird { - return this.searchUserDnStub(username); - } - - searchEmails(username: string): Bluebird { - return this.searchEmailsStub(username); - } - - searchGroups(username: string): Bluebird { - return this.searchGroupsStub(username); - } - - modifyPassword(username: string, newPassword: string): Bluebird { - return this.modifyPasswordStub(username, newPassword); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/connector/Connector.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/connector/Connector.ts deleted file mode 100644 index 2542ea7f9..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/connector/Connector.ts +++ /dev/null @@ -1,69 +0,0 @@ -import LdapJs = require("ldapjs"); -import EventEmitter = require("events"); -import Bluebird = require("bluebird"); -import { IConnector } from "./IConnector"; -import Exceptions = require("../../../../Exceptions"); - -interface SearchEntry { - object: any; -} - -export interface ClientAsync { - on(event: string, callback: (data?: any) => void): void; - bindAsync(username: string, password: string): Bluebird; - unbindAsync(): Bluebird; - searchAsync(base: string, query: LdapJs.SearchOptions): Bluebird; - modifyAsync(userdn: string, change: LdapJs.Change): Bluebird; -} - -export class Connector implements IConnector { - private client: ClientAsync; - - constructor(url: string, ldapjs: typeof LdapJs) { - const ldapClient = ldapjs.createClient({ - url: url, - reconnect: true - }); - - /*const clientLogger = (ldapClient as any).log; - if (clientLogger) { - clientLogger.level("trace"); - }*/ - - this.client = Bluebird.promisifyAll(ldapClient) as any; - } - - bindAsync(username: string, password: string): Bluebird { - return this.client.bindAsync(username, password); - } - - unbindAsync(): Bluebird { - return this.client.unbindAsync(); - } - - searchAsync(base: string, query: any): Bluebird { - const that = this; - return this.client.searchAsync(base, query) - .then(function (res: EventEmitter) { - const doc: SearchEntry[] = []; - return new Bluebird((resolve, reject) => { - res.on("searchEntry", function (entry: SearchEntry) { - doc.push(entry.object); - }); - res.on("error", function (err: Error) { - reject(new Exceptions.LdapSearchError(err.message)); - }); - res.on("end", function () { - resolve(doc); - }); - }); - }) - .catch(function (err: Error) { - return Bluebird.reject(new Exceptions.LdapSearchError(err.message)); - }); - } - - modifyAsync(dn: string, changeRequest: any): Bluebird { - return this.client.modifyAsync(dn, changeRequest); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts deleted file mode 100644 index 61fef07a4..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/connector/ConnectorFactory.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IConnector } from "./IConnector"; -import { Connector } from "./Connector"; -import { LdapConfiguration } from "../../../../configuration/schema/LdapConfiguration"; -import { Ldapjs } from "Dependencies"; - -export class ConnectorFactory { - private configuration: LdapConfiguration; - private ldapjs: Ldapjs; - - constructor(configuration: LdapConfiguration, ldapjs: Ldapjs) { - this.configuration = configuration; - this.ldapjs = ldapjs; - } - - create(): IConnector { - return new Connector(this.configuration.url, this.ldapjs); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts deleted file mode 100644 index d11fa6386..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/connector/ConnectorFactoryStub.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); - -import { IConnectorFactory } from "./IConnectorFactory"; -import { IConnector } from "./IConnector"; - -export class ConnectorFactoryStub implements IConnectorFactory { - createStub: Sinon.SinonStub; - - constructor() { - this.createStub = Sinon.stub(); - } - - create(): IConnector { - return this.createStub(); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts deleted file mode 100644 index 0b78225bf..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/connector/ConnectorStub.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); - -import { IConnector } from "./IConnector"; - -export class ConnectorStub implements IConnector { - bindAsyncStub: Sinon.SinonStub; - unbindAsyncStub: Sinon.SinonStub; - searchAsyncStub: Sinon.SinonStub; - modifyAsyncStub: Sinon.SinonStub; - - constructor() { - this.bindAsyncStub = Sinon.stub(); - this.unbindAsyncStub = Sinon.stub(); - this.searchAsyncStub = Sinon.stub(); - this.modifyAsyncStub = Sinon.stub(); - } - - bindAsync(username: string, password: string): BluebirdPromise { - return this.bindAsyncStub(username, password); - } - - unbindAsync(): BluebirdPromise { - return this.unbindAsyncStub(); - } - - searchAsync(base: string, query: any): BluebirdPromise { - return this.searchAsyncStub(base, query); - } - - modifyAsync(dn: string, changeRequest: any): BluebirdPromise { - return this.modifyAsyncStub(dn, changeRequest); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/connector/IConnector.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/connector/IConnector.ts deleted file mode 100644 index 1e63ab193..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/connector/IConnector.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Bluebird = require("bluebird"); -import EventEmitter = require("events"); - -export interface IConnector { - bindAsync(username: string, password: string): Bluebird; - unbindAsync(): Bluebird; - searchAsync(base: string, query: any): Bluebird; - modifyAsync(dn: string, changeRequest: any): Bluebird; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts b/themes/triangles/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts deleted file mode 100644 index f9ed65eff..000000000 --- a/themes/triangles/server/src/lib/authentication/backends/ldap/connector/IConnectorFactory.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { IConnector } from "./IConnector"; - -export interface IConnectorFactory { - create(): IConnector; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/totp/ITotpHandler.ts b/themes/triangles/server/src/lib/authentication/totp/ITotpHandler.ts deleted file mode 100644 index d600d31e4..000000000 --- a/themes/triangles/server/src/lib/authentication/totp/ITotpHandler.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { TOTPSecret } from "../../../../types/TOTPSecret"; - -export interface ITotpHandler { - generate(label: string, issuer: string): TOTPSecret; - validate(token: string, secret: string): boolean; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/totp/TotpHandler.spec.ts b/themes/triangles/server/src/lib/authentication/totp/TotpHandler.spec.ts deleted file mode 100644 index 67cffa638..000000000 --- a/themes/triangles/server/src/lib/authentication/totp/TotpHandler.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import { TotpHandler } from "./TotpHandler"; -import Sinon = require("sinon"); -import Speakeasy = require("speakeasy"); -import Assert = require("assert"); - -describe("authentication/totp/TotpHandler", function() { - let totpValidator: TotpHandler; - let validateStub: Sinon.SinonStub; - - beforeEach(() => { - validateStub = Sinon.stub(Speakeasy.totp, "verify"); - totpValidator = new TotpHandler(Speakeasy); - }); - - afterEach(function() { - validateStub.restore(); - }); - - it("should validate the TOTP token", function() { - const totp_secret = "NBD2ZV64R9UV1O7K"; - const token = "token"; - validateStub.withArgs({ - secret: totp_secret, - token: token, - encoding: "base32", - window: 1 - }).returns(true); - Assert(totpValidator.validate(token, totp_secret)); - }); - - it("should not validate a wrong TOTP token", function() { - const totp_secret = "NBD2ZV64R9UV1O7K"; - const token = "wrong token"; - validateStub.returns(false); - Assert(!totpValidator.validate(token, totp_secret)); - }); -}); - diff --git a/themes/triangles/server/src/lib/authentication/totp/TotpHandler.ts b/themes/triangles/server/src/lib/authentication/totp/TotpHandler.ts deleted file mode 100644 index dfab502a7..000000000 --- a/themes/triangles/server/src/lib/authentication/totp/TotpHandler.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ITotpHandler } from "./ITotpHandler"; -import { TOTPSecret } from "../../../../types/TOTPSecret"; -import Speakeasy = require("speakeasy"); - -const TOTP_ENCODING = "base32"; -const WINDOW: number = 1; - -export class TotpHandler implements ITotpHandler { - private speakeasy: typeof Speakeasy; - - constructor(speakeasy: typeof Speakeasy) { - this.speakeasy = speakeasy; - } - - generate(label: string, issuer: string): TOTPSecret { - const secret = this.speakeasy.generateSecret({ - otpauth_url: false - }) as TOTPSecret; - - secret.otpauth_url = this.speakeasy.otpauthURL({ - secret: secret.ascii, - label: label, - issuer: issuer - }); - return secret; - } - - validate(token: string, secret: string): boolean { - return this.speakeasy.totp.verify({ - secret: secret, - encoding: TOTP_ENCODING, - token: token, - window: WINDOW - }); - } -} diff --git a/themes/triangles/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts b/themes/triangles/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts deleted file mode 100644 index ea93330d9..000000000 --- a/themes/triangles/server/src/lib/authentication/totp/TotpHandlerStub.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import { ITotpHandler } from "./ITotpHandler"; -import { TOTPSecret } from "../../../../types/TOTPSecret"; - -export class TotpHandlerStub implements ITotpHandler { - generateStub: Sinon.SinonStub; - validateStub: Sinon.SinonStub; - - constructor() { - this.generateStub = Sinon.stub(); - this.validateStub = Sinon.stub(); - } - - generate(label: string, issuer: string): TOTPSecret { - return this.generateStub(label, issuer); - } - - validate(token: string, secret: string): boolean { - return this.validateStub(token, secret); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/u2f/IU2fHandler.ts b/themes/triangles/server/src/lib/authentication/u2f/IU2fHandler.ts deleted file mode 100644 index b9b7d6f26..000000000 --- a/themes/triangles/server/src/lib/authentication/u2f/IU2fHandler.ts +++ /dev/null @@ -1,9 +0,0 @@ -import U2f = require("u2f"); - -export interface IU2fHandler { - request(appId: string, keyHandle?: string): U2f.Request; - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error; - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authentication/u2f/U2fHandler.ts b/themes/triangles/server/src/lib/authentication/u2f/U2fHandler.ts deleted file mode 100644 index bf3891e5b..000000000 --- a/themes/triangles/server/src/lib/authentication/u2f/U2fHandler.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { IU2fHandler } from "./IU2fHandler"; -import U2f = require("u2f"); - -export class U2fHandler implements IU2fHandler { - private u2f: typeof U2f; - - constructor(u2f: typeof U2f) { - this.u2f = u2f; - } - - request(appId: string, keyHandle?: string): U2f.Request { - return this.u2f.request(appId, keyHandle); - } - - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error { - return this.u2f.checkRegistration(registrationRequest, registrationResponse); - } - - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error { - return this.u2f.checkSignature(signatureRequest, signatureResponse, publicKey); - } -} diff --git a/themes/triangles/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts b/themes/triangles/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts deleted file mode 100644 index 135d7eb06..000000000 --- a/themes/triangles/server/src/lib/authentication/u2f/U2fHandlerStub.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import U2f = require("u2f"); -import { IU2fHandler } from "./IU2fHandler"; - - -export class U2fHandlerStub implements IU2fHandler { - requestStub: Sinon.SinonStub; - checkRegistrationStub: Sinon.SinonStub; - checkSignatureStub: Sinon.SinonStub; - - constructor() { - this.requestStub = Sinon.stub(); - this.checkRegistrationStub = Sinon.stub(); - this.checkSignatureStub = Sinon.stub(); - } - - request(appId: string, keyHandle?: string): U2f.Request { - return this.requestStub(appId, keyHandle); - } - - checkRegistration(registrationRequest: U2f.Request, registrationResponse: U2f.RegistrationData) - : U2f.RegistrationResult | U2f.Error { - return this.checkRegistrationStub(registrationRequest, registrationResponse); - } - - checkSignature(signatureRequest: U2f.Request, signatureResponse: U2f.SignatureData, publicKey: string) - : U2f.SignatureResult | U2f.Error { - return this.checkSignatureStub(signatureRequest, signatureResponse, publicKey); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authorization/Authorizer.spec.ts b/themes/triangles/server/src/lib/authorization/Authorizer.spec.ts deleted file mode 100644 index 586814042..000000000 --- a/themes/triangles/server/src/lib/authorization/Authorizer.spec.ts +++ /dev/null @@ -1,372 +0,0 @@ - -import Assert = require("assert"); -import winston = require("winston"); -import { Authorizer } from "./Authorizer"; -import { ACLConfiguration, ACLRule } from "../configuration/schema/AclConfiguration"; -import { Level } from "./Level"; - -describe("authorization/Authorizer", function () { - let authorizer: Authorizer; - let configuration: ACLConfiguration; - - describe("configuration is null", function() { - it("should allow access to anything, anywhere for anybody", function() { - configuration = undefined; - authorizer = new Authorizer(configuration, winston); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc"}, {user: "user1", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user2", groups: ["group1", "group2"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "admin.example.com", resource: "/"}, {user: "user3", groups: ["group3"]}), Level.BYPASS); - }); - }); - - describe("configuration is not null", function () { - beforeEach(function () { - configuration = { - default_policy: "deny", - rules: [] - }; - authorizer = new Authorizer(configuration, winston); - }); - - describe("check access control with default policy to deny", function () { - beforeEach(function () { - configuration.default_policy = "deny"; - }); - - it("should deny access when no rule is provided", function () { - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should control access when multiple domain matcher is provided", function () { - configuration.rules = [{ - domain: "*.mail.example.com", - policy: "two_factor", - subject: "user:user1", - resources: [".*"] - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "mx1.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mx1.server.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should allow access to all resources when resources is not provided", function () { - configuration.rules = [{ - domain: "*.mail.example.com", - policy: "two_factor", - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "mx1.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mx1.server.mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "mail.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - describe("check user rules", function () { - it("should allow access when user has a matching allowing rule", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/another/resource"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should deny to other users", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user2", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/another/resource"}, {user: "user2", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, {user: "user2", groups: ["group1"]}), Level.DENY); - }); - - it("should allow user access only to specific resources", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["/private/.*", "^/begin", "/end$"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/class"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/middle/private/class"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/begin"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/not/begin"}, {user: "user1", groups: ["group1"]}), Level.DENY); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc/end"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/abc/end/x"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should allow access to multiple domains", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "user:user1" - }, { - domain: "home1.example.com", - policy: "one_factor", - resources: [".*"], - subject: "user:user1" - }, { - domain: "home2.example.com", - policy: "deny", - resources: [".*"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home1.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.ONE_FACTOR); - Assert.equal(authorizer.authorization({domain: "home2.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home3.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.DENY); - }); - - it("should apply rules in order", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "one_factor", - resources: ["/my/private/resource"], - subject: "user:user1" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/my/private/.*"], - subject: "user:user1" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/my/.*"], - subject: "user:user1" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/poney"}, {user: "user1", groups: ["group1"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/private/duck"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/my/private/resource"}, {user: "user1", groups: ["group1"]}), Level.ONE_FACTOR); - }); - }); - - describe("check group rules", function () { - it("should allow access when user is in group having a matching allowing rule", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/$"], - subject: "group:group1" - }, { - domain: "home.example.com", - policy: "one_factor", - resources: ["^/test$"], - subject: "group:group2" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/private$"], - subject: "group:group2" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.ONE_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "another.home.example.com", resource: "/"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - }); - }); - }); - - describe("check any rules", function () { - it("should control access when any rules are defined", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "bypass", - resources: ["^/public$"] - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/private$"] - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user1", groups: ["group1", "group2", "group3"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, - {user: "user4", groups: ["group5"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private"}, - {user: "user4", groups: ["group5"]}), Level.DENY); - }); - }); - - describe("check access control with default policy to allow", function () { - beforeEach(function () { - configuration.default_policy = "bypass"; - }); - - it("should allow access to anything when no rule is provided", function () { - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - }); - - it("should deny access to one resource when defined", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["/test"], - subject: "user:user1" - }]; - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/test"}, {user: "user1", groups: ["group1"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "user1", groups: ["group1"]}), Level.BYPASS); - }); - }); - - describe("check access control with complete use case", function () { - beforeEach(function () { - configuration.default_policy = "deny"; - }); - - it("should control access of multiple user (real use case)", function () { - // Let say we have three users: admin, john, harry. - // admin is in groups ["admins"] - // john is in groups ["dev", "admin-private"] - // harry is in groups ["dev"] - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/public$", "^/$"] - }, { - domain: "home.example.com", - policy: "two_factor", - resources: [".*"], - subject: "group:admins" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/?.*"], - subject: "group:admin-private" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/john$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/private/harry"], - subject: "user:harry" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "admin", groups: ["admins"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "john", groups: ["dev", "admin-private"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "john", groups: ["dev", "admin-private"]}), Level.TWO_FACTOR); - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/public"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/admin"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/josh"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/john"}, {user: "harry", groups: ["dev"]}), Level.DENY); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/private/harry"}, {user: "harry", groups: ["dev"]}), Level.TWO_FACTOR); - }); - - it("should allow when allowed at group level and denied at user level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"], - subject: "group:dev" - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should allow access when allowed at 'any' level and denied at user level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should allow access when allowed at 'any' level and denied at group level", function () { - configuration.rules = [{ - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "group:dev" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.DENY); - }); - - it("should respect rules precedence", function () { - // the priority from least to most is 'default_policy', 'all', 'group', 'user' - // and the first rules in each category as a lower priority than the latest. - // You can think of it that way: they override themselves inside each category. - configuration.rules = [{ - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"], - subject: "user:john" - }, { - domain: "home.example.com", - policy: "deny", - resources: ["^/dev/bob$"], - subject: "group:dev" - }, { - domain: "home.example.com", - policy: "two_factor", - resources: ["^/dev/?.*$"] - }]; - - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/john"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - Assert.equal(authorizer.authorization({domain: "home.example.com", resource: "/dev/bob"}, {user: "john", groups: ["dev"]}), Level.TWO_FACTOR); - }); - }); - }); -}); diff --git a/themes/triangles/server/src/lib/authorization/Authorizer.ts b/themes/triangles/server/src/lib/authorization/Authorizer.ts deleted file mode 100644 index 889b7ec20..000000000 --- a/themes/triangles/server/src/lib/authorization/Authorizer.ts +++ /dev/null @@ -1,85 +0,0 @@ - -import { ACLConfiguration, ACLPolicy, ACLRule } from "../configuration/schema/AclConfiguration"; -import { IAuthorizer } from "./IAuthorizer"; -import { Winston } from "../../../types/Dependencies"; -import { MultipleDomainMatcher } from "./MultipleDomainMatcher"; -import { Level } from "./Level"; -import { Object } from "./Object"; -import { Subject } from "./Subject"; - -function MatchDomain(actualDomain: string) { - return function (rule: ACLRule): boolean { - return MultipleDomainMatcher.match(actualDomain, rule.domain); - }; -} - -function MatchResource(actualResource: string) { - return function (rule: ACLRule): boolean { - // If resources key is not provided, the rule applies to all resources. - if (!rule.resources) return true; - - for (let i = 0; i < rule.resources.length; ++i) { - const regexp = new RegExp(rule.resources[i]); - if (regexp.test(actualResource)) return true; - } - return false; - }; -} - -function MatchSubject(subject: Subject) { - return (rule: ACLRule) => { - // If no subject, matches anybody - if (!rule.subject) return true; - - if (rule.subject.startsWith("user:")) { - const ruleUser = rule.subject.split(":")[1]; - if (subject.user == ruleUser) return true; - } - - if (rule.subject.startsWith("group:")) { - const ruleGroup = rule.subject.split(":")[1]; - if (subject.groups.indexOf(ruleGroup) > -1) return true; - } - return false; - }; -} - -export class Authorizer implements IAuthorizer { - private logger: Winston; - private readonly configuration: ACLConfiguration; - - constructor(configuration: ACLConfiguration, logger_: Winston) { - this.logger = logger_; - this.configuration = configuration; - } - - private getMatchingRules(object: Object, subject: Subject): ACLRule[] { - const rules = this.configuration.rules; - if (!rules) return []; - return rules - .filter(MatchDomain(object.domain)) - .filter(MatchResource(object.resource)) - .filter(MatchSubject(subject)); - } - - private ruleToLevel(policy: string): Level { - if (policy == "bypass") { - return Level.BYPASS; - } else if (policy == "one_factor") { - return Level.ONE_FACTOR; - } else if (policy == "two_factor") { - return Level.TWO_FACTOR; - } - return Level.DENY; - } - - authorization(object: Object, subject: Subject): Level { - if (!this.configuration) return Level.BYPASS; - - const rules = this.getMatchingRules(object, subject); - - return (rules.length > 0) - ? this.ruleToLevel(rules[0].policy) // extract the policy of the first matching rule - : this.ruleToLevel(this.configuration.default_policy); // otherwise use the default policy - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authorization/AuthorizerStub.spec.ts b/themes/triangles/server/src/lib/authorization/AuthorizerStub.spec.ts deleted file mode 100644 index 9bd6f4a85..000000000 --- a/themes/triangles/server/src/lib/authorization/AuthorizerStub.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import Sinon = require("sinon"); -import { IAuthorizer } from "./IAuthorizer"; -import { Level } from "./Level"; -import { Object } from "./Object"; -import { Subject } from "./Subject"; - -export class AuthorizerStub implements IAuthorizer { - authorizationMock: Sinon.SinonStub; - - constructor() { - this.authorizationMock = Sinon.stub(); - } - - authorization(object: Object, subject: Subject): Level { - return this.authorizationMock(object, subject); - } -} diff --git a/themes/triangles/server/src/lib/authorization/IAuthorizer.ts b/themes/triangles/server/src/lib/authorization/IAuthorizer.ts deleted file mode 100644 index fe7ba367a..000000000 --- a/themes/triangles/server/src/lib/authorization/IAuthorizer.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Level } from "./Level"; -import { Subject } from "./Subject"; -import { Object } from "./Object"; - -export interface IAuthorizer { - authorization(object: Object, subject: Subject): Level; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authorization/Level.ts b/themes/triangles/server/src/lib/authorization/Level.ts deleted file mode 100644 index d12802610..000000000 --- a/themes/triangles/server/src/lib/authorization/Level.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum Level { - BYPASS = 0, - ONE_FACTOR = 1, - TWO_FACTOR = 2, - DENY = 3 -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authorization/MultipleDomainMatcher.ts b/themes/triangles/server/src/lib/authorization/MultipleDomainMatcher.ts deleted file mode 100644 index 64c647a4a..000000000 --- a/themes/triangles/server/src/lib/authorization/MultipleDomainMatcher.ts +++ /dev/null @@ -1,12 +0,0 @@ - -export class MultipleDomainMatcher { - static match(domain: string, pattern: string): boolean { - if (pattern.startsWith("*") && - domain.endsWith(pattern.substr(1))) { - return true; - } - else if (domain == pattern) { - return true; - } - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authorization/Object.ts b/themes/triangles/server/src/lib/authorization/Object.ts deleted file mode 100644 index 5411b0d24..000000000 --- a/themes/triangles/server/src/lib/authorization/Object.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface Object { - domain: string; - resource: string; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/authorization/Subject.ts b/themes/triangles/server/src/lib/authorization/Subject.ts deleted file mode 100644 index 310d6b4c0..000000000 --- a/themes/triangles/server/src/lib/authorization/Subject.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface Subject { - user: string; - groups: string[]; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/ConfigurationParser.spec.ts b/themes/triangles/server/src/lib/configuration/ConfigurationParser.spec.ts deleted file mode 100644 index 60c0f6182..000000000 --- a/themes/triangles/server/src/lib/configuration/ConfigurationParser.spec.ts +++ /dev/null @@ -1,171 +0,0 @@ -import * as Assert from "assert"; -import { Configuration } from "./schema/Configuration"; -import { ACLConfiguration } from "./schema/AclConfiguration"; -import { ConfigurationParser } from "./ConfigurationParser"; - -describe("configuration/ConfigurationParser", function () { - function buildYamlConfig(): Configuration { - const yaml_config: Configuration = { - port: 8080, - authentication_backend: { - ldap: { - url: "http://ldap", - base_dn: "dc=example,dc=com", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - user: "user", - password: "pass" - }, - }, - session: { - domain: "example.com", - secret: "secret", - expiration: 40000 - }, - storage: { - local: { - path: "/mydirectory" - } - }, - regulation: { - max_retries: 3, - find_time: 5 * 60, - ban_time: 5 * 60 - }, - logs_level: "debug", - notifier: { - email: { - username: "user", - password: "password", - sender: "admin@example.com", - service: "gmail" - } - } - }; - return yaml_config; - } - - describe("port", function () { - it("should read the port from the yaml file", function () { - const yaml_config = buildYamlConfig(); - yaml_config.port = 7070; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.port, 7070); - }); - - it("should default the port to 8080 if not provided", function () { - const yaml_config = buildYamlConfig(); - delete yaml_config.port; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.port, 8080); - }); - }); - - describe("test session configuration", function() { - it("should get the session attributes", function () { - const yaml_config = buildYamlConfig(); - yaml_config.session = { - domain: "example.com", - secret: "secret", - expiration: 3600, - inactivity: 4000 - }; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.session.domain, "example.com"); - Assert.equal(config.session.secret, "secret"); - Assert.equal(config.session.expiration, 3600); - Assert.equal(config.session.inactivity, 4000); - }); - - it("should be ok not specifying inactivity", function () { - const yaml_config = buildYamlConfig(); - yaml_config.session = { - domain: "example.com", - secret: "secret", - expiration: 3600 - }; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.session.domain, "example.com"); - Assert.equal(config.session.secret, "secret"); - Assert.equal(config.session.expiration, 3600); - Assert.equal(config.session.inactivity, undefined); - }); - }); - - it("should get the log level", function () { - const yaml_config = buildYamlConfig(); - yaml_config.logs_level = "debug"; - const config = ConfigurationParser.parse(yaml_config); - Assert.equal(config.logs_level, "debug"); - }); - - it("should get the notifier config", function () { - const userConfig = buildYamlConfig(); - userConfig.notifier = { - email: { - username: "user", - password: "pass", - sender: "admin@example.com", - service: "gmail" - } - }; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.notifier, { - email: { - username: "user", - password: "pass", - sender: "admin@example.com", - service: "gmail" - } - }); - }); - - describe("access_control", function() { - it("should adapt access_control when it is already ok", function () { - const userConfig = buildYamlConfig(); - userConfig.access_control = { - default_policy: "deny", - rules: [{ - domain: "www.example.com", - policy: "two_factor", - subject: "user:user" - }, { - domain: "public.example.com", - policy: "two_factor" - }] - }; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.access_control, { - default_policy: "deny", - rules: [{ - domain: "www.example.com", - policy: "two_factor", - subject: "user:user" - }, { - domain: "public.example.com", - policy: "two_factor" - }] - } as ACLConfiguration); - }); - - - it("should adapt access_control when it is empty", function () { - const userConfig = buildYamlConfig(); - userConfig.access_control = {} as any; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.access_control, { - default_policy: "bypass", - rules: [] - }); - }); - }); - - describe("default_redirection_url", function() { - it("should parse default_redirection_url", function() { - const userConfig = buildYamlConfig(); - userConfig.default_redirection_url = "dummy_url"; - const config = ConfigurationParser.parse(userConfig); - Assert.deepEqual(config.default_redirection_url, "dummy_url"); - }); - }); -}); diff --git a/themes/triangles/server/src/lib/configuration/ConfigurationParser.ts b/themes/triangles/server/src/lib/configuration/ConfigurationParser.ts deleted file mode 100644 index d92d163c4..000000000 --- a/themes/triangles/server/src/lib/configuration/ConfigurationParser.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import * as ObjectPath from "object-path"; -import { Configuration, complete } from "./schema/Configuration"; -import Ajv = require("ajv"); -import Path = require("path"); -import Util = require("util"); - -export class ConfigurationParser { - private static parseTypes(configuration: Configuration): string[] { - const schema = require(Path.resolve(__dirname, "./Configuration.schema.json")); - const ajv = new Ajv({ - allErrors: true, - missingRefs: "fail" - }); - ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-06.json")); - const valid = ajv.validate(schema, configuration); - if (!valid) - return ajv.errors.map( - (e: Ajv.ErrorObject) => { return ajv.errorsText([e]); }); - return []; - } - - static parse(configuration: Configuration): Configuration { - const validationErrors = this.parseTypes(configuration); - if (validationErrors.length > 0) { - validationErrors.forEach((e: string) => { console.log(e); }); - throw new Error("Malformed configuration (schema). Please double-check your configuration file."); - } - - const [newConfiguration, completionErrors] = complete(configuration); - - if (completionErrors.length > 0) { - completionErrors.forEach((e: string) => { console.log(e); }); - throw new Error("Malformed configuration (validator). Please double-check your configuration file."); - } - return newConfiguration; - } -} - diff --git a/themes/triangles/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts b/themes/triangles/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts deleted file mode 100644 index d4a3093ee..000000000 --- a/themes/triangles/server/src/lib/configuration/SessionConfigurationBuilder.spec.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { SessionConfigurationBuilder } from "./SessionConfigurationBuilder"; -import { Configuration } from "./schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; - -import ExpressSession = require("express-session"); -import ConnectRedis = require("connect-redis"); -import Sinon = require("sinon"); -import Assert = require("assert"); - -describe("configuration/SessionConfigurationBuilder", function () { - const configuration: Configuration = { - access_control: { - default_policy: "deny", - rules: [] - }, - totp: { - issuer: "authelia.com" - }, - authentication_backend: { - ldap: { - url: "ldap://ldap", - user: "user", - base_dn: "dc=example,dc=com", - password: "password", - additional_groups_dn: "ou=groups", - additional_users_dn: "ou=users", - group_name_attribute: "", - groups_filter: "", - mail_attribute: "", - users_filter: "" - }, - }, - logs_level: "debug", - notifier: { - filesystem: { - filename: "/test" - } - }, - port: 8080, - session: { - name: "authelia_session", - domain: "example.com", - expiration: 3600, - secret: "secret" - }, - regulation: { - max_retries: 3, - ban_time: 5 * 60, - find_time: 5 * 60 - }, - storage: { - local: { - in_memory: true - } - } - }; - - const deps: GlobalDependencies = { - ConnectRedis: Sinon.spy() as any, - ldapjs: Sinon.spy() as any, - nedb: Sinon.spy() as any, - session: Sinon.spy() as any, - speakeasy: Sinon.spy() as any, - u2f: Sinon.spy() as any, - winston: Sinon.spy() as any, - Redis: Sinon.spy() as any - }; - - it("should return session options without redis options", function () { - const options = SessionConfigurationBuilder.build(configuration, deps); - const expectedOptions = { - name: "authelia_session", - secret: "secret", - resave: false, - saveUninitialized: true, - cookie: { - secure: true, - httpOnly: true, - maxAge: 3600, - domain: "example.com" - } - }; - - Assert.deepEqual(expectedOptions, options); - }); - - it("should return session options with redis options", function () { - configuration.session["redis"] = { - host: "redis.example.com", - port: 6379 - }; - const RedisStoreMock = Sinon.spy(); - const redisClient = Sinon.mock().returns({ on: Sinon.spy() }); - - deps.ConnectRedis = Sinon.stub().returns(RedisStoreMock) as any; - deps.Redis = { - createClient: Sinon.mock().returns(redisClient) - } as any; - - const options = SessionConfigurationBuilder.build(configuration, deps); - - const expectedOptions: ExpressSession.SessionOptions = { - secret: "secret", - resave: false, - saveUninitialized: true, - name: "authelia_session", - cookie: { - secure: true, - httpOnly: true, - maxAge: 3600, - domain: "example.com" - }, - store: Sinon.match.object as any - }; - - Assert((deps.ConnectRedis as Sinon.SinonStub).calledWith(deps.session)); - Assert.equal(options.secret, expectedOptions.secret); - Assert.equal(options.resave, expectedOptions.resave); - Assert.equal(options.saveUninitialized, expectedOptions.saveUninitialized); - Assert.deepEqual(options.cookie, expectedOptions.cookie); - Assert(options.store != undefined); - }); - - it("should return session options with redis password", function () { - configuration.session["redis"] = { - host: "redis.example.com", - port: 6379, - password: "authelia_pass" - }; - const RedisStoreMock = Sinon.spy(); - const redisClient = Sinon.mock().returns({ on: Sinon.spy() }); - const createClientStub = Sinon.stub(); - - deps.ConnectRedis = Sinon.stub().returns(RedisStoreMock) as any; - deps.Redis = { - createClient: createClientStub - } as any; - - createClientStub.returns(redisClient); - - const options = SessionConfigurationBuilder.build(configuration, deps); - - Assert(createClientStub.calledWith({ - host: "redis.example.com", - port: 6379, - password: "authelia_pass" - })); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/SessionConfigurationBuilder.ts b/themes/triangles/server/src/lib/configuration/SessionConfigurationBuilder.ts deleted file mode 100644 index 6ce643d9d..000000000 --- a/themes/triangles/server/src/lib/configuration/SessionConfigurationBuilder.ts +++ /dev/null @@ -1,52 +0,0 @@ -import ExpressSession = require("express-session"); -import Redis = require("redis"); - -import { Configuration } from "./schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; -import { RedisStoreOptions } from "connect-redis"; - -export class SessionConfigurationBuilder { - - static build(configuration: Configuration, deps: GlobalDependencies): ExpressSession.SessionOptions { - const sessionOptions: ExpressSession.SessionOptions = { - name: configuration.session.name, - secret: configuration.session.secret, - resave: false, - saveUninitialized: true, - cookie: { - secure: true, - httpOnly: true, - maxAge: configuration.session.expiration, - domain: configuration.session.domain - }, - }; - - if (configuration.session.redis) { - let redisOptions; - const options: Redis.ClientOpts = { - host: configuration.session.redis.host, - port: configuration.session.redis.port - }; - - if (configuration.session.redis.password) { - options["password"] = configuration.session.redis.password; - } - const client = deps.Redis.createClient(options); - - client.on("error", function (err: Error) { - console.error("Redis error:", err); - }); - - redisOptions = { - client: client, - logErrors: true - }; - - if (redisOptions) { - const RedisStore = deps.ConnectRedis(deps.session); - sessionOptions.store = new RedisStore(redisOptions); - } - } - return sessionOptions; - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/AclConfiguration.spec.ts b/themes/triangles/server/src/lib/configuration/schema/AclConfiguration.spec.ts deleted file mode 100644 index d1e2a03a2..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/AclConfiguration.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ACLConfiguration, complete } from "./AclConfiguration"; -import Assert = require("assert"); - -describe("configuration/schema/AclConfiguration", function() { - it("should complete ACLConfiguration", function() { - const configuration: ACLConfiguration = {}; - const [newConfiguration, errors] = complete(configuration); - - Assert.deepEqual(newConfiguration.default_policy, "bypass"); - Assert.deepEqual(newConfiguration.rules, []); - }); - - it("should return errors when subject is not good", function() { - const configuration: ACLConfiguration = { - default_policy: "deny", - rules: [{ - domain: "dev.example.com", - subject: "user:abc", - policy: "bypass" - }, { - domain: "dev.example.com", - subject: "user:def", - policy: "bypass" - }, { - domain: "dev.example.com", - subject: "badkey:abc", - policy: "bypass" - }] - }; - const [newConfiguration, errors] = complete(configuration); - - Assert.deepEqual(errors, ["Rule 2 has wrong subject. It should be starting with user: or group:."]); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/AclConfiguration.ts b/themes/triangles/server/src/lib/configuration/schema/AclConfiguration.ts deleted file mode 100644 index 40401dd64..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/AclConfiguration.ts +++ /dev/null @@ -1,41 +0,0 @@ - -export type ACLPolicy = "deny" | "bypass" | "one_factor" | "two_factor"; - -export type ACLRule = { - domain: string; - resources?: string[]; - subject?: string; - policy: ACLPolicy; -}; - -export interface ACLConfiguration { - default_policy?: ACLPolicy; - rules?: ACLRule[]; -} - -export function complete(configuration: ACLConfiguration): [ACLConfiguration, string[]] { - const newConfiguration: ACLConfiguration = (configuration) - ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.default_policy) { - newConfiguration.default_policy = "bypass"; - } - - if (!newConfiguration.rules) { - newConfiguration.rules = []; - } - - if (newConfiguration.rules.length > 0) { - const errors: string[] = []; - newConfiguration.rules.forEach((r, idx) => { - if (r.subject && !r.subject.match(/^(user|group):[a-zA-Z0-9]+$/)) { - errors.push(`Rule ${idx} has wrong subject. It should be starting with user: or group:.`); - } - }); - if (errors.length > 0) { - return [newConfiguration, errors]; - } - } - - return [newConfiguration, []]; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts b/themes/triangles/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts deleted file mode 100644 index 3ca86381c..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { AuthenticationBackendConfiguration, complete } from "./AuthenticationBackendConfiguration"; -import Assert = require("assert"); - -describe("configuration/schema/AuthenticationBackendConfiguration", function() { - it("should ensure there is at least one key", function() { - const configuration: AuthenticationBackendConfiguration = {} as any; - const [newConfiguration, error] = complete(configuration); - - Assert.equal(error, "Authentication backend must have one of the following keys:`ldap` or `file`"); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts b/themes/triangles/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts deleted file mode 100644 index 7f77f894f..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/AuthenticationBackendConfiguration.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { LdapConfiguration } from "./LdapConfiguration"; -import { FileUsersDatabaseConfiguration } from "./FileUsersDatabaseConfiguration"; - -export interface AuthenticationBackendConfiguration { - ldap?: LdapConfiguration; - file?: FileUsersDatabaseConfiguration; -} - -export function complete( - configuration: AuthenticationBackendConfiguration) - : [AuthenticationBackendConfiguration, string] { - - const newConfiguration: AuthenticationBackendConfiguration = (configuration) - ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (Object.keys(newConfiguration).length != 1) { - return [ - newConfiguration, - "Authentication backend must have one of the following keys:" + - "`ldap` or `file`" - ]; - } - - return [newConfiguration, undefined]; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/Configuration.ts b/themes/triangles/server/src/lib/configuration/schema/Configuration.ts deleted file mode 100644 index 8d16a5fb2..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/Configuration.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { ACLConfiguration, complete as AclConfigurationComplete } from "./AclConfiguration"; -import { AuthenticationBackendConfiguration, complete as AuthenticationBackendComplete } from "./AuthenticationBackendConfiguration"; -import { NotifierConfiguration, complete as NotifierConfigurationComplete } from "./NotifierConfiguration"; -import { RegulationConfiguration, complete as RegulationConfigurationComplete } from "./RegulationConfiguration"; -import { SessionConfiguration, complete as SessionConfigurationComplete } from "./SessionConfiguration"; -import { StorageConfiguration, complete as StorageConfigurationComplete } from "./StorageConfiguration"; -import { TotpConfiguration, complete as TotpConfigurationComplete } from "./TotpConfiguration"; - -export interface Configuration { - access_control?: ACLConfiguration; - authentication_backend: AuthenticationBackendConfiguration; - default_redirection_url?: string; - logs_level?: string; - notifier?: NotifierConfiguration; - port?: number; - regulation?: RegulationConfiguration; - session?: SessionConfiguration; - storage?: StorageConfiguration; - totp?: TotpConfiguration; -} - -export function complete( - configuration: Configuration): - [Configuration, string[]] { - - const newConfiguration: Configuration = JSON.parse( - JSON.stringify(configuration)); - const errors: string[] = []; - - const [acls, aclsErrors] = AclConfigurationComplete( - newConfiguration.access_control); - - newConfiguration.access_control = acls; - if (aclsErrors.length > 0) { - errors.concat(aclsErrors); - } - - const [backend, error] = - AuthenticationBackendComplete( - newConfiguration.authentication_backend); - - if (error) errors.push(error); - newConfiguration.authentication_backend = backend; - - if (!newConfiguration.logs_level) { - newConfiguration.logs_level = "info"; - } - - const [notifier, notifierError] = NotifierConfigurationComplete( - newConfiguration.notifier); - newConfiguration.notifier = notifier; - if (notifierError) errors.push(notifierError); - - if (!newConfiguration.port) { - newConfiguration.port = 8080; - } - - newConfiguration.regulation = RegulationConfigurationComplete( - newConfiguration.regulation); - newConfiguration.session = SessionConfigurationComplete( - newConfiguration.session); - newConfiguration.storage = StorageConfigurationComplete( - newConfiguration.storage); - newConfiguration.totp = TotpConfigurationComplete( - newConfiguration.totp); - - return [newConfiguration, errors]; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts b/themes/triangles/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts deleted file mode 100644 index d19002ba9..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/FileUsersDatabaseConfiguration.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export interface FileUsersDatabaseConfiguration { - path: string; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/LdapConfiguration.spec.ts b/themes/triangles/server/src/lib/configuration/schema/LdapConfiguration.spec.ts deleted file mode 100644 index cc73d1085..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/LdapConfiguration.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import Assert = require("assert"); -import { LdapConfiguration, complete } from "./LdapConfiguration"; - -describe("configuration/schema/AuthenticationMethodsConfiguration", function() { - it("should ensure at least one key is provided", function() { - const configuration: LdapConfiguration = { - url: "ldap.example.com", - base_dn: "dc=example,dc=com", - user: "admin", - password: "password" - }; - const newConfiguration = complete(configuration); - - Assert.deepEqual(newConfiguration, { - url: "ldap.example.com", - base_dn: "dc=example,dc=com", - user: "admin", - password: "password", - users_filter: "cn={0}", - group_name_attribute: "cn", - groups_filter: "member={dn}", - mail_attribute: "mail" - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/LdapConfiguration.ts b/themes/triangles/server/src/lib/configuration/schema/LdapConfiguration.ts deleted file mode 100644 index 5dacb9390..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/LdapConfiguration.ts +++ /dev/null @@ -1,40 +0,0 @@ -import Util = require("util"); - -export interface LdapConfiguration { - url: string; - base_dn: string; - - additional_users_dn?: string; - users_filter?: string; - - additional_groups_dn?: string; - groups_filter?: string; - - group_name_attribute?: string; - mail_attribute?: string; - - user: string; // admin username - password: string; // admin password -} - -export function complete(configuration: LdapConfiguration): LdapConfiguration { - const newConfiguration: LdapConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.users_filter) { - newConfiguration.users_filter = "cn={0}"; - } - - if (!newConfiguration.groups_filter) { - newConfiguration.groups_filter = "member={dn}"; - } - - if (!newConfiguration.group_name_attribute) { - newConfiguration.group_name_attribute = "cn"; - } - - if (!newConfiguration.mail_attribute) { - newConfiguration.mail_attribute = "mail"; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts b/themes/triangles/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts deleted file mode 100644 index 6c576e8e0..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/NotifierConfiguration.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import Assert = require("assert"); -import { NotifierConfiguration, complete } from "./NotifierConfiguration"; - -describe("configuration/schema/NotifierConfiguration", function() { - it("should use a default notifier when none is provided", function() { - const configuration: NotifierConfiguration = {}; - const [newConfiguration, error] = complete(configuration); - - Assert.deepEqual(newConfiguration.filesystem, {filename: "/tmp/authelia/notification.txt"}); - }); - - it("should ensure correct key is provided", function() { - const configuration = { - abc: "badvalue" - }; - const [newConfiguration, error] = complete(configuration as any); - - Assert.equal(error, "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"); - }); - - it("should ensure there is no more than one key", function() { - const configuration: NotifierConfiguration = { - smtp: { - host: "smtp.example.com", - port: 25, - secure: false, - sender: "test@example.com" - }, - email: { - username: "test", - password: "test", - sender: "test@example.com", - service: "gmail" - } - }; - const [newConfiguration, error] = complete(configuration); - - Assert.equal(error, "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/NotifierConfiguration.ts b/themes/triangles/server/src/lib/configuration/schema/NotifierConfiguration.ts deleted file mode 100644 index 7bcce15c2..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/NotifierConfiguration.ts +++ /dev/null @@ -1,45 +0,0 @@ - -export interface EmailNotifierConfiguration { - username: string; - password: string; - sender: string; - service: string; -} - -export interface SmtpNotifierConfiguration { - username?: string; - password?: string; - host: string; - port: number; - secure: boolean; - sender: string; -} - -export interface FileSystemNotifierConfiguration { - filename: string; -} - -export interface NotifierConfiguration { - email?: EmailNotifierConfiguration; - smtp?: SmtpNotifierConfiguration; - filesystem?: FileSystemNotifierConfiguration; -} - -export function complete(configuration: NotifierConfiguration): [NotifierConfiguration, string] { - const newConfiguration: NotifierConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (Object.keys(newConfiguration).length == 0) - newConfiguration.filesystem = { filename: "/tmp/authelia/notification.txt" }; - - const ERROR = "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'"; - - if (Object.keys(newConfiguration).length != 1) - return [newConfiguration, ERROR]; - - const key = Object.keys(newConfiguration)[0]; - - if (key != "filesystem" && key != "smtp" && key != "email") - return [newConfiguration, ERROR]; - - return [newConfiguration, undefined]; -} diff --git a/themes/triangles/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts b/themes/triangles/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts deleted file mode 100644 index dce2caf4e..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/RegulationConfiguration.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Assert = require("assert"); -import { RegulationConfiguration, complete } from "./RegulationConfiguration"; - -describe("configuration/schema/RegulationConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: RegulationConfiguration = {}; - const newConfiguration = complete(configuration); - - Assert.equal(newConfiguration.ban_time, 300); - Assert.equal(newConfiguration.find_time, 120); - Assert.equal(newConfiguration.max_retries, 3); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/RegulationConfiguration.ts b/themes/triangles/server/src/lib/configuration/schema/RegulationConfiguration.ts deleted file mode 100644 index 117463f43..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/RegulationConfiguration.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface RegulationConfiguration { - max_retries?: number; - find_time?: number; - ban_time?: number; -} - -export function complete(configuration: RegulationConfiguration): RegulationConfiguration { - const newConfiguration: RegulationConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.max_retries) { - newConfiguration.max_retries = 3; - } - - if (!newConfiguration.find_time) { - newConfiguration.find_time = 120; // seconds - } - - if (!newConfiguration.ban_time) { - newConfiguration.ban_time = 300; // seconds - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/SessionConfiguration.spec.ts b/themes/triangles/server/src/lib/configuration/schema/SessionConfiguration.spec.ts deleted file mode 100644 index e54010837..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/SessionConfiguration.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Assert = require("assert"); -import { SessionConfiguration, complete } from "./SessionConfiguration"; - -describe("configuration/schema/SessionConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: SessionConfiguration = { - domain: "example.com", - secret: "unsecure_secret" - }; - const newConfiguration = complete(configuration); - - Assert.equal(newConfiguration.name, 'authelia_session'); - Assert.equal(newConfiguration.expiration, 3600000); - Assert.equal(newConfiguration.inactivity, undefined); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/SessionConfiguration.ts b/themes/triangles/server/src/lib/configuration/schema/SessionConfiguration.ts deleted file mode 100644 index 2c88bb215..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/SessionConfiguration.ts +++ /dev/null @@ -1,32 +0,0 @@ -export interface SessionRedisOptions { - host: string; - port: number; - password?: string; -} - -export interface SessionConfiguration { - name?: string; - domain: string; - secret: string; - expiration?: number; - inactivity?: number; - redis?: SessionRedisOptions; -} - -export function complete(configuration: SessionConfiguration): SessionConfiguration { - const newConfiguration: SessionConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.name) { - newConfiguration.name = "authelia_session"; - } - - if (!newConfiguration.expiration) { - newConfiguration.expiration = 3600000; // 1 hour - } - - if (!newConfiguration.inactivity) { - newConfiguration.inactivity = undefined; // disabled - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/StorageConfiguration.spec.ts b/themes/triangles/server/src/lib/configuration/schema/StorageConfiguration.spec.ts deleted file mode 100644 index 9d02a11b6..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/StorageConfiguration.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import Assert = require("assert"); -import { StorageConfiguration, complete } from "./StorageConfiguration"; - -describe("configuration/schema/StorageConfiguration", function() { - it("should return default regulation configuration", function() { - const configuration: StorageConfiguration = {}; - const newConfiguration = complete(configuration); - - Assert.deepEqual(newConfiguration, { - local: { - in_memory: true - } - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/StorageConfiguration.ts b/themes/triangles/server/src/lib/configuration/schema/StorageConfiguration.ts deleted file mode 100644 index 47e356ef4..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/StorageConfiguration.ts +++ /dev/null @@ -1,30 +0,0 @@ -export interface MongoStorageConfiguration { - url: string; - database: string; - auth?: { - username: string; - password: string; - }; -} - -export interface LocalStorageConfiguration { - path?: string; - in_memory?: boolean; -} - -export interface StorageConfiguration { - local?: LocalStorageConfiguration; - mongo?: MongoStorageConfiguration; -} - -export function complete(configuration: StorageConfiguration): StorageConfiguration { - const newConfiguration: StorageConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.local && !newConfiguration.mongo) { - newConfiguration.local = { - in_memory: true - }; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/TotpConfiguration.ts b/themes/triangles/server/src/lib/configuration/schema/TotpConfiguration.ts deleted file mode 100644 index 683135639..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/TotpConfiguration.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface TotpConfiguration { - issuer: string; -} - -export function complete(configuration: TotpConfiguration): TotpConfiguration { - const newConfiguration: TotpConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {}; - - if (!newConfiguration.issuer) { - newConfiguration.issuer = "authelia.com"; - } - - return newConfiguration; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts b/themes/triangles/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts deleted file mode 100644 index 8008b4833..000000000 --- a/themes/triangles/server/src/lib/configuration/schema/UserDatabaseConfiguration.ts +++ /dev/null @@ -1,9 +0,0 @@ - -export interface UserInfo { - username: string; - password_hash: string; - email: string; - groups?: string[]; -} - -export type UserDatabaseConfiguration = UserInfo[]; \ No newline at end of file diff --git a/themes/triangles/server/src/lib/connectors/mongo/IMongoClient.d.ts b/themes/triangles/server/src/lib/connectors/mongo/IMongoClient.d.ts deleted file mode 100644 index 36cb4b8bf..000000000 --- a/themes/triangles/server/src/lib/connectors/mongo/IMongoClient.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import MongoDB = require("mongodb"); -import Bluebird = require("bluebird"); - -export interface IMongoClient { - collection(name: string): Bluebird -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/connectors/mongo/MongoClient.spec.ts b/themes/triangles/server/src/lib/connectors/mongo/MongoClient.spec.ts deleted file mode 100644 index ca0c68593..000000000 --- a/themes/triangles/server/src/lib/connectors/mongo/MongoClient.spec.ts +++ /dev/null @@ -1,119 +0,0 @@ -import Assert = require("assert"); -import Bluebird = require("bluebird"); -import MongoDB = require("mongodb"); -import Sinon = require("sinon"); - -import { MongoClient } from "./MongoClient"; -import { GlobalLoggerStub } from "../../logging/GlobalLoggerStub.spec"; -import { MongoStorageConfiguration } from "../../configuration/schema/StorageConfiguration"; - -describe("connectors/mongo/MongoClient", function () { - let MongoClientStub: any; - let mongoClientStub: any; - let mongoDatabaseStub: any; - let logger: GlobalLoggerStub = new GlobalLoggerStub(); - - const configuration: MongoStorageConfiguration = { - url: "mongo://url", - database: "databasename" - }; - - describe("connection", () => { - before(() => { - mongoClientStub = { - db: Sinon.stub() - }; - mongoDatabaseStub = { - on: Sinon.stub(), - collection: Sinon.stub() - } - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - undefined, mongoClientStub); - mongoClientStub.db.returns( - mongoDatabaseStub); - }); - - after(() => { - MongoClientStub.restore(); - }); - - it("should use credentials from configuration", () => { - configuration.auth = { - username: "authelia", - password: "authelia_pass" - }; - - const client = new MongoClient(configuration, logger); - return client.collection("test") - .then(() => { - Assert(MongoClientStub.calledWith("mongo://url", { - auth: { - user: "authelia", - password: "authelia_pass" - } - })) - }); - }); - }); - - describe("collection", () => { - before(function() { - mongoClientStub = { - db: Sinon.stub() - }; - mongoDatabaseStub = { - on: Sinon.stub(), - collection: Sinon.stub() - } - }); - - describe("Connection to mongo is ok", function() { - before(function () { - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - undefined, mongoClientStub); - mongoClientStub.db.returns( - mongoDatabaseStub); - }); - - after(function () { - MongoClientStub.restore(); - }); - - it("should create a collection", function () { - const COLLECTION_NAME = "mycollection"; - const client = new MongoClient(configuration, logger); - - mongoDatabaseStub.collection.returns("COL"); - return client.collection(COLLECTION_NAME) - .then((collection) => mongoDatabaseStub.collection.calledWith(COLLECTION_NAME)); - }); - }); - - describe("Connection to mongo is broken", function() { - before(function () { - MongoClientStub = Sinon.stub( - MongoDB.MongoClient, "connect"); - MongoClientStub.yields( - new Error("Failed connection"), undefined); - }); - - after(function () { - MongoClientStub.restore(); - }); - - it("should fail creating the collection", function() { - const COLLECTION_NAME = "mycollection"; - const client = new MongoClient(configuration, logger); - - mongoDatabaseStub.collection.returns("COL"); - return client.collection(COLLECTION_NAME) - .then((collection) => Bluebird.reject(new Error("should not be here."))) - .catch((err) => Bluebird.resolve()); - }); - }) - }); -}); diff --git a/themes/triangles/server/src/lib/connectors/mongo/MongoClient.ts b/themes/triangles/server/src/lib/connectors/mongo/MongoClient.ts deleted file mode 100644 index d15731e97..000000000 --- a/themes/triangles/server/src/lib/connectors/mongo/MongoClient.ts +++ /dev/null @@ -1,76 +0,0 @@ - -import MongoDB = require("mongodb"); -import { IMongoClient } from "./IMongoClient"; -import Bluebird = require("bluebird"); -import { AUTHENTICATION_FAILED } from "../../../../../shared/UserMessages"; -import { IGlobalLogger } from "../../logging/IGlobalLogger"; -import { MongoStorageConfiguration } from "../../configuration/schema/StorageConfiguration"; - -export class MongoClient implements IMongoClient { - private configuration: MongoStorageConfiguration; - - private database: MongoDB.Db; - private client: MongoDB.MongoClient; - private logger: IGlobalLogger; - - constructor( - configuration: MongoStorageConfiguration, - logger: IGlobalLogger) { - - this.configuration = configuration; - this.logger = logger; - } - - connect(): Bluebird { - const that = this; - const options: MongoDB.MongoClientOptions = {}; - if (that.configuration.auth) { - options["auth"] = { - user: that.configuration.auth.username, - password: that.configuration.auth.password - }; - } - - return new Bluebird((resolve, reject) => { - MongoDB.MongoClient.connect( - this.configuration.url, - options, - function(err, client) { - if (err) { - reject(err); - return; - } - resolve(client); - }); - }) - .then(function (client: MongoDB.MongoClient) { - that.database = client.db(that.configuration.database); - that.database.on("close", () => { - that.logger.info("[MongoClient] Lost connection."); - }); - that.database.on("reconnect", () => { - that.logger.info("[MongoClient] Reconnected."); - }); - that.client = client; - }); - } - - close(): Bluebird { - if (this.client) { - this.client.close(); - this.database = undefined; - this.client = undefined; - } - return Bluebird.resolve(); - } - - collection(name: string): Bluebird { - if (!this.client) { - const that = this; - return this.connect() - .then(() => Bluebird.resolve(that.database.collection(name))); - } - - return Bluebird.resolve(this.database.collection(name)); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/connectors/mongo/MongoClientStub.spec.ts b/themes/triangles/server/src/lib/connectors/mongo/MongoClientStub.spec.ts deleted file mode 100644 index 1cfd48e32..000000000 --- a/themes/triangles/server/src/lib/connectors/mongo/MongoClientStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import MongoDB = require("mongodb"); -import Bluebird = require("bluebird"); -import { IMongoClient } from "../../../../src/lib/connectors/mongo/IMongoClient"; - -export class MongoClientStub implements IMongoClient { - public collectionStub: Sinon.SinonStub; - - constructor() { - this.collectionStub = Sinon.stub(); - } - - collection(name: string): Bluebird { - return this.collectionStub(name); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/logging/GlobalLogger.ts b/themes/triangles/server/src/lib/logging/GlobalLogger.ts deleted file mode 100644 index 4da7acf49..000000000 --- a/themes/triangles/server/src/lib/logging/GlobalLogger.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { IGlobalLogger } from "./IGlobalLogger"; -import Util = require("util"); -import Express = require("express"); -import Winston = require("winston"); - -declare module "express" { - interface Request { - id: string; - } -} - -export class GlobalLogger implements IGlobalLogger { - private winston: typeof Winston; - constructor(winston: typeof Winston) { - this.winston = winston; - } - - private buildMessage(message: string, ...args: any[]): string { - return Util.format("date='%s' message='%s'", new Date(), - Util.format(message, ...args)); - } - - info(message: string, ...args: any[]): void { - this.winston.info(this.buildMessage(message, ...args)); - } - - debug(message: string, ...args: any[]): void { - this.winston.debug(this.buildMessage(message, ...args)); - } - - error(message: string, ...args: any[]): void { - this.winston.debug(this.buildMessage(message, ...args)); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/logging/GlobalLoggerStub.spec.ts b/themes/triangles/server/src/lib/logging/GlobalLoggerStub.spec.ts deleted file mode 100644 index d4bb13710..000000000 --- a/themes/triangles/server/src/lib/logging/GlobalLoggerStub.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import Sinon = require("sinon"); -import { GlobalLogger } from "./GlobalLogger"; -import Winston = require("winston"); -import Express = require("express"); -import { IGlobalLogger } from "./IGlobalLogger"; - -export class GlobalLoggerStub implements IGlobalLogger { - infoStub: Sinon.SinonStub; - debugStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - private globalLogger: IGlobalLogger; - - constructor(enableLogging?: boolean) { - this.infoStub = Sinon.stub(); - this.debugStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - if (enableLogging) - this.globalLogger = new GlobalLogger(Winston); - } - - info(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.infoStub(message, ...args); - } - - debug(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.debugStub(message, ...args); - } - - error(message: string, ...args: any[]): void { - if (this.globalLogger) - this.globalLogger.info(message, ...args); - this.errorStub(message, ...args); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/logging/IGlobalLogger.ts b/themes/triangles/server/src/lib/logging/IGlobalLogger.ts deleted file mode 100644 index 548515ec7..000000000 --- a/themes/triangles/server/src/lib/logging/IGlobalLogger.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface IGlobalLogger { - info(message: string, ...args: any[]): void; - debug(message: string, ...args: any[]): void; - error(message: string, ...args: any[]): void; -} diff --git a/themes/triangles/server/src/lib/logging/IRequestLogger.ts b/themes/triangles/server/src/lib/logging/IRequestLogger.ts deleted file mode 100644 index 126a601fe..000000000 --- a/themes/triangles/server/src/lib/logging/IRequestLogger.ts +++ /dev/null @@ -1,7 +0,0 @@ -import Express = require("express"); - -export interface IRequestLogger { - info(req: Express.Request, message: string, ...args: any[]): void; - debug(req: Express.Request, message: string, ...args: any[]): void; - error(req: Express.Request, message: string, ...args: any[]): void; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/logging/RequestLogger.ts b/themes/triangles/server/src/lib/logging/RequestLogger.ts deleted file mode 100644 index c45c66018..000000000 --- a/themes/triangles/server/src/lib/logging/RequestLogger.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { IRequestLogger } from "./IRequestLogger"; -import Util = require("util"); -import Express = require("express"); -import Winston = require("winston"); - -declare module "express" { - interface Request { - id: string; - } -} - -export class RequestLogger implements IRequestLogger { - private winston: typeof Winston; - - constructor(winston: typeof Winston) { - this.winston = winston; - } - - private formatHeader(req: Express.Request) { - const clientIP = req.ip; // The IP of the original client going through the proxy chain. - return Util.format("date='%s' method='%s', path='%s' requestId='%s' sessionId='%s' ip='%s'", - new Date(), req.method, req.path, req.id, req.sessionID, clientIP); - } - - private formatBody(message: string) { - return Util.format("message='%s'", message); - } - - private formatMessage(req: Express.Request, message: string) { - return Util.format("%s %s", this.formatHeader(req), - this.formatBody(message)); - } - - info(req: Express.Request, message: string, ...args: any[]): void { - this.winston.info(this.formatMessage(req, message), ...args); - } - - debug(req: Express.Request, message: string, ...args: any[]): void { - this.winston.debug(this.formatMessage(req, message), ...args); - } - - error(req: Express.Request, message: string, ...args: any[]): void { - this.winston.error(this.formatMessage(req, message), ...args); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/logging/RequestLoggerStub.spec.ts b/themes/triangles/server/src/lib/logging/RequestLoggerStub.spec.ts deleted file mode 100644 index b0e375210..000000000 --- a/themes/triangles/server/src/lib/logging/RequestLoggerStub.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { IRequestLogger } from "./IRequestLogger"; -import Sinon = require("sinon"); -import { RequestLogger } from "./RequestLogger"; -import Winston = require("winston"); -import Express = require("express"); - -export class RequestLoggerStub implements IRequestLogger { - infoStub: Sinon.SinonStub; - debugStub: Sinon.SinonStub; - errorStub: Sinon.SinonStub; - private requestLogger: RequestLogger; - - constructor(enableLogging?: boolean) { - this.infoStub = Sinon.stub(); - this.debugStub = Sinon.stub(); - this.errorStub = Sinon.stub(); - if (enableLogging) - this.requestLogger = new RequestLogger(Winston); - } - - info(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.infoStub(req, message, ...args); - } - - debug(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.debugStub(req, message, ...args); - } - - error(req: Express.Request, message: string, ...args: any[]): void { - if (this.requestLogger) - this.requestLogger.info(req, message, ...args); - this.errorStub(req, message, ...args); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/notifiers/AbstractEmailNotifier.ts b/themes/triangles/server/src/lib/notifiers/AbstractEmailNotifier.ts deleted file mode 100644 index 198e4e5db..000000000 --- a/themes/triangles/server/src/lib/notifiers/AbstractEmailNotifier.ts +++ /dev/null @@ -1,23 +0,0 @@ - -import { INotifier } from "../notifiers/INotifier"; -import { Identity } from "../../../types/Identity"; - -import Fs = require("fs"); -import Path = require("path"); -import Ejs = require("ejs"); -import BluebirdPromise = require("bluebird"); - -const email_template = Fs.readFileSync(Path.join(__dirname, "../../resources/email-template.ejs"), "UTF-8"); - -export abstract class AbstractEmailNotifier implements INotifier { - notify(to: string, subject: string, link: string): BluebirdPromise { - const d = { - url: link, - button_title: "Continue", - title: subject - }; - return this.sendEmail(to, subject, Ejs.render(email_template, d)); - } - - abstract sendEmail(to: string, subject: string, content: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/notifiers/EmailNotifier.spec.ts b/themes/triangles/server/src/lib/notifiers/EmailNotifier.spec.ts deleted file mode 100644 index 8211bbc02..000000000 --- a/themes/triangles/server/src/lib/notifiers/EmailNotifier.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as sinon from "sinon"; -import * as Assert from "assert"; -import BluebirdPromise = require("bluebird"); - -import { MailSenderStub } from "./MailSenderStub.spec"; -import EmailNotifier = require("./EmailNotifier"); - - -describe("notifiers/EmailNotifier", function () { - it("should send an email to given user", function () { - const mailSender = new MailSenderStub(); - const options = { - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }; - - mailSender.sendStub.returns(BluebirdPromise.resolve()); - const sender = new EmailNotifier.EmailNotifier(options, mailSender); - const subject = "subject"; - const url = "http://test.com"; - - return sender.notify("user@example.com", subject, url) - .then(function () { - Assert.equal(mailSender.sendStub.getCall(0).args[0].to, "user@example.com"); - Assert.equal(mailSender.sendStub.getCall(0).args[0].subject, "subject"); - return BluebirdPromise.resolve(); - }); - }); - - it("should fail while sending an email", function () { - const mailSender = new MailSenderStub(); - const options = { - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }; - - mailSender.sendStub.returns(BluebirdPromise.reject(new Error("Failed to send mail"))); - const sender = new EmailNotifier.EmailNotifier(options, mailSender); - const subject = "subject"; - const url = "http://test.com"; - - return sender.notify("user@example.com", subject, url) - .then(function () { - return BluebirdPromise.reject(new Error()); - }, function() { - Assert.equal(mailSender.sendStub.getCall(0).args[0].from, "admin@example.com"); - return BluebirdPromise.resolve(); - }); - }); -}); diff --git a/themes/triangles/server/src/lib/notifiers/EmailNotifier.ts b/themes/triangles/server/src/lib/notifiers/EmailNotifier.ts deleted file mode 100644 index 4df7c861e..000000000 --- a/themes/triangles/server/src/lib/notifiers/EmailNotifier.ts +++ /dev/null @@ -1,27 +0,0 @@ - -import * as BluebirdPromise from "bluebird"; - -import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier"; -import { EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; -import { IMailSender } from "./IMailSender"; - -export class EmailNotifier extends AbstractEmailNotifier { - private mailSender: IMailSender; - private sender: string; - - constructor(options: EmailNotifierConfiguration, mailSender: IMailSender) { - super(); - this.mailSender = mailSender; - this.sender = options.sender; - } - - sendEmail(to: string, subject: string, content: string) { - const mailOptions = { - from: this.sender, - to: to, - subject: subject, - html: content - }; - return this.mailSender.send(mailOptions); - } -} diff --git a/themes/triangles/server/src/lib/notifiers/FileSystemNotifier.ts b/themes/triangles/server/src/lib/notifiers/FileSystemNotifier.ts deleted file mode 100644 index 23f6242c4..000000000 --- a/themes/triangles/server/src/lib/notifiers/FileSystemNotifier.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as BluebirdPromise from "bluebird"; -import * as util from "util"; -import * as Fs from "fs"; -import { INotifier } from "./INotifier"; -import { Identity } from "../../../types/Identity"; - -import { FileSystemNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class FileSystemNotifier implements INotifier { - private filename: string; - - constructor(options: FileSystemNotifierConfiguration) { - this.filename = options.filename; - } - - notify(to: string, subject: string, link: string): BluebirdPromise { - const content = util.format("Date: %s\nEmail: %s\nSubject: %s\nLink: %s", - new Date().toString(), to, subject, link); - const writeFilePromised: any = BluebirdPromise.promisify(Fs.writeFile); - return writeFilePromised(this.filename, content); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/notifiers/IMailSender.ts b/themes/triangles/server/src/lib/notifiers/IMailSender.ts deleted file mode 100644 index 34ac464a8..000000000 --- a/themes/triangles/server/src/lib/notifiers/IMailSender.ts +++ /dev/null @@ -1,6 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); - -export interface IMailSender { - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/notifiers/IMailSenderBuilder.ts b/themes/triangles/server/src/lib/notifiers/IMailSenderBuilder.ts deleted file mode 100644 index 36d4dcdf7..000000000 --- a/themes/triangles/server/src/lib/notifiers/IMailSenderBuilder.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export interface IMailSenderBuilder { - buildEmail(options: EmailNotifierConfiguration): IMailSender; - buildSmtp(options: SmtpNotifierConfiguration): IMailSender; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/notifiers/INotifier.ts b/themes/triangles/server/src/lib/notifiers/INotifier.ts deleted file mode 100644 index b9a6b138c..000000000 --- a/themes/triangles/server/src/lib/notifiers/INotifier.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as BluebirdPromise from "bluebird"; - -export interface INotifier { - notify(to: string, subject: string, link: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/notifiers/MailSender.ts b/themes/triangles/server/src/lib/notifiers/MailSender.ts deleted file mode 100644 index 536a88e63..000000000 --- a/themes/triangles/server/src/lib/notifiers/MailSender.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import Nodemailer = require("nodemailer"); -import NodemailerDirectTransport = require("nodemailer-direct-transport"); -import NodemailerSmtpTransport = require("nodemailer-smtp-transport"); -import BluebirdPromise = require("bluebird"); - -export class MailSender implements IMailSender { - private transporter: Nodemailer.Transporter; - - constructor(options: NodemailerDirectTransport.DirectOptions | - NodemailerSmtpTransport.SmtpOptions, nodemailer: typeof Nodemailer) { - this.transporter = nodemailer.createTransport(options); - } - - verify(): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - that.transporter.verify(function (error: Error, success: any) { - if (error) { - reject(new Error("Unable to connect to SMTP server. \ - Please check the service is running and your credentials are correct.")); - return; - } - resolve(); - }); - }); - } - - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - that.transporter.sendMail(mailOptions, (error: Error, - data: Nodemailer.SentMessageInfo) => { - if (error) { - reject(new Error("Error while sending email: " + error.message)); - return; - } - resolve(); - }); - }); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/notifiers/MailSenderBuilder.spec.ts b/themes/triangles/server/src/lib/notifiers/MailSenderBuilder.spec.ts deleted file mode 100644 index 41e0db426..000000000 --- a/themes/triangles/server/src/lib/notifiers/MailSenderBuilder.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ - -import { MailSenderBuilder } from ".//MailSenderBuilder"; -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); -import Assert = require("assert"); - -describe("notifiers/MailSenderBuilder", function() { - let createTransportStub: Sinon.SinonStub; - beforeEach(function() { - createTransportStub = Sinon.stub(Nodemailer, "createTransport"); - }); - - afterEach(function() { - createTransportStub.restore(); - }); - - it("should create a email mail sender", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildEmail({ - username: "user_gmail", - password: "pass_gmail", - sender: "admin@example.com", - service: "gmail" - }); - Assert.equal(createTransportStub.getCall(0).args[0].auth.user, "user_gmail"); - Assert.equal(createTransportStub.getCall(0).args[0].auth.pass, "pass_gmail"); - Assert.equal(createTransportStub.getCall(0).args[0].service, "gmail"); - }); - - describe("build smtp mail sender", function() { - it("should create a smtp mail sender with authenticated user", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildSmtp({ - host: "mail.example.com", - password: "password", - port: 25, - secure: true, - username: "user", - sender: "admin@example.com" - }); - Assert.deepStrictEqual(createTransportStub.getCall(0).args[0], { - host: "mail.example.com", - auth: { - pass: "password", - user: "user" - }, - port: 25, - secure: true, - }); - }); - - it("should create a smtp mail sender with anonymous user", function() { - const mailSenderBuilder = new MailSenderBuilder(Nodemailer); - mailSenderBuilder.buildSmtp({ - host: "mail.example.com", - port: 25, - secure: true, - sender: "admin@example.com" - }); - Assert.deepStrictEqual(createTransportStub.getCall(0).args[0], { - host: "mail.example.com", - port: 25, - secure: true, - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/notifiers/MailSenderBuilder.ts b/themes/triangles/server/src/lib/notifiers/MailSenderBuilder.ts deleted file mode 100644 index 1d06be52f..000000000 --- a/themes/triangles/server/src/lib/notifiers/MailSenderBuilder.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { IMailSender } from "./IMailSender"; -import { IMailSenderBuilder } from "./IMailSenderBuilder"; -import { MailSender } from "./MailSender"; -import Nodemailer = require("nodemailer"); -import NodemailerSmtpTransport = require("nodemailer-smtp-transport"); -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class MailSenderBuilder implements IMailSenderBuilder { - private nodemailer: typeof Nodemailer; - - constructor(nodemailer: typeof Nodemailer) { - this.nodemailer = nodemailer; - } - - buildEmail(options: EmailNotifierConfiguration): IMailSender { - const emailOptions = { - service: options.service, - auth: { - user: options.username, - pass: options.password - } - }; - return new MailSender(emailOptions, this.nodemailer); - } - - buildSmtp(options: SmtpNotifierConfiguration): IMailSender { - const smtpOptions: NodemailerSmtpTransport.SmtpOptions = { - host: options.host, - port: options.port, - secure: options.secure, // upgrade later with STARTTLS - }; - - if (options.username && options.password) { - smtpOptions.auth = { - user: options.username, - pass: options.password - }; - } - - return new MailSender(smtpOptions, this.nodemailer); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts b/themes/triangles/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts deleted file mode 100644 index 5b76f6e56..000000000 --- a/themes/triangles/server/src/lib/notifiers/MailSenderBuilderStub.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { IMailSenderBuilder } from "../../../src/lib/notifiers/IMailSenderBuilder"; -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); -import { IMailSender } from "../../../src/lib/notifiers/IMailSender"; -import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../../../src/lib/configuration/schema/NotifierConfiguration"; - -export class MailSenderBuilderStub implements IMailSenderBuilder { - buildEmailStub: Sinon.SinonStub; - buildSmtpStub: Sinon.SinonStub; - - constructor() { - this.buildEmailStub = Sinon.stub(); - this.buildSmtpStub = Sinon.stub(); - } - - buildEmail(options: EmailNotifierConfiguration): IMailSender { - return this.buildEmailStub(options); - } - - buildSmtp(options: SmtpNotifierConfiguration): IMailSender { - return this.buildSmtpStub(options); - } - -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/notifiers/MailSenderStub.spec.ts b/themes/triangles/server/src/lib/notifiers/MailSenderStub.spec.ts deleted file mode 100644 index d57c458fc..000000000 --- a/themes/triangles/server/src/lib/notifiers/MailSenderStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { IMailSender } from "../../../src/lib/notifiers/IMailSender"; -import BluebirdPromise = require("bluebird"); -import Nodemailer = require("nodemailer"); -import Sinon = require("sinon"); - -export class MailSenderStub implements IMailSender { - sendStub: Sinon.SinonStub; - - constructor() { - this.sendStub = Sinon.stub(); - } - - send(mailOptions: Nodemailer.SendMailOptions): BluebirdPromise { - return this.sendStub(mailOptions); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/notifiers/NotifierFactory.spec.ts b/themes/triangles/server/src/lib/notifiers/NotifierFactory.spec.ts deleted file mode 100644 index f15e7667f..000000000 --- a/themes/triangles/server/src/lib/notifiers/NotifierFactory.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ - -import * as sinon from "sinon"; -import * as BluebirdPromise from "bluebird"; -import * as assert from "assert"; - -import { NotifierFactory } from "./NotifierFactory"; -import { EmailNotifier } from "./EmailNotifier"; -import { SmtpNotifier } from "./SmtpNotifier"; -import { MailSenderBuilderStub } from "./MailSenderBuilderStub.spec"; - - -describe("notifiers/NotifierFactory", function () { - let mailSenderBuilderStub: MailSenderBuilderStub; - it("should build a Email Notifier", function () { - const options = { - email: { - username: "abc", - password: "password", - sender: "admin@example.com", - service: "gmail" - } - }; - mailSenderBuilderStub = new MailSenderBuilderStub(); - assert(NotifierFactory.build(options, mailSenderBuilderStub) instanceof EmailNotifier); - }); - - it("should build a SMTP Notifier", function () { - const options = { - smtp: { - username: "user", - password: "pass", - secure: true, - host: "localhost", - port: 25, - sender: "admin@example.com" - } - }; - - mailSenderBuilderStub = new MailSenderBuilderStub(); - assert(NotifierFactory.build(options, mailSenderBuilderStub) instanceof SmtpNotifier); - }); -}); diff --git a/themes/triangles/server/src/lib/notifiers/NotifierFactory.ts b/themes/triangles/server/src/lib/notifiers/NotifierFactory.ts deleted file mode 100644 index a89155feb..000000000 --- a/themes/triangles/server/src/lib/notifiers/NotifierFactory.ts +++ /dev/null @@ -1,33 +0,0 @@ - -import { NotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; -import Nodemailer = require("nodemailer"); -import { INotifier } from "./INotifier"; - -import { FileSystemNotifier } from "./FileSystemNotifier"; -import { EmailNotifier } from "./EmailNotifier"; -import { SmtpNotifier } from "./SmtpNotifier"; -import { IMailSender } from "./IMailSender"; -import { IMailSenderBuilder } from "./IMailSenderBuilder"; - -export class NotifierFactory { - static build(options: NotifierConfiguration, mailSenderBuilder: IMailSenderBuilder): INotifier { - if ("email" in options) { - const mailSender = mailSenderBuilder.buildEmail(options.email); - return new EmailNotifier(options.email, mailSender); - } - else if ("smtp" in options) { - const mailSender = mailSenderBuilder.buildSmtp(options.smtp); - return new SmtpNotifier(options.smtp, mailSender); - } - else if ("filesystem" in options) { - return new FileSystemNotifier(options.filesystem); - } - else { - throw new Error("No available notifier option detected."); - } - } -} - - - - diff --git a/themes/triangles/server/src/lib/notifiers/NotifierStub.spec.ts b/themes/triangles/server/src/lib/notifiers/NotifierStub.spec.ts deleted file mode 100644 index f99231b50..000000000 --- a/themes/triangles/server/src/lib/notifiers/NotifierStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); - -import { INotifier } from "./INotifier"; - -export class NotifierStub implements INotifier { - notifyStub: Sinon.SinonStub; - - constructor() { - this.notifyStub = Sinon.stub(); - } - - notify(to: string, subject: string, link: string): BluebirdPromise { - return this.notifyStub(to, subject, link); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/notifiers/SmtpNotifier.ts b/themes/triangles/server/src/lib/notifiers/SmtpNotifier.ts deleted file mode 100644 index f93a6d4a0..000000000 --- a/themes/triangles/server/src/lib/notifiers/SmtpNotifier.ts +++ /dev/null @@ -1,30 +0,0 @@ - - -import * as BluebirdPromise from "bluebird"; - -import { IMailSender } from "./IMailSender"; -import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier"; -import { SmtpNotifierConfiguration } from "../configuration/schema/NotifierConfiguration"; - -export class SmtpNotifier extends AbstractEmailNotifier { - private mailSender: IMailSender; - private sender: string; - - constructor(options: SmtpNotifierConfiguration, - mailSender: IMailSender) { - super(); - this.mailSender = mailSender; - this.sender = options.sender; - } - - sendEmail(to: string, subject: string, content: string) { - const mailOptions = { - from: this.sender, - to: to, - subject: subject, - html: content - }; - const that = this; - return this.mailSender.send(mailOptions); - } -} diff --git a/themes/triangles/server/src/lib/regulation/IRegulator.ts b/themes/triangles/server/src/lib/regulation/IRegulator.ts deleted file mode 100644 index c49425b24..000000000 --- a/themes/triangles/server/src/lib/regulation/IRegulator.ts +++ /dev/null @@ -1,6 +0,0 @@ -import BluebirdPromise = require("bluebird"); - -export interface IRegulator { - mark(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise; - regulate(userId: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/regulation/Regulator.spec.ts b/themes/triangles/server/src/lib/regulation/Regulator.spec.ts deleted file mode 100644 index f9c6e6086..000000000 --- a/themes/triangles/server/src/lib/regulation/Regulator.spec.ts +++ /dev/null @@ -1,186 +0,0 @@ - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); - -import { Regulator } from "./Regulator"; -import MockDate = require("mockdate"); -import exceptions = require("../Exceptions"); -import { UserDataStoreStub } from "../storage/UserDataStoreStub.spec"; - -describe("regulation/Regulator", function () { - const USER1 = "USER1"; - const USER2 = "USER2"; - let userDataStoreStub: UserDataStoreStub; - - beforeEach(function () { - userDataStoreStub = new UserDataStoreStub(); - const dataStore: { [userId: string]: { userId: string, date: Date, isAuthenticationSuccessful: boolean }[] } = { - [USER1]: [], - [USER2]: [] - }; - - userDataStoreStub.saveAuthenticationTraceStub.callsFake(function (userId, isAuthenticationSuccessful) { - dataStore[userId].unshift({ - userId: userId, - date: new Date(), - isAuthenticationSuccessful: isAuthenticationSuccessful, - }); - return BluebirdPromise.resolve(); - }); - - userDataStoreStub.retrieveLatestAuthenticationTracesStub.callsFake(function (userId, count) { - const ret = (dataStore[userId].length <= count) ? dataStore[userId] : dataStore[userId].slice(0, 3); - return BluebirdPromise.resolve(ret); - }); - }); - - afterEach(function () { - MockDate.reset(); - }); - - function markAuthenticationAt(regulator: Regulator, user: string, time: string, success: boolean) { - MockDate.set(time); - return regulator.mark(user, success); - } - - it("should mark 2 authentication and regulate (accept)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 10, 10); - - return regulator.mark(USER1, false) - .then(function () { - return regulator.mark(USER1, true); - }) - .then(function () { - return regulator.regulate(USER1); - }); - }); - - it("should mark 3 authentications and regulate (reject)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 10, 10); - - return regulator.mark(USER1, false) - .then(function () { - return regulator.mark(USER1, false); - }) - .then(function () { - return regulator.mark(USER1, false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { return BluebirdPromise.reject(new Error("should not be here!")); }) - .catch(exceptions.AuthenticationRegulationError, function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should mark 1 failed, 1 successful and 1 failed authentications within minimum time and regulate (accept)", function () { - const regulator = new Regulator(userDataStoreStub, 3, 60, 30); - - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:10", true); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:20", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:30", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:39", false); - }) - .then(function () { - return regulator.regulate(USER1); - }) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here!")); - }, - function () { - return BluebirdPromise.resolve(); - }); - }); - - it("should regulate user if number of failures is greater than 3 in allowed time lapse", function () { - function markAuthentications(regulator: Regulator, user: string) { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:45", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:01:05", false); - }) - .then(function () { - return regulator.regulate(user); - }); - } - - const regulator1 = new Regulator(userDataStoreStub, 3, 60, 60); - const regulator2 = new Regulator(userDataStoreStub, 3, 2 * 60, 60); - - const p1 = markAuthentications(regulator1, USER1); - const p2 = markAuthentications(regulator2, USER2); - - return BluebirdPromise.join(p1, p2) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here...")); - }, function () { - Assert(p1.isFulfilled()); - Assert(p2.isRejected()); - }); - }); - - it("should user wait after regulation to authenticate again", function () { - function markAuthentications(regulator: Regulator, user: string) { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:10", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:15", false); - }) - .then(function () { - return markAuthenticationAt(regulator, user, "1/2/2000 00:00:25", false); - }) - .then(function () { - MockDate.set("1/2/2000 00:00:54"); - return regulator.regulate(user); - }) - .then(function () { - return BluebirdPromise.reject(new Error("should fail at this time")); - }, function () { - MockDate.set("1/2/2000 00:00:56"); - return regulator.regulate(user); - }); - } - - const regulator = new Regulator(userDataStoreStub, 4, 30, 30); - return markAuthentications(regulator, USER1); - }); - - it("should disable regulation when max_retries is set to 0", function () { - const maxRetries = 0; - const regulator = new Regulator(userDataStoreStub, maxRetries, 60, 30); - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:00", false) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:10", false); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:15", false); - }) - .then(function () { - return markAuthenticationAt(regulator, USER1, "1/2/2000 00:00:25", false); - }) - .then(function () { - MockDate.set("1/2/2000 00:00:26"); - return regulator.regulate(USER1); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/regulation/Regulator.ts b/themes/triangles/server/src/lib/regulation/Regulator.ts deleted file mode 100644 index 1037a6a17..000000000 --- a/themes/triangles/server/src/lib/regulation/Regulator.ts +++ /dev/null @@ -1,55 +0,0 @@ - -import * as BluebirdPromise from "bluebird"; -import exceptions = require("../Exceptions"); -import { IUserDataStore } from "../storage/IUserDataStore"; -import { AuthenticationTraceDocument } from "../storage/AuthenticationTraceDocument"; -import { IRegulator } from "./IRegulator"; - -export class Regulator implements IRegulator { - private userDataStore: IUserDataStore; - private banTime: number; - private findTime: number; - private maxRetries: number; - - constructor(userDataStore: any, maxRetries: number, findTime: number, banTime: number) { - this.userDataStore = userDataStore; - this.banTime = banTime; - this.findTime = findTime; - this.maxRetries = maxRetries; - } - - // Mark authentication - mark(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - return this.userDataStore.saveAuthenticationTrace(userId, isAuthenticationSuccessful); - } - - regulate(userId: string): BluebirdPromise { - const that = this; - - if (that.maxRetries <= 0) return BluebirdPromise.resolve(); - - return this.userDataStore.retrieveLatestAuthenticationTraces(userId, that.maxRetries) - .then((docs: AuthenticationTraceDocument[]) => { - // less than the max authorized number of authentication in time range, thus authorizing access - if (docs.length < that.maxRetries) return BluebirdPromise.resolve(); - - const numberOfFailedAuth = docs - .map(function (d: AuthenticationTraceDocument) { return d.isAuthenticationSuccessful == false ? 1 : 0; }) - .reduce(function (acc, v) { return acc + v; }, 0); - - if (numberOfFailedAuth < this.maxRetries) return BluebirdPromise.resolve(); - - const newestDocument = docs[0]; - const oldestDocument = docs[that.maxRetries - 1]; - - const authenticationsTimeRangeInSeconds = (newestDocument.date.getTime() - oldestDocument.date.getTime()) / 1000; - const tooManyAuthInTimelapse = (authenticationsTimeRangeInSeconds < this.findTime); - const stillInBannedTimeRange = (new Date(new Date().getTime() - this.banTime * 1000) < newestDocument.date); - - if (tooManyAuthInTimelapse && stillInBannedTimeRange) - throw new exceptions.AuthenticationRegulationError("Max number of authentication. Please retry in few minutes."); - - return BluebirdPromise.resolve(); - }); - } -} diff --git a/themes/triangles/server/src/lib/regulation/RegulatorStub.spec.ts b/themes/triangles/server/src/lib/regulation/RegulatorStub.spec.ts deleted file mode 100644 index ca8a00fb1..000000000 --- a/themes/triangles/server/src/lib/regulation/RegulatorStub.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Bluebird = require("bluebird"); -import Sinon = require("sinon"); -import { IRegulator } from "./IRegulator"; - - -export class RegulatorStub implements IRegulator { - markStub: Sinon.SinonStub; - regulateStub: Sinon.SinonStub; - - constructor() { - this.markStub = Sinon.stub(); - this.regulateStub = Sinon.stub(); - } - - mark(userId: string, isAuthenticationSuccessful: boolean): Bluebird { - return this.markStub(userId, isAuthenticationSuccessful); - } - - regulate(userId: string): Bluebird { - return this.regulateStub(userId); - } -} diff --git a/themes/triangles/server/src/lib/routes/error/401/get.spec.ts b/themes/triangles/server/src/lib/routes/error/401/get.spec.ts deleted file mode 100644 index 9fdac9c3c..000000000 --- a/themes/triangles/server/src/lib/routes/error/401/get.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get401 from "./get"; -import { ServerVariables } from "../../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../../ServerVariablesMockBuilder.spec"; - -describe("routes/error/401/get", function () { - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let req: any; - let res: any; - let renderSpy: Sinon.SinonSpy; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - - renderSpy = Sinon.spy(); - req = { - headers: {} - }; - res = { - render: renderSpy - }; - }); - - it("should set redirection url to the default redirection url", function () { - vars.config.default_redirection_url = "http://default-redirection"; - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: "http://default-redirection" - })); - }); - }); - - it("should set redirection url to the referer", function () { - req.headers["referer"] = "http://redirection"; - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: "http://redirection" - })); - }); - }); - - it("should render without redirecting the user", function () { - return Get401(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/401", { - redirection_url: undefined - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/error/401/get.ts b/themes/triangles/server/src/lib/routes/error/401/get.ts deleted file mode 100644 index ca4a3963d..000000000 --- a/themes/triangles/server/src/lib/routes/error/401/get.ts +++ /dev/null @@ -1,15 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import redirector from "../redirector"; -import { ServerVariables } from "../../../ServerVariables"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - const redirectionUrl = redirector(req, vars); - res.render("errors/401", { - redirection_url: redirectionUrl - }); - return BluebirdPromise.resolve(); - }; -} diff --git a/themes/triangles/server/src/lib/routes/error/403/get.spec.ts b/themes/triangles/server/src/lib/routes/error/403/get.spec.ts deleted file mode 100644 index 22eb84853..000000000 --- a/themes/triangles/server/src/lib/routes/error/403/get.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get403 from "./get"; -import { ServerVariables } from "../../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../../ServerVariablesMockBuilder.spec"; - -describe("routes/error/403/get", function () { - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let req: any; - let res: any; - let renderSpy: Sinon.SinonSpy; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - - renderSpy = Sinon.spy(); - req = { - headers: {} - }; - res = { - render: renderSpy - }; - }); - - it("should set redirection url to the default redirection url", function () { - vars.config.default_redirection_url = "http://default-redirection"; - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: "http://default-redirection" - })); - }); - }); - - it("should set redirection url to the referer", function () { - req.headers["referer"] = "http://redirection"; - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: "http://redirection" - })); - }); - }); - - it("should render without redirecting the user", function () { - return Get403(vars)(req, res as any) - .then(function () { - Assert(renderSpy.calledOnce); - Assert(renderSpy.calledWithExactly("errors/403", { - redirection_url: undefined - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/error/403/get.ts b/themes/triangles/server/src/lib/routes/error/403/get.ts deleted file mode 100644 index 3ab0319e5..000000000 --- a/themes/triangles/server/src/lib/routes/error/403/get.ts +++ /dev/null @@ -1,15 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import redirector from "../redirector"; -import { ServerVariables } from "../../../ServerVariables"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - const redirectionUrl = redirector(req, vars); - res.render("errors/403", { - redirection_url: redirectionUrl - }); - return BluebirdPromise.resolve(); - }; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/error/404/get.spec.ts b/themes/triangles/server/src/lib/routes/error/404/get.spec.ts deleted file mode 100644 index 73e4e6cef..000000000 --- a/themes/triangles/server/src/lib/routes/error/404/get.spec.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Sinon = require("sinon"); -import Express = require("express"); -import Assert = require("assert"); -import Get404 from "./get"; - -describe("routes/error/404/get", function () { - it("should render the page", function () { - const req = {} as Express.Request; - const res = { - render: Sinon.stub() - }; - - return Get404(req, res as any) - .then(function () { - Assert(res.render.calledOnce); - Assert(res.render.calledWith("errors/404")); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/error/404/get.ts b/themes/triangles/server/src/lib/routes/error/404/get.ts deleted file mode 100644 index 6693b6fc8..000000000 --- a/themes/triangles/server/src/lib/routes/error/404/get.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); - -export default function (req: express.Request, res: express.Response): BluebirdPromise { - res.render("errors/404"); - return BluebirdPromise.resolve(); -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/error/redirector.ts b/themes/triangles/server/src/lib/routes/error/redirector.ts deleted file mode 100644 index b1a3ccc11..000000000 --- a/themes/triangles/server/src/lib/routes/error/redirector.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Express = require("express"); -import { ServerVariables } from "../../ServerVariables"; - -export default function (req: Express.Request, vars: ServerVariables): string { - let redirectionUrl: string; - - if (req.headers && req.headers["referer"]) - redirectionUrl = "" + req.headers["referer"]; - else if (vars.config.default_redirection_url) - redirectionUrl = vars.config.default_redirection_url; - - return redirectionUrl; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/firstfactor/get.ts b/themes/triangles/server/src/lib/routes/firstfactor/get.ts deleted file mode 100644 index d94f656c8..000000000 --- a/themes/triangles/server/src/lib/routes/firstfactor/get.ts +++ /dev/null @@ -1,72 +0,0 @@ - -import express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import Util = require("util"); -import { ServerVariables } from "../../ServerVariables"; -import { SafeRedirector } from "../../utils/SafeRedirection"; -import { Level } from "../../authentication/Level"; - -function getRedirectParam( - req: express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -function redirectToSecondFactorPage( - req: express.Request, - res: express.Response) { - - const redirectUrl = getRedirectParam(req); - if (!redirectUrl) - res.redirect(Endpoints.SECOND_FACTOR_GET); - else - res.redirect( - Util.format("%s?%s=%s", - Endpoints.SECOND_FACTOR_GET, - Constants.REDIRECT_QUERY_PARAM, - redirectUrl)); -} - -function redirectToService( - req: express.Request, - res: express.Response, - redirector: SafeRedirector) { - const redirectUrl = getRedirectParam(req); - if (!redirectUrl) { - res.redirect(Endpoints.LOGGED_IN); - } else { - redirector.redirectOrElse(res, redirectUrl, Endpoints.LOGGED_IN); - } -} - -function renderFirstFactor( - res: express.Response) { - - res.render("firstfactor", { - first_factor_post_endpoint: Endpoints.FIRST_FACTOR_POST, - reset_password_request_endpoint: Endpoints.RESET_PASSWORD_REQUEST_GET - }); -} - -export default function ( - vars: ServerVariables) { - - const redirector = new SafeRedirector(vars.config.session.domain); - return function (req: express.Request, res: express.Response): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (authSession.authentication_level == Level.ONE_FACTOR) { - redirectToSecondFactorPage(req, res); - } else if (authSession.authentication_level == Level.TWO_FACTOR) { - redirectToService(req, res, redirector); - } else { - renderFirstFactor(res); - } - resolve(); - }); - }; -} diff --git a/themes/triangles/server/src/lib/routes/firstfactor/post.spec.ts b/themes/triangles/server/src/lib/routes/firstfactor/post.spec.ts deleted file mode 100644 index e1d078cdd..000000000 --- a/themes/triangles/server/src/lib/routes/firstfactor/post.spec.ts +++ /dev/null @@ -1,136 +0,0 @@ - -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import FirstFactorPost = require("./post"); -import exceptions = require("../../Exceptions"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import Endpoints = require("../../../../../shared/api"); -import AuthenticationRegulatorMock = require("../../regulation/RegulatorStub.spec"); -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; - -describe("routes/firstfactor/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let emails: string[]; - let groups: string[]; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let authSession: AuthenticationSession; - - beforeEach(function () { - emails = ["test_ok@example.com"]; - groups = ["group1", "group2" ]; - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - mocks.authorizer.authorizationMock.returns(true); - mocks.regulator.regulateStub.returns(BluebirdPromise.resolve()); - mocks.regulator.markStub.returns(BluebirdPromise.resolve()); - - req = { - originalUrl: "/api/firstfactor", - body: { - username: "username", - password: "password" - }, - query: { - redirect: "http://redirect.url" - }, - session: { - cookie: {} - }, - headers: { - host: "home.example.com" - } - }; - - res = ExpressMock.ResponseMock(); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - it("should reply with 204 if success", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal("username", authSession.userid); - Assert(res.send.calledOnce); - }); - }); - - describe("keep me logged in", () => { - beforeEach(() => { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - req.body.keepMeLoggedIn = "true"; - return FirstFactorPost.default(vars)(req as any, res as any); - }); - - it("should set keep_me_logged_in session variable to true", function () { - Assert.equal(authSession.keep_me_logged_in, true); - }); - - it("should set cookie maxAge to one year", function () { - Assert.equal(req.session.cookie.maxAge, 365 * 24 * 60 * 60 * 1000); - }); - }); - - it("should retrieve email from LDAP", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve([{ mail: ["test@example.com"] }])); - return FirstFactorPost.default(vars)(req as any, res as any); - }); - - it("should set first email address as user session variable", function () { - const emails = ["test_ok@example.com"]; - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.resolve({ - emails: emails, - groups: groups - })); - - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal("test_ok@example.com", authSession.email); - }); - }); - - it("should return error message when LDAP authenticator throws", function () { - mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") - .returns(BluebirdPromise.reject(new exceptions.LdapBindError("Bad credentials"))); - - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.equal(mocks.regulator.markStub.getCall(0).args[0], "username"); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); - - it("should return error message when regulator rejects authentication", function () { - const err = new exceptions.AuthenticationRegulationError("Authentication regulation..."); - mocks.regulator.regulateStub.returns(BluebirdPromise.reject(err)); - return FirstFactorPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); -}); - - diff --git a/themes/triangles/server/src/lib/routes/firstfactor/post.ts b/themes/triangles/server/src/lib/routes/firstfactor/post.ts deleted file mode 100644 index 565681d6a..000000000 --- a/themes/triangles/server/src/lib/routes/firstfactor/post.ts +++ /dev/null @@ -1,101 +0,0 @@ - -import Exceptions = require("../../Exceptions"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import Endpoint = require("../../../../../shared/api"); -import ErrorReplies = require("../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import UserMessages = require("../../../../../shared/UserMessages"); -import { ServerVariables } from "../../ServerVariables"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import { GroupsAndEmails } from "../../authentication/backends/GroupsAndEmails"; -import { Level as AuthenticationLevel } from "../../authentication/Level"; -import { Level as AuthorizationLevel } from "../../authorization/Level"; -import { URLDecomposer } from "../../utils/URLDecomposer"; - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response) - : BluebirdPromise { - const username: string = req.body.username; - const password: string = req.body.password; - const keepMeLoggedIn: boolean = req.body.keepMeLoggedIn && - req.body.keepMeLoggedIn === "true"; - let authSession: AuthenticationSession; - - if (keepMeLoggedIn) { - // Stay connected for 1 year. - vars.logger.debug(req, "User requested to stay logged in for one year."); - req.session.cookie.maxAge = 365 * 24 * 60 * 60 * 1000; - } - - return BluebirdPromise.resolve() - .then(function () { - if (!username || !password) { - return BluebirdPromise.reject(new Error("No username or password.")); - } - vars.logger.info(req, "Starting authentication of user \"%s\"", username); - authSession = AuthenticationSessionHandler.get(req, vars.logger); - return vars.regulator.regulate(username); - }) - .then(function () { - vars.logger.info(req, "No regulation applied."); - return vars.usersDatabase.checkUserPassword(username, password); - }) - .then(function (groupsAndEmails: GroupsAndEmails) { - vars.logger.info(req, - "LDAP binding successful. Retrieved information about user are %s", - JSON.stringify(groupsAndEmails)); - authSession.userid = username; - authSession.keep_me_logged_in = keepMeLoggedIn; - authSession.authentication_level = AuthenticationLevel.ONE_FACTOR; - const redirectUrl: string = req.query[Constants.REDIRECT_QUERY_PARAM] !== "undefined" - // Fuck, don't know why it is a string! - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : ""; - - const emails: string[] = groupsAndEmails.emails; - const groups: string[] = groupsAndEmails.groups; - const decomposition = URLDecomposer.fromUrl(redirectUrl); - const authorizationLevel = (decomposition) - ? vars.authorizer.authorization( - {domain: decomposition.domain, resource: decomposition.path}, - {user: username, groups: groups}) - : AuthorizationLevel.TWO_FACTOR; - - if (emails.length > 0) - authSession.email = emails[0]; - authSession.groups = groups; - - vars.logger.debug(req, "Mark successful authentication to regulator."); - vars.regulator.mark(username, true); - - if (authorizationLevel <= AuthorizationLevel.ONE_FACTOR) { - let newRedirectionUrl: string = redirectUrl; - if (!newRedirectionUrl) - newRedirectionUrl = Endpoint.LOGGED_IN; - res.send({ - redirect: newRedirectionUrl - }); - vars.logger.debug(req, "Redirect to '%s'", redirectUrl); - } - else { - let newRedirectUrl = Endpoint.SECOND_FACTOR_GET; - if (redirectUrl) { - newRedirectUrl += "?" + Constants.REDIRECT_QUERY_PARAM + "=" - + redirectUrl; - } - vars.logger.debug(req, "Redirect to '%s'", newRedirectUrl); - res.send({ - redirect: newRedirectUrl - }); - } - return BluebirdPromise.resolve(); - }) - .catch(Exceptions.LdapBindError, function (err: Error) { - vars.regulator.mark(username, false); - return ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.OPERATION_FAILED)(err); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.OPERATION_FAILED)); - }; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/loggedin/get.ts b/themes/triangles/server/src/lib/routes/loggedin/get.ts deleted file mode 100644 index 283a041b1..000000000 --- a/themes/triangles/server/src/lib/routes/loggedin/get.ts +++ /dev/null @@ -1,23 +0,0 @@ -import Express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; -import ErrorReplies = require("../../ErrorReplies"); - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - res.render("already-logged-in", { - logout_endpoint: Endpoints.LOGOUT_GET, - username: authSession.userid, - redirection_url: vars.config.default_redirection_url - }); - resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); - } - - return handler; -} diff --git a/themes/triangles/server/src/lib/routes/logout/get.ts b/themes/triangles/server/src/lib/routes/logout/get.ts deleted file mode 100644 index 4d5112146..000000000 --- a/themes/triangles/server/src/lib/routes/logout/get.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import express = require("express"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Constants = require("../../../../../shared/constants"); -import { ServerVariables } from "../../ServerVariables"; - -function getRedirectParam(req: express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -export default function (vars: ServerVariables) { - return function(req: express.Request, res: express.Response) { - const redirect_param = getRedirectParam(req); - const redirect_url = redirect_param || "/"; - AuthenticationSessionHandler.reset(req); - res.redirect(redirect_url); - }; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/password-reset/constants.ts b/themes/triangles/server/src/lib/routes/password-reset/constants.ts deleted file mode 100644 index 5c639e92a..000000000 --- a/themes/triangles/server/src/lib/routes/password-reset/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export const CHALLENGE = "reset-password"; \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/password-reset/form/post.spec.ts b/themes/triangles/server/src/lib/routes/password-reset/form/post.spec.ts deleted file mode 100644 index ed029c906..000000000 --- a/themes/triangles/server/src/lib/routes/password-reset/form/post.spec.ts +++ /dev/null @@ -1,122 +0,0 @@ - -import PasswordResetFormPost = require("./post"); -import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../types/AuthenticationSession"; -import { UserDataStore } from "../../../storage/UserDataStore"; -import Sinon = require("sinon"); -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../ServerVariables"; -import { Level } from "../../../authentication/Level"; - -describe("routes/password-reset/form/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = { - originalUrl: "/api/password-reset", - body: { - userid: "user" - }, - session: {}, - headers: { - host: "localhost" - } - }; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - - mocks.config.authentication_backend.ldap = { - url: "ldap://ldapjs", - mail_attribute: "mail", - user: "user", - password: "password", - additional_users_dn: "ou=users", - additional_groups_dn: "ou=groups", - base_dn: "dc=example,dc=com", - users_filter: "user", - group_name_attribute: "cn", - groups_filter: "groups" - }; - - res = ExpressMock.ResponseMock(); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - authSession.userid = "user"; - authSession.email = "user@example.com"; - authSession.authentication_level = Level.ONE_FACTOR; - }); - - describe("test reset password post", () => { - it("should update the password and reset auth_session for reauthentication", function () { - req.body = {}; - req.body.password = "new-password"; - - mocks.usersDatabase.updatePasswordStub.returns(BluebirdPromise.resolve()); - - authSession.identity_check = { - userid: "user", - challenge: "reset-password" - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }).then(function (_authSession) { - Assert.equal(res.status.getCall(0).args[0], 204); - Assert.equal(_authSession.authentication_level, Level.NOT_AUTHENTICATED); - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if identity_challenge does not exist", function () { - authSession.identity_check = { - userid: "user", - challenge: undefined - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "An error occurred during password reset. Your password has not been changed." - }); - }); - }); - - it("should fail when ldap fails", function () { - req.body = {}; - req.body.password = "new-password"; - - mocks.usersDatabase.updatePasswordStub - .returns(BluebirdPromise.reject("Internal error with LDAP")); - - authSession.identity_check = { - challenge: "reset-password", - userid: "user" - }; - return PasswordResetFormPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "An error occurred during password reset. Your password has not been changed." - }); - return BluebirdPromise.resolve(); - }); - }); - }); -}); diff --git a/themes/triangles/server/src/lib/routes/password-reset/form/post.ts b/themes/triangles/server/src/lib/routes/password-reset/form/post.ts deleted file mode 100644 index fccd7471b..000000000 --- a/themes/triangles/server/src/lib/routes/password-reset/form/post.ts +++ /dev/null @@ -1,50 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); -import exceptions = require("../../../Exceptions"); -import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../types/AuthenticationSession"; -import ErrorReplies = require("../../../ErrorReplies"); -import UserMessages = require("../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../ServerVariables"; - -import Constants = require("./../constants"); - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const newPassword = objectPath.get(req, "body.password"); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.identity_check) { - reject(new Error("No identity check initiated")); - return; - } - - vars.logger.info(req, "User %s wants to reset his/her password.", - authSession.identity_check.userid); - vars.logger.debug(req, "Challenge %s", authSession.identity_check.challenge); - - if (authSession.identity_check.challenge != Constants.CHALLENGE) { - reject(new Error("Bad challenge.")); - return; - } - resolve(); - }) - .then(function () { - return vars.usersDatabase.updatePassword(authSession.identity_check.userid, newPassword); - }) - .then(function () { - vars.logger.info(req, "Password reset for user '%s'", - authSession.identity_check.userid); - AuthenticationSessionHandler.reset(req); - res.status(204); - res.send(); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.RESET_PASSWORD_FAILED)); - }; -} diff --git a/themes/triangles/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts b/themes/triangles/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts deleted file mode 100644 index ac6a41754..000000000 --- a/themes/triangles/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts +++ /dev/null @@ -1,92 +0,0 @@ - -import PasswordResetHandler - from "./PasswordResetHandler"; -import { UserDataStore } from "../../../storage/UserDataStore"; -import Sinon = require("sinon"); -import winston = require("winston"); -import assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../ServerVariables"; - -describe("routes/password-reset/identity/PasswordResetHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = { - originalUrl: "/non-api/xxx", - query: { - userid: "user" - }, - session: { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false - } - }, - headers: { - host: "localhost" - } - }; - - const options = { - inMemoryOnly: true - }; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - mocks.userDataStore.saveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - res = ExpressMock.ResponseMock(); - }); - - describe("test reset password identity pre check", () => { - it("should fail when no userid is provided", function () { - req.query.userid = undefined; - const handler = new PasswordResetHandler(vars.logger, - vars.usersDatabase); - return handler.preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject("It should fail"); - }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if ldap fail", function () { - mocks.usersDatabase.getEmailsStub - .returns(BluebirdPromise.reject("Internal error")); - new PasswordResetHandler(vars.logger, vars.usersDatabase) - .preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should returns identity when ldap replies", function () { - mocks.usersDatabase.getEmailsStub - .returns(BluebirdPromise.resolve(["test@example.com"])); - return new PasswordResetHandler(vars.logger, vars.usersDatabase) - .preValidationInit(req as any); - }); - }); -}); diff --git a/themes/triangles/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts b/themes/triangles/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts deleted file mode 100644 index 42ae92cda..000000000 --- a/themes/triangles/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts +++ /dev/null @@ -1,69 +0,0 @@ -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); - -import exceptions = require("../../../Exceptions"); -import { Identity } from "../../../../../types/Identity"; -import { IdentityValidable } from "../../../IdentityValidable"; -import { PRE_VALIDATION_TEMPLATE } from "../../../IdentityCheckPreValidationTemplate"; -import Constants = require("../constants"); -import { IRequestLogger } from "../../../logging/IRequestLogger"; -import { IUsersDatabase } from "../../../authentication/backends/IUsersDatabase"; - -export const TEMPLATE_NAME = "password-reset-form"; - -export default class PasswordResetHandler implements IdentityValidable { - private logger: IRequestLogger; - private usersDatabase: IUsersDatabase; - - constructor(logger: IRequestLogger, usersDatabase: IUsersDatabase) { - this.logger = logger; - this.usersDatabase = usersDatabase; - } - - challenge(): string { - return Constants.CHALLENGE; - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - const userid: string = - objectPath.get(req, "query.userid"); - return BluebirdPromise.resolve() - .then(function () { - that.logger.debug(req, "User '%s' requested a password reset", userid); - if (!userid) - return BluebirdPromise.reject( - new exceptions.AccessDeniedError("No user id provided")); - - return that.usersDatabase.getEmails(userid); - }) - .then(function (emails: string[]) { - if (!emails && emails.length <= 0) throw new Error("No email found"); - const identity = { - email: emails[0], - userid: userid - }; - return BluebirdPromise.resolve(identity); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject(new exceptions.IdentityError(err.message)); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return BluebirdPromise.resolve(); - } - - postValidationResponse(req: express.Request, res: express.Response) { - res.render(TEMPLATE_NAME); - } - - mailSubject(): string { - return "Reset your password"; - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/password-reset/request/get.ts b/themes/triangles/server/src/lib/routes/password-reset/request/get.ts deleted file mode 100644 index 8f3ae2b4b..000000000 --- a/themes/triangles/server/src/lib/routes/password-reset/request/get.ts +++ /dev/null @@ -1,13 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); -import exceptions = require("../../../Exceptions"); - -import Constants = require("./../constants"); - -const TEMPLATE_NAME = "password-reset-request"; - -export default function (req: express.Request, res: express.Response) { - res.render(TEMPLATE_NAME); -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/secondfactor/get.spec.ts b/themes/triangles/server/src/lib/routes/secondfactor/get.spec.ts deleted file mode 100644 index 6c77e1f69..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/get.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import SecondFactorGet from "./get"; -import { ServerVariablesMockBuilder, ServerVariablesMock } - from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; -import Sinon = require("sinon"); -import ExpressMock = require("../../stubs/express.spec"); -import Assert = require("assert"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); - -describe("routes/secondfactor/get", function () { - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false - } - }; - }); - - describe("test rendering", function () { - it("should render second factor page", function () { - req.session.auth.second_factor = false; - return SecondFactorGet(vars)(req as any, res as any) - .then(function () { - Assert(res.render.calledWith("secondfactor")); - return BluebirdPromise.resolve(); - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/secondfactor/get.ts b/themes/triangles/server/src/lib/routes/secondfactor/get.ts deleted file mode 100644 index 9f6deb4c6..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/get.ts +++ /dev/null @@ -1,28 +0,0 @@ - -import Express = require("express"); -import Endpoints = require("../../../../../shared/api"); -import BluebirdPromise = require("bluebird"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; - -const TEMPLATE_NAME = "secondfactor"; - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response) - : BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, vars.logger); - - res.render(TEMPLATE_NAME, { - username: authSession.userid, - totp_identity_start_endpoint: - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - u2f_identity_start_endpoint: - Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET - }); - resolve(); - }); - } - return handler; -} diff --git a/themes/triangles/server/src/lib/routes/secondfactor/redirect.spec.ts b/themes/triangles/server/src/lib/routes/secondfactor/redirect.spec.ts deleted file mode 100644 index ea66e6dca..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/redirect.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import Redirect from "./redirect"; -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariablesMockBuilder, ServerVariablesMock } -from "../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../ServerVariables"; -import Assert = require("assert"); - -describe("routes/secondfactor/redirect", function() { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - }); - - it("should redirect to default_redirection_url", function() { - vars.config.default_redirection_url = "http://default_redirection_url"; - Redirect(vars)(req as any, res as any) - .then(function() { - Assert(res.json.calledWith({ - redirect: "http://default_redirection_url" - })); - }); - }); - - it("should redirect to /", function() { - Redirect(vars)(req as any, res as any) - .then(function() { - Assert(res.json.calledWith({ - redirect: "/" - })); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/secondfactor/redirect.ts b/themes/triangles/server/src/lib/routes/secondfactor/redirect.ts deleted file mode 100644 index 5d84d9ebf..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/redirect.ts +++ /dev/null @@ -1,30 +0,0 @@ - -import express = require("express"); -import objectPath = require("object-path"); -import Endpoints = require("../../../../../shared/api"); -import { ServerVariables } from "../../ServerVariables"; -import BluebirdPromise = require("bluebird"); -import ErrorReplies = require("../../ErrorReplies"); -import UserMessages = require("../../../../../shared/UserMessages"); -import { RedirectionMessage } from "../../../../../shared/RedirectionMessage"; -import Constants = require("../../../../../shared/constants"); - -export default function (vars: ServerVariables) { - return function (req: express.Request, res: express.Response) - : BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - let redirectUrl: string = "/"; - if (vars.config.default_redirection_url) { - redirectUrl = vars.config.default_redirection_url; - } - vars.logger.debug(req, "Request redirection to \"%s\".", redirectUrl); - res.json({ - redirect: redirectUrl - } as RedirectionMessage); - return resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - }; -} diff --git a/themes/triangles/server/src/lib/routes/secondfactor/totp/constants.ts b/themes/triangles/server/src/lib/routes/secondfactor/totp/constants.ts deleted file mode 100644 index 7b5a1dcfe..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/totp/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export const CHALLENGE = "totp-register"; -export const TEMPLATE_NAME = "totp-register"; - diff --git a/themes/triangles/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts b/themes/triangles/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts deleted file mode 100644 index 78b8ea3ea..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.spec.ts +++ /dev/null @@ -1,116 +0,0 @@ -import Sinon = require("sinon"); -import RegistrationHandler from "./RegistrationHandler"; -import { Identity } from "../../../../../../types/Identity"; -import { UserDataStore } from "../../../../storage/UserDataStore"; -import BluebirdPromise = require("bluebird"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { ServerVariablesMock, ServerVariablesMockBuilder } - from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; -import Assert = require("assert"); - -describe("routes/secondfactor/totp/identity/RegistrationHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - req.session = { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false, - identity_check: { - userid: "user", - challenge: "totp-register" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub - .returns(BluebirdPromise.resolve({})); - mocks.userDataStore.saveTOTPSecretStub - .returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - }); - - describe("test totp registration pre validation", function () { - it("should fail if first_factor has not been passed", function () { - req.session.auth.first_factor = false; - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("It should fail")); - }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if userid is missing", function (done) { - req.session.auth.first_factor = false; - req.session.auth.userid = undefined; - - new RegistrationHandler(vars.logger, vars.userDataStore, vars.totpHandler, - vars.config.totp) - .preValidationInit(req as any) - .catch(function (err: Error) { - done(); - }); - }); - - it("should fail if email is missing", function (done) { - req.session.auth.first_factor = false; - req.session.auth.email = undefined; - - new RegistrationHandler(vars.logger, vars.userDataStore, vars.totpHandler, - vars.config.totp) - .preValidationInit(req as any) - .catch(function (err: Error) { - done(); - }); - }); - - it("should succeed if first factor passed, userid and email are provided", - function () { - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .preValidationInit(req as any); - }); - }); - - describe("test totp registration post validation", function () { - it("should generate a secret using userId as label and issuer defined in config", function () { - vars.config.totp = { - issuer: "issuer" - }; - return new RegistrationHandler(vars.logger, vars.userDataStore, - vars.totpHandler, vars.config.totp) - .postValidationResponse(req as any, res as any) - .then(function() { - Assert(mocks.totpHandler.generateStub.calledWithExactly("user", "issuer")); - }); - }); - }); -}); diff --git a/themes/triangles/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts b/themes/triangles/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts deleted file mode 100644 index b39b6d045..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts +++ /dev/null @@ -1,112 +0,0 @@ - -import express = require("express"); -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); - -import { Identity } from "../../../../../../types/Identity"; -import { IdentityValidable } from "../../../../IdentityValidable"; -import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate"; -import Constants = require("../constants"); -import Endpoints = require("../../../../../../../shared/api"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import FirstFactorValidator = require("../../../../FirstFactorValidator"); -import { IRequestLogger } from "../../../../logging/IRequestLogger"; -import { IUserDataStore } from "../../../../storage/IUserDataStore"; -import { ITotpHandler } from "../../../../authentication/totp/ITotpHandler"; -import { TOTPSecret } from "../../../../../../types/TOTPSecret"; -import { TotpConfiguration } from "../../../../configuration/schema/TotpConfiguration"; - - -export default class RegistrationHandler implements IdentityValidable { - private logger: IRequestLogger; - private userDataStore: IUserDataStore; - private totp: ITotpHandler; - private configuration: TotpConfiguration; - - constructor(logger: IRequestLogger, - userDataStore: IUserDataStore, - totp: ITotpHandler, configuration: TotpConfiguration) { - this.logger = logger; - this.userDataStore = userDataStore; - this.totp = totp; - this.configuration = configuration; - } - - challenge(): string { - return Constants.CHALLENGE; - } - - private retrieveIdentity(req: express.Request): BluebirdPromise { - const that = this; - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - const userid = authSession.userid; - const email = authSession.email; - - if (!(userid && email)) { - return reject(new Error("User ID or email is missing")); - } - - const identity = { - email: email, - userid: userid - }; - return resolve(identity); - }); - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - return FirstFactorValidator.validate(req, this.logger) - .then(function () { - return that.retrieveIdentity(req); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return FirstFactorValidator.validate(req, this.logger); - } - - postValidationResponse(req: express.Request, res: express.Response) - : BluebirdPromise { - const that = this; - let secret: TOTPSecret; - let userId: string; - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - userId = authSession.userid; - - if (authSession.identity_check.challenge != Constants.CHALLENGE - || !userId) - return reject(new Error("Bad challenge.")); - - resolve(); - }) - .then(function () { - secret = that.totp.generate(userId, - that.configuration.issuer); - that.logger.debug(req, "Save the TOTP secret in DB"); - return that.userDataStore.saveTOTPSecret(userId, secret); - }) - .then(function () { - AuthenticationSessionHandler.reset(req); - - res.render(Constants.TEMPLATE_NAME, { - base32_secret: secret.base32, - otpauth_url: secret.otpauth_url, - login_endpoint: Endpoints.FIRST_FACTOR_GET - }); - }) - .catch(ErrorReplies.replyWithError200(req, res, that.logger, UserMessages.OPERATION_FAILED)); - } - - mailSubject(): string { - return "Set up Authelia's one-time password"; - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts b/themes/triangles/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts deleted file mode 100644 index 70a20d39d..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/totp/sign/post.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import Assert = require("assert"); -import Exceptions = require("../../../../Exceptions"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import SignPost = require("./post"); -import { ServerVariables } from "../../../../ServerVariables"; - -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { Level } from "../../../../authentication/Level"; - -describe("routes/secondfactor/totp/sign/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let authSession: AuthenticationSession; - let vars: ServerVariables; - let mocks: ServerVariablesMock; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - vars = s.variables; - mocks = s.mocks; - const app_get = Sinon.stub(); - req = { - originalUrl: "/api/totp-register", - app: {}, - body: { - token: "abc" - }, - session: {}, - query: { - redirect: "http://redirect" - } - }; - res = ExpressMock.ResponseMock(); - - const doc = { - userid: "user", - secret: { - base32: "ABCDEF" - } - }; - mocks.userDataStore.retrieveTOTPSecretStub.returns(BluebirdPromise.resolve(doc)); - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - authSession.userid = "user"; - authSession.authentication_level = Level.ONE_FACTOR; - }); - - - it("should send status code 200 when totp is valid", function () { - mocks.totpHandler.validateStub.returns(true); - return SignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(authSession.authentication_level, Level.TWO_FACTOR); - return BluebirdPromise.resolve(); - }); - }); - - it("should send error message when totp is not valid", function () { - mocks.totpHandler.validateStub.returns(false); - return SignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.notEqual(authSession.authentication_level, Level.TWO_FACTOR); - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); -}); - diff --git a/themes/triangles/server/src/lib/routes/secondfactor/totp/sign/post.ts b/themes/triangles/server/src/lib/routes/secondfactor/totp/sign/post.ts deleted file mode 100644 index 34a276d12..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/totp/sign/post.ts +++ /dev/null @@ -1,42 +0,0 @@ -import Bluebird = require("bluebird"); -import Express = require("express"); - -import { TOTPSecretDocument } from "../../../../storage/TOTPSecretDocument"; -import Endpoints = require("../../../../../../../shared/api"); -import Redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; -import { Level } from "../../../../authentication/Level"; - -const UNAUTHORIZED_MESSAGE = "Unauthorized access"; - -export default function (vars: ServerVariables) { - function handler(req: Express.Request, res: Express.Response): Bluebird { - let authSession: AuthenticationSession; - const token = req.body.token; - - return new Bluebird(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - vars.logger.info(req, "Initiate TOTP validation for user \"%s\".", authSession.userid); - resolve(); - }) - .then(function () { - return vars.userDataStore.retrieveTOTPSecret(authSession.userid); - }) - .then(function (doc: TOTPSecretDocument) { - if (!vars.totpHandler.validate(token, doc.secret.base32)) - return Bluebird.reject(new Error("Invalid TOTP token.")); - - vars.logger.debug(req, "TOTP validation succeeded."); - authSession.authentication_level = Level.TWO_FACTOR; - Redirect(vars)(req, res); - return Bluebird.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/triangles/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts b/themes/triangles/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts deleted file mode 100644 index 7f16c0ee8..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/u2f/U2FCommon.ts +++ /dev/null @@ -1,11 +0,0 @@ - -import util = require("util"); -import express = require("express"); - -function extract_app_id(req: express.Request): string { - return util.format("https://%s", req.headers.host); -} - -export = { - extract_app_id: extract_app_id -}; \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts b/themes/triangles/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts deleted file mode 100644 index a54bfbfe6..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); - -import { Identity } from "../../../../../../types/Identity"; -import RegistrationHandler from "./RegistrationHandler"; -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -describe("routes/secondfactor/u2f/identity/RegistrationHandler", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req = ExpressMock.RequestMock(); - req.app = {}; - req.session = { - auth: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = Sinon.spy(); - res.json = Sinon.spy(); - res.status = Sinon.spy(); - }); - - describe("test u2f registration check", test_registration_check); - - function test_registration_check() { - it("should fail if first_factor has not been passed", function () { - req.session.auth.first_factor = false; - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if userid is missing", function () { - req.session.auth.first_factor = false; - req.session.auth.userid = undefined; - - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if email is missing", function () { - req.session.auth.first_factor = false; - req.session.auth.email = undefined; - - return new RegistrationHandler(vars.logger).preValidationInit(req as any) - .then(function () { - return BluebirdPromise.reject(new Error("should not be here")); - }, - function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should succeed if first factor passed, userid and email are provided", function () { - req.session.auth.first_factor = true; - req.session.auth.email = "admin@example.com"; - req.session.auth.userid = "user"; - return new RegistrationHandler(vars.logger).preValidationInit(req as any); - }); - } -}); diff --git a/themes/triangles/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts b/themes/triangles/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts deleted file mode 100644 index bc4713c77..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts +++ /dev/null @@ -1,73 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import objectPath = require("object-path"); - -import { IdentityValidable } from "../../../../IdentityValidable"; -import { Identity } from "../../../../../../types/Identity"; -import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate"; -import FirstFactorValidator = require("../../../../FirstFactorValidator"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { IRequestLogger } from "../../../../logging/IRequestLogger"; - -const CHALLENGE = "u2f-register"; -const MAIL_SUBJECT = "Register your security key with Authelia"; - -const POST_VALIDATION_TEMPLATE_NAME = "u2f-register"; - - -export default class RegistrationHandler implements IdentityValidable { - private logger: IRequestLogger; - - constructor(logger: IRequestLogger) { - this.logger = logger; - } - - challenge(): string { - return CHALLENGE; - } - - private retrieveIdentity(req: express.Request): BluebirdPromise { - const that = this; - return new BluebirdPromise(function(resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, that.logger); - const userid = authSession.userid; - const email = authSession.email; - - if (!(userid && email)) { - return reject(new Error("User ID or email is missing")); - } - - const identity = { - email: email, - userid: userid - }; - return resolve(identity); - }); - } - - preValidationInit(req: express.Request): BluebirdPromise { - const that = this; - return FirstFactorValidator.validate(req, this.logger) - .then(function () { - return that.retrieveIdentity(req); - }); - } - - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } - - postValidationInit(req: express.Request) { - return FirstFactorValidator.validate(req, this.logger); - } - - postValidationResponse(req: express.Request, res: express.Response) { - res.render(POST_VALIDATION_TEMPLATE_NAME); - } - - mailSubject(): string { - return MAIL_SUBJECT; - } -} - diff --git a/themes/triangles/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts b/themes/triangles/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts deleted file mode 100644 index de3347a21..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/u2f/register/post.spec.ts +++ /dev/null @@ -1,146 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import assert = require("assert"); -import U2FRegisterPost = require("./post"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - - -describe("routes/secondfactor/u2f/register/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - describe("test registration", test_registration); - - - function test_registration() { - it("should save u2f meta and return status code 200", function () { - const expectedStatus = { - keyHandle: "keyHandle", - publicKey: "pbk", - certificate: "cert" - }; - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve(expectedStatus)); - - authSession.register_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { - assert.equal("user", mocks.userDataStore.saveU2FRegistrationStub.getCall(0).args[0]); - assert.equal(authSession.identity_check, undefined); - }); - }); - - it("should return error message on finishRegistration error", function () { - mocks.u2f.checkRegistrationStub.returns({ errorCode: 500 }); - - authSession.register_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when register_request is not provided", function () { - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve()); - authSession.register_request = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when no auth request has been initiated", function () { - mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve()); - authSession.register_request = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - - it("should return error message when identity has not been verified", function () { - authSession.identity_check = undefined; - return U2FRegisterPost.default(vars)(req as any, res as any) - .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) - .catch(function () { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - return BluebirdPromise.resolve(); - }); - }); - } -}); - diff --git a/themes/triangles/server/src/lib/routes/secondfactor/u2f/register/post.ts b/themes/triangles/server/src/lib/routes/secondfactor/u2f/register/post.ts deleted file mode 100644 index 7296ccbe5..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/u2f/register/post.ts +++ /dev/null @@ -1,64 +0,0 @@ - -import { UserDataStore } from "../../../../storage/UserDataStore"; -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import U2f = require("u2f"); -import { U2FRegistration } from "../../../../../../types/U2FRegistration"; -import redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; - - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appid = u2f_common.extract_app_id(req); - const registrationResponse: U2f.RegistrationData = req.body; - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - const registrationRequest = authSession.register_request; - - if (!registrationRequest) { - return reject(new Error("No registration request")); - } - - if (!authSession.identity_check - || authSession.identity_check.challenge != "u2f-register") { - return reject(new Error("Bad challenge for registration request")); - } - - vars.logger.info(req, "Finishing registration"); - vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest)); - vars.logger.debug(req, "RegistrationResponse = %s", JSON.stringify(registrationResponse)); - - return resolve(vars.u2f.checkRegistration(registrationRequest, registrationResponse)); - }) - .then(function (u2fResult: U2f.RegistrationResult | U2f.Error): BluebirdPromise { - if (objectPath.has(u2fResult, "errorCode")) - return BluebirdPromise.reject(new Error("Error while registering.")); - - const registrationResult: U2f.RegistrationResult = u2fResult as U2f.RegistrationResult; - vars.logger.info(req, "Store registration and reply"); - vars.logger.debug(req, "RegistrationResult = %s", JSON.stringify(registrationResult)); - const registration: U2FRegistration = { - keyHandle: registrationResult.keyHandle, - publicKey: registrationResult.publicKey - }; - return vars.userDataStore.saveU2FRegistration(authSession.userid, appid, registration); - }) - .then(function () { - authSession.identity_check = undefined; - redirect(vars)(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/triangles/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts b/themes/triangles/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts deleted file mode 100644 index a207c9109..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/u2f/register_request/get.spec.ts +++ /dev/null @@ -1,86 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import U2FRegisterRequestGet = require("./get"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -describe("routes/secondfactor/u2f/register_request/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - const options = { - inMemoryOnly: true - }; - - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - describe("test registration request", () => { - it("should send back the registration request and save it in the session", function () { - const expectedRequest = { - test: "abc" - }; - mocks.u2f.requestStub.returns(BluebirdPromise.resolve(expectedRequest)); - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.deepEqual(expectedRequest, res.json.getCall(0).args[0]); - }); - }); - - it("should return internal error on registration request", function () { - res.send = sinon.spy(); - const user_key_container = {}; - mocks.u2f.requestStub.returns(BluebirdPromise.reject("Internal error")); - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." - }); - }); - }); - - it("should return forbidden if identity has not been verified", function () { - req.session.auth.identity_check = undefined; - return U2FRegisterRequestGet.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(403, res.status.getCall(0).args[0]); - }); - }); - }); -}); - diff --git a/themes/triangles/server/src/lib/routes/secondfactor/u2f/register_request/get.ts b/themes/triangles/server/src/lib/routes/secondfactor/u2f/register_request/get.ts deleted file mode 100644 index f611af933..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/u2f/register_request/get.ts +++ /dev/null @@ -1,43 +0,0 @@ - -import { UserDataStore } from "../../../../storage/UserDataStore"; - -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import U2f = require("u2f"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appid: string = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.identity_check - || authSession.identity_check.challenge != "u2f-register") { - res.status(403); - res.send(); - return reject(new Error("Bad challenge.")); - } - - vars.logger.info(req, "Starting registration for appId '%s'", appid); - return resolve(vars.u2f.request(appid)); - }) - .then(function (registrationRequest: U2f.Request) { - vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest)); - authSession.register_request = registrationRequest; - res.json(registrationRequest); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - - return handler; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts b/themes/triangles/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts deleted file mode 100644 index 9b137e66d..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/u2f/sign/post.spec.ts +++ /dev/null @@ -1,101 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import Assert = require("assert"); -import U2FSignPost = require("./post"); -import { ServerVariables } from "../../../../ServerVariables"; -import winston = require("winston"); - -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; -import ExpressMock = require("../../../../stubs/express.spec"); -import U2FMock = require("../../../../stubs/u2f.spec"); -import U2f = require("u2f"); -import { Level } from "../../../../authentication/Level"; - -describe("routes/secondfactor/u2f/sign/post", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.app = {}; - req.originalUrl = "/api/xxxx"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - req.session = { - auth: { - userid: "user", - authentication_level: Level.ONE_FACTOR, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - it("should return status code 204", function () { - const expectedStatus = { - keyHandle: "keyHandle", - publicKey: "pbk", - certificate: "cert" - }; - mocks.u2f.checkSignatureStub.returns(expectedStatus); - - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({ - registration: { - publicKey: "PUBKEY" - } - })); - - req.session.auth.sign_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FSignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(req.session.auth.authentication_level, Level.TWO_FACTOR); - }); - }); - - it("should return unauthorized error on registration request internal error", function () { - mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({ - registration: { - publicKey: "PUBKEY" - } - })); - mocks.u2f.checkSignatureStub.returns({ errorCode: 500 }); - - req.session.auth.sign_request = { - appId: "app", - challenge: "challenge", - keyHandle: "key", - version: "U2F_V2" - }; - return U2FSignPost.default(vars)(req as any, res as any) - .then(function () { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], - { error: "Operation failed." }); - }); - }); -}); - diff --git a/themes/triangles/server/src/lib/routes/secondfactor/u2f/sign/post.ts b/themes/triangles/server/src/lib/routes/secondfactor/u2f/sign/post.ts deleted file mode 100644 index 7ee711c2c..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/u2f/sign/post.ts +++ /dev/null @@ -1,57 +0,0 @@ - -import objectPath = require("object-path"); -import u2f_common = require("../U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import { UserDataStore } from "../../../../storage/UserDataStore"; -import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocument"; -import { Winston } from "../../../../../../types/Dependencies"; -import U2f = require("u2f"); -import exceptions = require("../../../../Exceptions"); -import redirect from "../../redirect"; -import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -import { Level } from "../../../../authentication/Level"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appId = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - if (!authSession.sign_request) { - const err = new Error("No sign request"); - ErrorReplies.replyWithError401(req, res, vars.logger)(err); - return reject(err); - } - resolve(); - }) - .then(function () { - const userid = authSession.userid; - return vars.userDataStore.retrieveU2FRegistration(userid, appId); - }) - .then(function (doc: U2FRegistrationDocument): BluebirdPromise { - const signRequest = authSession.sign_request; - const signData: U2f.SignatureData = req.body; - vars.logger.info(req, "Finish authentication"); - return BluebirdPromise.resolve(vars.u2f.checkSignature(signRequest, signData, doc.registration.publicKey)); - }) - .then(function (result: U2f.SignatureResult | U2f.Error): BluebirdPromise { - if (objectPath.has(result, "errorCode")) - return BluebirdPromise.reject(new Error("Error while signing")); - vars.logger.info(req, "Successful authentication"); - authSession.authentication_level = Level.TWO_FACTOR; - redirect(vars)(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - - return handler; -} - diff --git a/themes/triangles/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts b/themes/triangles/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts deleted file mode 100644 index dd52b27e0..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/u2f/sign_request/get.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ - -import sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); -import assert = require("assert"); -import U2FSignRequestGet = require("./get"); -import ExpressMock = require("../../../../stubs/express.spec"); -import { Request } from "u2f"; -import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec"; -import { ServerVariables } from "../../../../ServerVariables"; - -import { SignMessage } from "../../../../../../../shared/SignMessage"; - -describe("routes/secondfactor/u2f/sign_request/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.originalUrl = "/api/xxxx"; - req.app = {}; - req.session = { - auth: { - userid: "user", - first_factor: true, - second_factor: false, - identity_check: { - challenge: "u2f-register", - userid: "user" - } - } - }; - req.headers = {}; - req.headers.host = "localhost"; - - const s = ServerVariablesMockBuilder.build(); - mocks = s.mocks; - vars = s.variables; - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - it("should send back the sign request and save it in the session", function () { - const expectedRequest: Request = { - version: "U2F_V2", - appId: 'app', - challenge: 'challenge!' - }; - mocks.u2f.requestStub.returns(expectedRequest); - mocks.userDataStore.retrieveU2FRegistrationStub - .returns(BluebirdPromise.resolve({ - registration: { - keyHandle: "KeyHandle" - } - })); - - return U2FSignRequestGet.default(vars)(req as any, res as any) - .then(() => { - assert.deepEqual(expectedRequest, req.session.auth.sign_request); - assert.deepEqual(expectedRequest, res.json.getCall(0).args[0]); - }); - }); -}); - diff --git a/themes/triangles/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts b/themes/triangles/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts deleted file mode 100644 index 9e93dde06..000000000 --- a/themes/triangles/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts +++ /dev/null @@ -1,42 +0,0 @@ - -import u2f_common = require("../../../secondfactor/u2f/U2FCommon"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocument"; -import exceptions = require("../../../../Exceptions"); -import ErrorReplies = require("../../../../ErrorReplies"); -import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler"; -import UserMessages = require("../../../../../../../shared/UserMessages"); -import { ServerVariables } from "../../../../ServerVariables"; -import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; - -export default function (vars: ServerVariables) { - function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession; - const appId = u2f_common.extract_app_id(req); - - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - resolve(); - }) - .then(function () { - return vars.userDataStore.retrieveU2FRegistration(authSession.userid, appId); - }) - .then(function (doc: U2FRegistrationDocument): BluebirdPromise { - if (!doc) - return BluebirdPromise.reject(new exceptions.AccessDeniedError("No U2F registration document found.")); - - const appId: string = u2f_common.extract_app_id(req); - vars.logger.info(req, "Start authentication of app '%s'", appId); - vars.logger.debug(req, "AppId = %s, keyHandle = %s", appId, JSON.stringify(doc.registration.keyHandle)); - - const request = vars.u2f.request(appId, doc.registration.keyHandle); - authSession.sign_request = request; - res.json(request); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, vars.logger, - UserMessages.OPERATION_FAILED)); - } - return handler; -} diff --git a/themes/triangles/server/src/lib/routes/verify/access_control.ts b/themes/triangles/server/src/lib/routes/verify/access_control.ts deleted file mode 100644 index 136239aeb..000000000 --- a/themes/triangles/server/src/lib/routes/verify/access_control.ts +++ /dev/null @@ -1,51 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import Util = require("util"); - -import Exceptions = require("../../Exceptions"); - -import { Level as AuthorizationLevel } from "../../authorization/Level"; -import { Level as AuthenticationLevel } from "../../authentication/Level"; -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { ServerVariables } from "../../ServerVariables"; - -function isAuthorized( - authorization: AuthorizationLevel, - authentication: AuthenticationLevel): boolean { - - if (authorization == AuthorizationLevel.BYPASS) { - return true; - } else if (authorization == AuthorizationLevel.ONE_FACTOR && - authentication >= AuthenticationLevel.ONE_FACTOR) { - return true; - } else if (authorization == AuthorizationLevel.TWO_FACTOR && - authentication >= AuthenticationLevel.TWO_FACTOR) { - return true; - } - return false; -} - -export default function ( - req: Express.Request, - vars: ServerVariables, - domain: string, resource: string, - user: string, groups: string[], - authenticationLevel: AuthenticationLevel) { - - return new BluebirdPromise(function (resolve, reject) { - const authorizationLevel = vars.authorizer - .authorization({domain, resource}, {user, groups}); - - if (!isAuthorized(authorizationLevel, authenticationLevel)) { - if (authorizationLevel == AuthorizationLevel.DENY) { - reject(new Exceptions.NotAuthorizedError( - Util.format("User %s is not authorized to access %s%s", user, domain, resource))); - return; - } - reject(new Exceptions.NotAuthenticatedError(Util.format( - "User '%s' is not sufficiently authorized to access %s%s.", user, domain, resource))); - return; - } - resolve(); - }); -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/verify/get.spec.ts b/themes/triangles/server/src/lib/routes/verify/get.spec.ts deleted file mode 100644 index 67cf19fb6..000000000 --- a/themes/triangles/server/src/lib/routes/verify/get.spec.ts +++ /dev/null @@ -1,320 +0,0 @@ - -import Assert = require("assert"); -import BluebirdPromise = require("bluebird"); -import Express = require("express"); -import Sinon = require("sinon"); -import winston = require("winston"); - -import VerifyGet = require("./get"); -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } from "../../../../types/AuthenticationSession"; -import ExpressMock = require("../../stubs/express.spec"); -import { ServerVariables } from "../../ServerVariables"; -import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../ServerVariablesMockBuilder.spec"; -import { Level } from "../../authentication/Level"; -import { Level as AuthorizationLevel } from "../../authorization/Level"; - -describe("routes/verify/get", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock; - let vars: ServerVariables; - let authSession: AuthenticationSession; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - req.originalUrl = "/api/xxxx"; - req.query = { - redirect: "undefined" - }; - AuthenticationSessionHandler.reset(req as any); - req.headers["x-original-url"] = "https://secret.example.com/"; - const s = ServerVariablesMockBuilder.build(false); - mocks = s.mocks; - vars = s.variables; - authSession = AuthenticationSessionHandler.get(req as any, vars.logger); - }); - - describe("with session cookie", function () { - it("should be already authenticated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser"); - Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup"); - Assert.equal(204, res.status.getCall(0).args[0]); - }); - }); - - function test_session(_authSession: AuthenticationSession, status_code: number) { - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert.equal(status_code, res.status.getCall(0).args[0]); - }); - } - - function test_non_authenticated_401(authSession: AuthenticationSession) { - return test_session(authSession, 401); - } - - function test_unauthorized_403(authSession: AuthenticationSession) { - return test_session(authSession, 403); - } - - function test_authorized(authSession: AuthenticationSession) { - return test_session(authSession, 204); - } - - describe("given user tries to access a 2-factor endpoint", function () { - before(function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - }); - - describe("given different cases of session", function () { - it("should not be authenticated when second factor is missing", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: "user", - authentication_level: Level.ONE_FACTOR, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when userid is missing", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: undefined, - authentication_level: Level.TWO_FACTOR, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when level is insufficient", function () { - return test_non_authenticated_401({ - keep_me_logged_in: false, - userid: "user", - authentication_level: Level.NOT_AUTHENTICATED, - email: undefined, - groups: [], - last_activity_datetime: new Date().getTime() - }); - }); - - it("should not be authenticated when session has not be initiated", function () { - return test_non_authenticated_401(undefined); - }); - - it("should not be authenticated when domain is not allowed for user", function () { - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - req.headers["x-original-url"] = "https://test.example.com/"; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.DENY); - - return test_unauthorized_403({ - keep_me_logged_in: false, - authentication_level: Level.TWO_FACTOR, - userid: "user", - groups: ["group1", "group2"], - email: undefined, - last_activity_datetime: new Date().getTime() - }); - }); - }); - }); - - describe("given user tries to access a single factor endpoint", function () { - beforeEach(function () { - req.headers["x-original-url"] = "https://redirect.url/"; - }); - - it("should be authenticated when first factor is validated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - authSession.authentication_level = Level.ONE_FACTOR; - authSession.userid = "user1"; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWith(204)); - Assert(res.send.calledOnce); - }); - }); - - it("should be rejected with 401 when not authenticated", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - authSession.authentication_level = Level.NOT_AUTHENTICATED; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWith(401)); - }); - }); - }); - - describe("inactivity period", function () { - it("should update last inactivity period on requests on /api/verify", function () { - mocks.config.session.inactivity = 200000; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - const currentTime = new Date().getTime() - 1000; - AuthenticationSessionHandler.reset(req as any); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - authSession.last_activity_datetime = currentTime; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }) - .then(function (authSession) { - Assert(authSession.last_activity_datetime > currentTime); - }); - }); - - it("should reset session when max inactivity period has been reached", function () { - mocks.config.session.inactivity = 1; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - const currentTime = new Date().getTime() - 1000; - AuthenticationSessionHandler.reset(req as any); - authSession.authentication_level = Level.TWO_FACTOR; - authSession.userid = "myuser"; - authSession.groups = ["mygroup", "othergroup"]; - authSession.last_activity_datetime = currentTime; - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - return AuthenticationSessionHandler.get(req as any, vars.logger); - }) - .then(function (authSession) { - Assert.equal(authSession.authentication_level, Level.NOT_AUTHENTICATED); - Assert.equal(authSession.userid, undefined); - }); - }); - }); - }); - - describe("response type 401 | 302", function() { - it("should return error code 401", function() { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should redirect to provided redirection url", function() { - const REDIRECT_URL = "http://redirection_url.com"; - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - req.query["rd"] = REDIRECT_URL; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.redirect.calledWithExactly(REDIRECT_URL)); - }); - }); - }); - - describe("with basic auth", function () { - it("should authenticate correctly", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.returns({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "john"); - Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup"); - Assert.equal(204, res.status.getCall(0).args[0]); - }); - }); - - it("should fail when endpoint is protected by two factors", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.config.access_control.rules = [{ - domain: "secret.example.com", - policy: "two_factor" - }]; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when base64 token is not valid", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic i_m*not_a_base64*token"; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when base64 token has not format user:psswd", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzOmJhZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when bad user password is provided", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.rejects(new Error( - "Invalid credentials")); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - - it("should fail when resource is restricted", function () { - mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR); - mocks.config.access_control.default_policy = "one_factor"; - mocks.usersDatabase.checkUserPasswordStub.resolves({ - groups: ["mygroup", "othergroup"], - }); - req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA=="; - - return VerifyGet.default(vars)(req as Express.Request, res as any) - .then(function () { - Assert(res.status.calledWithExactly(401)); - }); - }); - }); -}); - diff --git a/themes/triangles/server/src/lib/routes/verify/get.ts b/themes/triangles/server/src/lib/routes/verify/get.ts deleted file mode 100644 index f73861696..000000000 --- a/themes/triangles/server/src/lib/routes/verify/get.ts +++ /dev/null @@ -1,91 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Express = require("express"); -import Exceptions = require("../../Exceptions"); -import ErrorReplies = require("../../ErrorReplies"); -import { ServerVariables } from "../../ServerVariables"; -import GetWithSessionCookieMethod from "./get_session_cookie"; -import GetWithBasicAuthMethod from "./get_basic_auth"; -import Constants = require("../../../../../shared/constants"); -import ObjectPath = require("object-path"); - -import { AuthenticationSessionHandler } - from "../../AuthenticationSessionHandler"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; - -const REMOTE_USER = "Remote-User"; -const REMOTE_GROUPS = "Remote-Groups"; - - -function verifyWithSelectedMethod(req: Express.Request, res: Express.Response, - vars: ServerVariables, authSession: AuthenticationSession) - : () => BluebirdPromise<{ username: string, groups: string[] }> { - return function () { - const authorization: string = "" + req.headers["proxy-authorization"]; - if (authorization && authorization.startsWith("Basic ")) - return GetWithBasicAuthMethod(req, res, vars, authorization); - - return GetWithSessionCookieMethod(req, res, vars, authSession); - }; -} - -function setRedirectHeader(req: Express.Request, res: Express.Response) { - return function () { - const originalUrl = ObjectPath.get( - req, "headers.x-original-url"); - res.set("Redirect", originalUrl); - return BluebirdPromise.resolve(); - }; -} - -function setUserAndGroupsHeaders(res: Express.Response) { - return function (u: { username: string, groups: string[] }) { - res.setHeader(REMOTE_USER, u.username); - res.setHeader(REMOTE_GROUPS, u.groups.join(",")); - return BluebirdPromise.resolve(); - }; -} - -function replyWith200(res: Express.Response) { - return function () { - res.status(204); - res.send(); - }; -} - -function getRedirectParam(req: Express.Request) { - return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" - ? req.query[Constants.REDIRECT_QUERY_PARAM] - : undefined; -} - -export default function (vars: ServerVariables) { - return function (req: Express.Request, res: Express.Response) - : BluebirdPromise { - let authSession: AuthenticationSession; - return new BluebirdPromise(function (resolve, reject) { - authSession = AuthenticationSessionHandler.get(req, vars.logger); - resolve(); - }) - .then(setRedirectHeader(req, res)) - .then(verifyWithSelectedMethod(req, res, vars, authSession)) - .then(setUserAndGroupsHeaders(res)) - .then(replyWith200(res)) - // The user is authenticated but has restricted access -> 403 - .catch(Exceptions.NotAuthorizedError, - ErrorReplies.replyWithError403(req, res, vars.logger)) - .catch(Exceptions.NotAuthenticatedError, - ErrorReplies.replyWithError401(req, res, vars.logger)) - // The user is not yet authenticated -> 401 - .catch((err) => { - const redirectUrl = getRedirectParam(req); - if (redirectUrl) { - ErrorReplies.redirectTo(redirectUrl, req, res, vars.logger)(err); - } - else { - ErrorReplies.replyWithError401(req, res, vars.logger)(err); - } - }); - }; -} - diff --git a/themes/triangles/server/src/lib/routes/verify/get_basic_auth.ts b/themes/triangles/server/src/lib/routes/verify/get_basic_auth.ts deleted file mode 100644 index af23c76c9..000000000 --- a/themes/triangles/server/src/lib/routes/verify/get_basic_auth.ts +++ /dev/null @@ -1,55 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import ObjectPath = require("object-path"); -import { ServerVariables } from "../../ServerVariables"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; -import AccessControl from "./access_control"; -import { URLDecomposer } from "../../utils/URLDecomposer"; -import { Level } from "../../authentication/Level"; - -export default function (req: Express.Request, res: Express.Response, - vars: ServerVariables, authorizationHeader: string) - : BluebirdPromise<{ username: string, groups: string[] }> { - let username: string; - const uri = ObjectPath.get(req, "headers.x-original-url"); - const urlDecomposition = URLDecomposer.fromUrl(uri); - - return BluebirdPromise.resolve() - .then(() => { - const base64Re = new RegExp("^Basic ((?:[A-Za-z0-9+/]{4})*" + - "(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)$"); - const isTokenValidBase64 = base64Re.test(authorizationHeader); - - if (!isTokenValidBase64) { - return BluebirdPromise.reject(new Error("No valid base64 token found in the header")); - } - - const tokenMatches = authorizationHeader.match(base64Re); - const base64Token = tokenMatches[1]; - const decodedToken = Buffer.from(base64Token, "base64").toString(); - const splittedToken = decodedToken.split(":"); - - if (splittedToken.length != 2) { - return BluebirdPromise.reject(new Error( - "The authorization token is invalid. Expecting 'userid:password'")); - } - - username = splittedToken[0]; - const password = splittedToken[1]; - return vars.usersDatabase.checkUserPassword(username, password); - }) - .then(function (groupsAndEmails) { - return AccessControl(req, vars, urlDecomposition.domain, urlDecomposition.path, - username, groupsAndEmails.groups, Level.ONE_FACTOR) - .then(() => BluebirdPromise.resolve({ - username: username, - groups: groupsAndEmails.groups - })); - }) - .catch(function (err: Error) { - return BluebirdPromise.reject( - new Error("Unable to authenticate the user with basic auth. Cause: " - + err.message)); - }); -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/routes/verify/get_session_cookie.ts b/themes/triangles/server/src/lib/routes/verify/get_session_cookie.ts deleted file mode 100644 index 070344812..000000000 --- a/themes/triangles/server/src/lib/routes/verify/get_session_cookie.ts +++ /dev/null @@ -1,78 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import Util = require("util"); -import ObjectPath = require("object-path"); - -import Exceptions = require("../../Exceptions"); -import { Configuration } from "../../configuration/schema/Configuration"; -import { ServerVariables } from "../../ServerVariables"; -import { IRequestLogger } from "../../logging/IRequestLogger"; -import { AuthenticationSession } - from "../../../../types/AuthenticationSession"; -import { AuthenticationSessionHandler } - from "../../AuthenticationSessionHandler"; -import AccessControl from "./access_control"; -import { URLDecomposer } from "../../utils/URLDecomposer"; - -function verify_inactivity(req: Express.Request, - authSession: AuthenticationSession, - configuration: Configuration, logger: IRequestLogger) - : BluebirdPromise { - - // If inactivity is not specified, then inactivity timeout does not apply - if (!configuration.session.inactivity || authSession.keep_me_logged_in) { - return BluebirdPromise.resolve(); - } - - const lastActivityTime = authSession.last_activity_datetime; - const currentTime = new Date().getTime(); - authSession.last_activity_datetime = currentTime; - - const inactivityPeriodMs = currentTime - lastActivityTime; - logger.debug(req, "Inactivity period was %s s and max period was %s.", - inactivityPeriodMs / 1000, configuration.session.inactivity / 1000); - if (inactivityPeriodMs < configuration.session.inactivity) { - return BluebirdPromise.resolve(); - } - - logger.debug(req, "Session has been reset after too long inactivity period."); - AuthenticationSessionHandler.reset(req); - return BluebirdPromise.reject(new Error("Inactivity period exceeded.")); -} - -export default function (req: Express.Request, res: Express.Response, - vars: ServerVariables, authSession: AuthenticationSession) - : BluebirdPromise<{ username: string, groups: string[] }> { - - return BluebirdPromise.resolve() - .then(() => { - const username = authSession.userid; - const groups = authSession.groups; - - if (!authSession.userid) { - return BluebirdPromise.reject(new Exceptions.AccessDeniedError( - "userid is missing")); - } - - const originalUrl = ObjectPath.get( - req, "headers.x-original-url"); - const originalUri = - ObjectPath.get(req, "headers.x-original-uri"); - - const d = URLDecomposer.fromUrl(originalUrl); - vars.logger.debug(req, "domain=%s, path=%s, user=%s, groups=%s", d.domain, - d.path, username, groups.join(",")); - return AccessControl(req, vars, d.domain, d.path, username, groups, - authSession.authentication_level); - }) - .then(() => { - return verify_inactivity(req, authSession, - vars.config, vars.logger); - }) - .then(() => { - return BluebirdPromise.resolve({ - username: authSession.userid, - groups: authSession.groups - }); - }); -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/AuthenticationTraceDocument.d.ts b/themes/triangles/server/src/lib/storage/AuthenticationTraceDocument.d.ts deleted file mode 100644 index 69818c055..000000000 --- a/themes/triangles/server/src/lib/storage/AuthenticationTraceDocument.d.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export interface AuthenticationTraceDocument { - userId: string; - date: Date; - isAuthenticationSuccessful: boolean; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/CollectionFactoryFactory.ts b/themes/triangles/server/src/lib/storage/CollectionFactoryFactory.ts deleted file mode 100644 index 92b29abfa..000000000 --- a/themes/triangles/server/src/lib/storage/CollectionFactoryFactory.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ICollectionFactory } from "./ICollectionFactory"; -import { NedbCollectionFactory } from "./nedb/NedbCollectionFactory"; -import { MongoCollectionFactory } from "./mongo/MongoCollectionFactory"; -import { IMongoClient } from "../connectors/mongo/IMongoClient"; - - -export class CollectionFactoryFactory { - static createNedb(options: Nedb.DataStoreOptions): ICollectionFactory { - return new NedbCollectionFactory(options); - } - - static createMongo(client: IMongoClient): ICollectionFactory { - return new MongoCollectionFactory(client); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/CollectionFactoryStub.spec.ts b/themes/triangles/server/src/lib/storage/CollectionFactoryStub.spec.ts deleted file mode 100644 index 17f8bb021..000000000 --- a/themes/triangles/server/src/lib/storage/CollectionFactoryStub.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import { ICollection } from "./ICollection"; -import { ICollectionFactory } from "./ICollectionFactory"; - -export class CollectionFactoryStub implements ICollectionFactory { - buildStub: Sinon.SinonStub; - - constructor() { - this.buildStub = Sinon.stub(); - } - - build(collectionName: string): ICollection { - return this.buildStub(collectionName); - } -} diff --git a/themes/triangles/server/src/lib/storage/CollectionStub.spec.ts b/themes/triangles/server/src/lib/storage/CollectionStub.spec.ts deleted file mode 100644 index 42895d672..000000000 --- a/themes/triangles/server/src/lib/storage/CollectionStub.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import Sinon = require("sinon"); -import { ICollection } from "./ICollection"; - -export class CollectionStub implements ICollection { - findStub: Sinon.SinonStub; - findOneStub: Sinon.SinonStub; - updateStub: Sinon.SinonStub; - removeStub: Sinon.SinonStub; - insertStub: Sinon.SinonStub; - - constructor() { - this.findStub = Sinon.stub(); - this.findOneStub = Sinon.stub(); - this.updateStub = Sinon.stub(); - this.removeStub = Sinon.stub(); - this.insertStub = Sinon.stub(); - } - - find(filter: any, sortKeys: any, count: number): BluebirdPromise { - return this.findStub(filter, sortKeys, count); - } - - findOne(filter: any): BluebirdPromise { - return this.findOneStub(filter); - } - - update(filter: any, document: any, options: any): BluebirdPromise { - return this.updateStub(filter, document, options); - } - - remove(filter: any): BluebirdPromise { - return this.removeStub(filter); - } - - insert(document: any): BluebirdPromise { - return this.insertStub(document); - } -} diff --git a/themes/triangles/server/src/lib/storage/ICollection.d.ts b/themes/triangles/server/src/lib/storage/ICollection.d.ts deleted file mode 100644 index caa6c2a87..000000000 --- a/themes/triangles/server/src/lib/storage/ICollection.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* istanbul ignore next */ -import BluebirdPromise = require("bluebird"); - -/* istanbul ignore next */ -export interface ICollection { - find(query: any, sortKeys: any, count: number): BluebirdPromise; - findOne(query: any): BluebirdPromise; - update(query: any, updateQuery: any, options?: any): BluebirdPromise; - remove(query: any): BluebirdPromise; - insert(document: any): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/ICollectionFactory.d.ts b/themes/triangles/server/src/lib/storage/ICollectionFactory.d.ts deleted file mode 100644 index 39eb42c77..000000000 --- a/themes/triangles/server/src/lib/storage/ICollectionFactory.d.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import { ICollection } from "./ICollection"; - -export interface ICollectionFactory { - build(collectionName: string): ICollection; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/IUserDataStore.d.ts b/themes/triangles/server/src/lib/storage/IUserDataStore.d.ts deleted file mode 100644 index 81df482aa..000000000 --- a/themes/triangles/server/src/lib/storage/IUserDataStore.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; - -export interface IUserDataStore { - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise; - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise; - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise; - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise; - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise; - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise; - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise; - retrieveTOTPSecret(userId: string): BluebirdPromise; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/IdentityValidationDocument.d.ts b/themes/triangles/server/src/lib/storage/IdentityValidationDocument.d.ts deleted file mode 100644 index e7fd7b3f4..000000000 --- a/themes/triangles/server/src/lib/storage/IdentityValidationDocument.d.ts +++ /dev/null @@ -1,7 +0,0 @@ - -export interface IdentityValidationDocument { - userId: string; - token: string; - challenge: string; - maxDate: Date; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/TOTPSecretDocument.d.ts b/themes/triangles/server/src/lib/storage/TOTPSecretDocument.d.ts deleted file mode 100644 index a6c0bf9e6..000000000 --- a/themes/triangles/server/src/lib/storage/TOTPSecretDocument.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { TOTPSecret } from "../../../types/TOTPSecret"; - -export interface TOTPSecretDocument { - userid: string; - secret: TOTPSecret; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/U2FRegistrationDocument.d.ts b/themes/triangles/server/src/lib/storage/U2FRegistrationDocument.d.ts deleted file mode 100644 index efec6cb1d..000000000 --- a/themes/triangles/server/src/lib/storage/U2FRegistrationDocument.d.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import { U2FRegistration } from "../../../types/U2FRegistration"; - -export interface U2FRegistrationDocument { - userId: string; - appId: string; - registration: U2FRegistration; -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/UserDataStore.spec.ts b/themes/triangles/server/src/lib/storage/UserDataStore.spec.ts deleted file mode 100644 index 66fb85461..000000000 --- a/themes/triangles/server/src/lib/storage/UserDataStore.spec.ts +++ /dev/null @@ -1,264 +0,0 @@ - -import * as Assert from "assert"; -import * as Sinon from "sinon"; -import * as MockDate from "mockdate"; -import BluebirdPromise = require("bluebird"); - -import { UserDataStore } from "./UserDataStore"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { CollectionStub } from "./CollectionStub.spec"; -import { CollectionFactoryStub } from "./CollectionFactoryStub.spec"; - -describe("storage/UserDataStore", function () { - let factory: CollectionFactoryStub; - let collection: CollectionStub; - let userId: string; - let appId: string; - let totpSecret: TOTPSecret; - let u2fRegistration: U2FRegistration; - - beforeEach(function () { - factory = new CollectionFactoryStub(); - collection = new CollectionStub(); - - userId = "user"; - appId = "https://myappId"; - - totpSecret = { - ascii: "abc", - base32: "ABCDKZLEFZGREJK", - otpauth_url: "totp://test", - google_auth_qr: "dummy", - hex: "dummy", - qr_code_ascii: "dummy", - qr_code_base32: "dummy", - qr_code_hex: "dummy" - }; - - u2fRegistration = { - keyHandle: "KEY_HANDLE", - publicKey: "publickey" - }; - }); - - it("should correctly creates collections", function () { - new UserDataStore(factory); - - Assert.equal(4, factory.buildStub.callCount); - Assert(factory.buildStub.calledWith("authentication_traces")); - Assert(factory.buildStub.calledWith("identity_validation_tokens")); - Assert(factory.buildStub.calledWith("u2f_registrations")); - Assert(factory.buildStub.calledWith("totp_secrets")); - }); - - describe("TOTP secrets collection", function () { - it("should save a totp secret", function () { - factory.buildStub.returns(collection); - collection.updateStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveTOTPSecret(userId, totpSecret) - .then(function (doc) { - Assert(collection.updateStub.calledOnce); - Assert(collection.updateStub.calledWith({ userId: userId }, { - userId: userId, - secret: totpSecret - }, { upsert: true })); - return BluebirdPromise.resolve(); - }); - }); - - it("should retrieve a totp secret", function () { - factory.buildStub.returns(collection); - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveTOTPSecret(userId) - .then(function (doc) { - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ userId: userId })); - return BluebirdPromise.resolve(); - }); - }); - }); - - describe("U2F secrets collection", function () { - it("should save a U2F secret", function () { - factory.buildStub.returns(collection); - collection.updateStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveU2FRegistration(userId, appId, u2fRegistration) - .then(function (doc) { - Assert(collection.updateStub.calledOnce); - Assert(collection.updateStub.calledWith({ - userId: userId, - appId: appId - }, { - userId: userId, - appId: appId, - registration: u2fRegistration - }, { upsert: true })); - return BluebirdPromise.resolve(); - }); - }); - - it("should retrieve a U2F secret", function () { - factory.buildStub.returns(collection); - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveU2FRegistration(userId, appId) - .then(function (doc) { - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - userId: userId, - appId: appId - })); - return BluebirdPromise.resolve(); - }); - }); - }); - - - describe("Regulator traces collection", function () { - it("should save a trace", function () { - factory.buildStub.returns(collection); - collection.insertStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.saveAuthenticationTrace(userId, true) - .then(function (doc) { - Assert(collection.insertStub.calledOnce); - Assert(collection.insertStub.calledWith({ - userId: userId, - date: Sinon.match.date, - isAuthenticationSuccessful: true - })); - return BluebirdPromise.resolve(); - }); - }); - - function should_retrieve_latest_authentication_traces(count: number) { - factory.buildStub.returns(collection); - collection.findStub.withArgs().returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - return dataStore.retrieveLatestAuthenticationTraces(userId, count) - .then(function (doc: AuthenticationTraceDocument[]) { - Assert(collection.findStub.calledOnce); - Assert(collection.findStub.calledWith({ - userId: userId, - }, { date: -1 }, count)); - return BluebirdPromise.resolve(); - }); - } - - it("should retrieve 3 latest failed authentication traces", function () { - should_retrieve_latest_authentication_traces(3); - }); - }); - - - describe("Identity validation collection", function () { - it("should save a identity validation token", function () { - factory.buildStub.returns(collection); - collection.insertStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - const maxAge = 400; - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - return dataStore.produceIdentityValidationToken(userId, token, challenge, maxAge) - .then(function (doc) { - Assert(collection.insertStub.calledOnce); - Assert(collection.insertStub.calledWith({ - userId: userId, - token: token, - challenge: challenge, - maxDate: Sinon.match.date - })); - return BluebirdPromise.resolve(); - }); - }); - - it("should consume an identity token successfully", function () { - factory.buildStub.returns(collection); - - MockDate.set(100); - - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve({ - userId: "USER", - token: token, - challenge: challenge, - maxDate: new Date() - })); - collection.removeStub.returns(BluebirdPromise.resolve()); - - const dataStore = new UserDataStore(factory); - - MockDate.set(80); - - return dataStore.consumeIdentityValidationToken(token, challenge) - .then(function (doc) { - MockDate.reset(); - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - token: token, - challenge: challenge - })); - - Assert(collection.removeStub.calledOnce); - Assert(collection.removeStub.calledWith({ - token: token, - challenge: challenge - })); - return BluebirdPromise.resolve(); - }); - }); - - it("should consume an expired identity token", function () { - factory.buildStub.returns(collection); - - MockDate.set(0); - - const token = "TOKEN"; - const challenge = "CHALLENGE"; - - collection.findOneStub.withArgs().returns(BluebirdPromise.resolve({ - userId: "USER", - token: token, - challenge: challenge, - maxDate: new Date() - })); - - const dataStore = new UserDataStore(factory); - - MockDate.set(80000); - - return dataStore.consumeIdentityValidationToken(token, challenge) - .then(function () { return BluebirdPromise.reject(new Error("should not be here")); }) - .catch(function () { - MockDate.reset(); - Assert(collection.findOneStub.calledOnce); - Assert(collection.findOneStub.calledWith({ - token: token, - challenge: challenge - })); - return BluebirdPromise.resolve(); - }); - }); - }); -}); diff --git a/themes/triangles/server/src/lib/storage/UserDataStore.ts b/themes/triangles/server/src/lib/storage/UserDataStore.ts deleted file mode 100644 index 27b0cddbd..000000000 --- a/themes/triangles/server/src/lib/storage/UserDataStore.ts +++ /dev/null @@ -1,143 +0,0 @@ -import * as BluebirdPromise from "bluebird"; -import * as path from "path"; -import { IUserDataStore } from "./IUserDataStore"; -import { ICollection } from "./ICollection"; -import { ICollectionFactory } from "./ICollectionFactory"; -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; - -// Constants - -const IDENTITY_VALIDATION_TOKENS_COLLECTION_NAME = "identity_validation_tokens"; -const AUTHENTICATION_TRACES_COLLECTION_NAME = "authentication_traces"; - -const U2F_REGISTRATIONS_COLLECTION_NAME = "u2f_registrations"; -const TOTP_SECRETS_COLLECTION_NAME = "totp_secrets"; - - -export interface U2FRegistrationKey { - userId: string; - appId: string; -} - -// Source - -export class UserDataStore implements IUserDataStore { - private u2fSecretCollection: ICollection; - private identityCheckTokensCollection: ICollection; - private authenticationTracesCollection: ICollection; - private totpSecretCollection: ICollection; - - private collectionFactory: ICollectionFactory; - - constructor(collectionFactory: ICollectionFactory) { - this.collectionFactory = collectionFactory; - - this.u2fSecretCollection = this.collectionFactory.build(U2F_REGISTRATIONS_COLLECTION_NAME); - this.identityCheckTokensCollection = this.collectionFactory.build(IDENTITY_VALIDATION_TOKENS_COLLECTION_NAME); - this.authenticationTracesCollection = this.collectionFactory.build(AUTHENTICATION_TRACES_COLLECTION_NAME); - this.totpSecretCollection = this.collectionFactory.build(TOTP_SECRETS_COLLECTION_NAME); - } - - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise { - const newDocument: U2FRegistrationDocument = { - userId: userId, - appId: appId, - registration: registration - }; - - const filter: U2FRegistrationKey = { - userId: userId, - appId: appId - }; - - return this.u2fSecretCollection.update(filter, newDocument, { upsert: true }); - } - - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise { - const filter: U2FRegistrationKey = { - userId: userId, - appId: appId - }; - return this.u2fSecretCollection.findOne(filter); - } - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - const newDocument: AuthenticationTraceDocument = { - userId: userId, - date: new Date(), - isAuthenticationSuccessful: isAuthenticationSuccessful, - }; - - return this.authenticationTracesCollection.insert(newDocument); - } - - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise { - const q = { - userId: userId - }; - - return this.authenticationTracesCollection.find(q, { date: -1 }, count); - } - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise { - const newDocument: IdentityValidationDocument = { - userId: userId, - token: token, - challenge: challenge, - maxDate: new Date(new Date().getTime() + maxAge) - }; - - return this.identityCheckTokensCollection.insert(newDocument); - } - - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise { - const that = this; - const filter = { - token: token, - challenge: challenge - }; - - let identityValidationDocument: IdentityValidationDocument; - - return this.identityCheckTokensCollection.findOne(filter) - .then(function (doc: IdentityValidationDocument) { - if (!doc) { - return BluebirdPromise.reject(new Error("Registration token does not exist")); - } - - identityValidationDocument = doc; - const current_date = new Date(); - if (current_date > doc.maxDate) - return BluebirdPromise.reject(new Error("Registration token is not valid anymore")); - - return that.identityCheckTokensCollection.remove(filter); - }) - .then(() => { - return BluebirdPromise.resolve(identityValidationDocument); - }); - } - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise { - const doc = { - userId: userId, - secret: secret - }; - - const filter = { - userId: userId - }; - return this.totpSecretCollection.update(filter, doc, { upsert: true }); - } - - retrieveTOTPSecret(userId: string): BluebirdPromise { - const filter = { - userId: userId - }; - return this.totpSecretCollection.findOne(filter); - } -} diff --git a/themes/triangles/server/src/lib/storage/UserDataStoreStub.spec.ts b/themes/triangles/server/src/lib/storage/UserDataStoreStub.spec.ts deleted file mode 100644 index 5ea27a2de..000000000 --- a/themes/triangles/server/src/lib/storage/UserDataStoreStub.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import Sinon = require("sinon"); -import BluebirdPromise = require("bluebird"); - -import { TOTPSecretDocument } from "./TOTPSecretDocument"; -import { U2FRegistrationDocument } from "./U2FRegistrationDocument"; -import { U2FRegistration } from "../../../types/U2FRegistration"; -import { TOTPSecret } from "../../../types/TOTPSecret"; -import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument"; -import { IdentityValidationDocument } from "./IdentityValidationDocument"; -import { IUserDataStore } from "./IUserDataStore"; - -export class UserDataStoreStub implements IUserDataStore { - saveU2FRegistrationStub: Sinon.SinonStub; - retrieveU2FRegistrationStub: Sinon.SinonStub; - saveAuthenticationTraceStub: Sinon.SinonStub; - retrieveLatestAuthenticationTracesStub: Sinon.SinonStub; - produceIdentityValidationTokenStub: Sinon.SinonStub; - consumeIdentityValidationTokenStub: Sinon.SinonStub; - saveTOTPSecretStub: Sinon.SinonStub; - retrieveTOTPSecretStub: Sinon.SinonStub; - - constructor() { - this.saveU2FRegistrationStub = Sinon.stub(); - this.retrieveU2FRegistrationStub = Sinon.stub(); - this.saveAuthenticationTraceStub = Sinon.stub(); - this.retrieveLatestAuthenticationTracesStub = Sinon.stub(); - this.produceIdentityValidationTokenStub = Sinon.stub(); - this.consumeIdentityValidationTokenStub = Sinon.stub(); - this.saveTOTPSecretStub = Sinon.stub(); - this.retrieveTOTPSecretStub = Sinon.stub(); - } - - saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise { - return this.saveU2FRegistrationStub(userId, appId, registration); - } - - retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise { - return this.retrieveU2FRegistrationStub(userId, appId); - } - - saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise { - return this.saveAuthenticationTraceStub(userId, isAuthenticationSuccessful); - } - - retrieveLatestAuthenticationTraces(userId: string, count: number): BluebirdPromise { - return this.retrieveLatestAuthenticationTracesStub(userId, count); - } - - produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise { - return this.produceIdentityValidationTokenStub(userId, token, challenge, maxAge); - } - - consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise { - return this.consumeIdentityValidationTokenStub(token, challenge); - } - - saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise { - return this.saveTOTPSecretStub(userId, secret); - } - - retrieveTOTPSecret(userId: string): BluebirdPromise { - return this.retrieveTOTPSecretStub(userId); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/mongo/MongoCollection.spec.ts b/themes/triangles/server/src/lib/storage/mongo/MongoCollection.spec.ts deleted file mode 100644 index 74a773a1e..000000000 --- a/themes/triangles/server/src/lib/storage/mongo/MongoCollection.spec.ts +++ /dev/null @@ -1,110 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import MongoDB = require("mongodb"); -import BluebirdPromise = require("bluebird"); -import { MongoClientStub } from "../../connectors/mongo/MongoClientStub.spec"; -import { MongoCollection } from "./MongoCollection"; - -describe("storage/mongo/MongoCollection", function () { - let mongoCollectionStub: any; - let mongoClientStub: MongoClientStub; - let findStub: Sinon.SinonStub; - let findOneStub: Sinon.SinonStub; - let insertOneStub: Sinon.SinonStub; - let updateStub: Sinon.SinonStub; - let removeStub: Sinon.SinonStub; - let countStub: Sinon.SinonStub; - const COLLECTION_NAME = "collection"; - - before(function () { - mongoClientStub = new MongoClientStub(); - mongoCollectionStub = Sinon.createStubInstance(require("mongodb").Collection as any); - findStub = mongoCollectionStub.find as Sinon.SinonStub; - findOneStub = mongoCollectionStub.findOne as Sinon.SinonStub; - insertOneStub = mongoCollectionStub.insertOne as Sinon.SinonStub; - updateStub = mongoCollectionStub.update as Sinon.SinonStub; - removeStub = mongoCollectionStub.remove as Sinon.SinonStub; - countStub = mongoCollectionStub.count as Sinon.SinonStub; - mongoClientStub.collectionStub.returns( - BluebirdPromise.resolve(mongoCollectionStub) - ); - }); - - describe("find", function () { - it("should find a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - findStub.returns({ - sort: Sinon.stub().returns({ - limit: Sinon.stub().returns({ - toArray: Sinon.stub().returns(BluebirdPromise.resolve([])) - }) - }) - }); - - return collection.find({ key: "KEY" }) - .then(function () { - Assert(findStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("findOne", function () { - it("should find one document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - findOneStub.returns(BluebirdPromise.resolve({})); - - return collection.findOne({ key: "KEY" }) - .then(function () { - Assert(findOneStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("insert", function () { - it("should insert a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - insertOneStub.returns(BluebirdPromise.resolve({})); - - return collection.insert({ key: "KEY" }) - .then(function () { - Assert(insertOneStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("update", function () { - it("should update a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - updateStub.returns(BluebirdPromise.resolve({})); - - return collection.update({ key: "KEY" }, { key: "KEY", value: 1 }) - .then(function () { - Assert(updateStub.calledWith({ key: "KEY" }, { key: "KEY", value: 1 })); - }); - }); - }); - - describe("remove", function () { - it("should remove a document in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - removeStub.returns(BluebirdPromise.resolve({})); - - return collection.remove({ key: "KEY" }) - .then(function () { - Assert(removeStub.calledWith({ key: "KEY" })); - }); - }); - }); - - describe("count", function () { - it("should count documents in the collection", function () { - const collection = new MongoCollection(COLLECTION_NAME, mongoClientStub); - countStub.returns(BluebirdPromise.resolve({})); - - return collection.count({ key: "KEY" }) - .then(function () { - Assert(countStub.calledWith({ key: "KEY" })); - }); - }); - }); -}); diff --git a/themes/triangles/server/src/lib/storage/mongo/MongoCollection.ts b/themes/triangles/server/src/lib/storage/mongo/MongoCollection.ts deleted file mode 100644 index 9771389f1..000000000 --- a/themes/triangles/server/src/lib/storage/mongo/MongoCollection.ts +++ /dev/null @@ -1,50 +0,0 @@ -import Bluebird = require("bluebird"); -import { ICollection } from "../ICollection"; -import MongoDB = require("mongodb"); -import { IMongoClient } from "../../connectors/mongo/IMongoClient"; - - -export class MongoCollection implements ICollection { - private mongoClient: IMongoClient; - private collectionName: string; - - constructor(collectionName: string, mongoClient: IMongoClient) { - this.collectionName = collectionName; - this.mongoClient = mongoClient; - } - - private collection(): Bluebird { - return this.mongoClient.collection(this.collectionName); - } - - find(query: any, sortKeys?: any, count?: number): Bluebird { - return this.collection() - .then((collection) => collection.find(query).sort(sortKeys).limit(count)) - .then((query) => query.toArray()); - } - - findOne(query: any): Bluebird { - return this.collection() - .then((collection) => collection.findOne(query)); - } - - update(query: any, updateQuery: any, options?: any): Bluebird { - return this.collection() - .then((collection) => collection.update(query, updateQuery, options)); - } - - remove(query: any): Bluebird { - return this.collection() - .then((collection) => collection.remove(query)); - } - - insert(document: any): Bluebird { - return this.collection() - .then((collection) => collection.insertOne(document)); - } - - count(query: any): Bluebird { - return this.collection() - .then((collection) => collection.count(query)); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts b/themes/triangles/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts deleted file mode 100644 index bd959cacb..000000000 --- a/themes/triangles/server/src/lib/storage/mongo/MongoCollectionFactory.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import { MongoClientStub } from "../../connectors/mongo/MongoClientStub.spec"; -import { MongoCollectionFactory } from "./MongoCollectionFactory"; - -describe("storage/mongo/MongoCollectionFactory", function () { - let mongoClient: MongoClientStub; - - before(function() { - mongoClient = new MongoClientStub(); - }); - - describe("create", function () { - it("should create a collection", function () { - const COLLECTION_NAME = "COLLECTION_NAME"; - - const factory = new MongoCollectionFactory(mongoClient); - Assert(factory.build(COLLECTION_NAME)); - }); - }); -}); diff --git a/themes/triangles/server/src/lib/storage/mongo/MongoCollectionFactory.ts b/themes/triangles/server/src/lib/storage/mongo/MongoCollectionFactory.ts deleted file mode 100644 index 14a8262c9..000000000 --- a/themes/triangles/server/src/lib/storage/mongo/MongoCollectionFactory.ts +++ /dev/null @@ -1,19 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ICollection } from "../ICollection"; -import { ICollectionFactory } from "../ICollectionFactory"; -import { MongoCollection } from "./MongoCollection"; -import path = require("path"); -import MongoDB = require("mongodb"); -import { IMongoClient } from "../../connectors/mongo/IMongoClient"; - -export class MongoCollectionFactory implements ICollectionFactory { - private mongoClient: IMongoClient; - - constructor(mongoClient: IMongoClient) { - this.mongoClient = mongoClient; - } - - build(collectionName: string): ICollection { - return new MongoCollection(collectionName, this.mongoClient); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/nedb/NedbCollection.spec.ts b/themes/triangles/server/src/lib/storage/nedb/NedbCollection.spec.ts deleted file mode 100644 index a69962b67..000000000 --- a/themes/triangles/server/src/lib/storage/nedb/NedbCollection.spec.ts +++ /dev/null @@ -1,136 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); - -import { NedbCollection } from "./NedbCollection"; - -describe("storage/nedb/NedbCollection", function () { - describe("insert", function () { - it("should insert one entry", function () { - const nedbOptions = { - inMemoryOnly: true - }; - const collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - - return collection.count({}).then(function (count: number) { - Assert.equal(1, count); - }); - }); - - it("should insert three entries", function () { - const nedbOptions = { - inMemoryOnly: true - }; - const collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - collection.insert({ key: "hello" }); - collection.insert({ key: "hey" }); - - return collection.count({}).then(function (count: number) { - Assert.equal(3, count); - }); - }); - }); - - describe("find", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "hello" }); - collection.insert({ key: "hey" }); - collection.insert({ key: "coucou", value: 2 }); - }); - - it("should find one hello", function () { - return collection.find({ key: "hello" }, { key: 1 }) - .then(function (docs: { key: string }[]) { - Assert.equal(1, docs.length); - Assert(docs[0].key == "hello"); - }); - }); - - it("should find two coucou", function () { - return collection.find({ key: "coucou" }, { value: 1 }) - .then(function (docs: { value: number }[]) { - Assert.equal(2, docs.length); - }); - }); - }); - - describe("findOne", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - collection.insert({ key: "coucou", value: 1 }); - }); - - it("should find two coucou", function () { - const doc = { key: "coucou", value: 1 }; - return collection.count(doc) - .then(function (count: number) { - Assert.equal(4, count); - return collection.findOne(doc); - }); - }); - }); - - describe("update", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou", value: 1 }); - }); - - it("should update the value", function () { - return collection.update({ key: "coucou" }, { key: "coucou", value: 2 }, { multi: true }) - .then(function () { - return collection.find({ key: "coucou" }); - }) - .then(function (docs: { key: string, value: number }[]) { - Assert.equal(1, docs.length); - Assert.equal(2, docs[0].value); - }); - }); - }); - - describe("update", function () { - let collection: NedbCollection; - before(function () { - const nedbOptions = { - inMemoryOnly: true - }; - collection = new NedbCollection(nedbOptions); - - collection.insert({ key: "coucou" }); - collection.insert({ key: "hello" }); - }); - - it("should update the value", function () { - return collection.remove({ key: "coucou" }) - .then(function () { - return collection.count({}); - }) - .then(function (count: number) { - Assert.equal(1, count); - }); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/nedb/NedbCollection.ts b/themes/triangles/server/src/lib/storage/nedb/NedbCollection.ts deleted file mode 100644 index 88a93ad05..000000000 --- a/themes/triangles/server/src/lib/storage/nedb/NedbCollection.ts +++ /dev/null @@ -1,47 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import { ICollection } from "../ICollection"; -import Nedb = require("nedb"); - -declare module "nedb" { - export class NedbAsync extends Nedb { - constructor(pathOrOptions?: string | Nedb.DataStoreOptions); - updateAsync(query: any, updateQuery: any, options?: Nedb.UpdateOptions): BluebirdPromise; - findOneAsync(query: any): BluebirdPromise; - insertAsync(newDoc: T): BluebirdPromise; - removeAsync(query: any): BluebirdPromise; - countAsync(query: any): BluebirdPromise; - } -} - -export class NedbCollection implements ICollection { - private collection: Nedb.NedbAsync; - - constructor(options: Nedb.DataStoreOptions) { - this.collection = BluebirdPromise.promisifyAll(new Nedb(options)) as Nedb.NedbAsync; - } - - find(query: any, sortKeys?: any, count?: number): BluebirdPromise { - const q = this.collection.find(query).sort(sortKeys).limit(count); - return BluebirdPromise.promisify(q.exec, { context: q })(); - } - - findOne(query: any): BluebirdPromise { - return this.collection.findOneAsync(query); - } - - update(query: any, updateQuery: any, options?: any): BluebirdPromise { - return this.collection.updateAsync(query, updateQuery, options); - } - - remove(query: any): BluebirdPromise { - return this.collection.removeAsync(query); - } - - insert(document: any): BluebirdPromise { - return this.collection.insertAsync(document); - } - - count(query: any): BluebirdPromise { - return this.collection.countAsync(query); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts b/themes/triangles/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts deleted file mode 100644 index da90c661f..000000000 --- a/themes/triangles/server/src/lib/storage/nedb/NedbCollectionFactory.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Sinon = require("sinon"); -import Assert = require("assert"); - -import { NedbCollectionFactory } from "./NedbCollectionFactory"; - -describe("storage/nedb/NedbCollectionFactory", function() { - it("should create a nedb collection", function() { - const nedbOptions = { - inMemoryOnly: true - }; - const factory = new NedbCollectionFactory(nedbOptions); - - const collection = factory.build("mycollection"); - Assert(collection); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/storage/nedb/NedbCollectionFactory.ts b/themes/triangles/server/src/lib/storage/nedb/NedbCollectionFactory.ts deleted file mode 100644 index 49c4dc853..000000000 --- a/themes/triangles/server/src/lib/storage/nedb/NedbCollectionFactory.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ICollection } from "../ICollection"; -import { ICollectionFactory } from "../ICollectionFactory"; -import { NedbCollection } from "./NedbCollection"; -import path = require("path"); -import Nedb = require("nedb"); - -export interface NedbOptions { - inMemoryOnly?: boolean; - directory?: string; -} - -export class NedbCollectionFactory implements ICollectionFactory { - private options: Nedb.DataStoreOptions; - - constructor(options: Nedb.DataStoreOptions) { - this.options = options; - } - - build(collectionName: string): ICollection { - const datastoreOptions: Nedb.DataStoreOptions = { - inMemoryOnly: this.options.inMemoryOnly || false, - autoload: true, - filename: (this.options.filename) ? path.resolve(this.options.filename, collectionName) : undefined - }; - - return new NedbCollection(datastoreOptions); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/stubs/express.spec.ts b/themes/triangles/server/src/lib/stubs/express.spec.ts deleted file mode 100644 index 48f15d7e1..000000000 --- a/themes/triangles/server/src/lib/stubs/express.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ - -import sinon = require("sinon"); -import express = require("express"); - -export interface RequestMock { - app?: any; - body?: any; - session?: any; - headers?: any; - get?: any; - query?: any; - originalUrl: string; -} - -export interface ResponseMock { - send: sinon.SinonStub | sinon.SinonSpy; - sendStatus: sinon.SinonStub; - sendFile: sinon.SinonStub; - sendfile: sinon.SinonStub; - status: sinon.SinonStub | sinon.SinonSpy; - json: sinon.SinonStub | sinon.SinonSpy; - links: sinon.SinonStub; - jsonp: sinon.SinonStub; - download: sinon.SinonStub; - contentType: sinon.SinonStub; - type: sinon.SinonStub; - format: sinon.SinonStub; - attachment: sinon.SinonStub; - set: sinon.SinonStub; - header: sinon.SinonStub; - headersSent: boolean; - get: sinon.SinonStub; - clearCookie: sinon.SinonStub; - cookie: sinon.SinonStub; - location: sinon.SinonStub; - redirect: sinon.SinonStub | sinon.SinonSpy; - render: sinon.SinonStub | sinon.SinonSpy; - locals: sinon.SinonStub; - charset: string; - vary: sinon.SinonStub; - app: any; - write: sinon.SinonStub; - writeContinue: sinon.SinonStub; - writeHead: sinon.SinonStub; - statusCode: number; - statusMessage: string; - setHeader: sinon.SinonStub; - setTimeout: sinon.SinonStub; - sendDate: boolean; - getHeader: sinon.SinonStub; -} - -export function RequestMock(): RequestMock { - return { - originalUrl: "/non-api/xxx", - app: { - get: sinon.stub() - }, - headers: { - "x-forwarded-for": "127.0.0.1" - }, - session: {} - }; -} -export function ResponseMock(): ResponseMock { - return { - send: sinon.stub(), - status: sinon.stub(), - json: sinon.stub(), - sendStatus: sinon.stub(), - links: sinon.stub(), - jsonp: sinon.stub(), - sendFile: sinon.stub(), - sendfile: sinon.stub(), - download: sinon.stub(), - contentType: sinon.stub(), - type: sinon.stub(), - format: sinon.stub(), - attachment: sinon.stub(), - set: sinon.stub(), - header: sinon.stub(), - headersSent: true, - get: sinon.stub(), - clearCookie: sinon.stub(), - cookie: sinon.stub(), - location: sinon.stub(), - redirect: sinon.stub(), - render: sinon.stub(), - locals: sinon.stub(), - charset: "utf-8", - vary: sinon.stub(), - app: sinon.stub(), - write: sinon.stub(), - writeContinue: sinon.stub(), - writeHead: sinon.stub(), - statusCode: 200, - statusMessage: "message", - setHeader: sinon.stub(), - setTimeout: sinon.stub(), - sendDate: true, - getHeader: sinon.stub() - }; -} diff --git a/themes/triangles/server/src/lib/stubs/ldapjs.spec.ts b/themes/triangles/server/src/lib/stubs/ldapjs.spec.ts deleted file mode 100644 index 045c0e11b..000000000 --- a/themes/triangles/server/src/lib/stubs/ldapjs.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ - -import Sinon = require("sinon"); - -export class LdapjsMock { - createClientStub: sinon.SinonStub; - - constructor() { - this.createClientStub = Sinon.stub(); - } - - createClient(params: any) { - return this.createClientStub(params); - } -} - -export class LdapjsClientMock { - bindStub: sinon.SinonStub; - unbindStub: sinon.SinonStub; - searchStub: sinon.SinonStub; - modifyStub: sinon.SinonStub; - onStub: sinon.SinonStub; - - constructor() { - this.bindStub = Sinon.stub(); - this.unbindStub = Sinon.stub(); - this.searchStub = Sinon.stub(); - this.modifyStub = Sinon.stub(); - this.onStub = Sinon.stub(); - } - - bind() { - return this.bindStub(); - } - - unbind() { - return this.unbindStub(); - } - - search() { - return this.searchStub(); - } - - modify() { - return this.modifyStub(); - } - - on() { - return this.onStub(); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/stubs/speakeasy.spec.ts b/themes/triangles/server/src/lib/stubs/speakeasy.spec.ts deleted file mode 100644 index 023614dcd..000000000 --- a/themes/triangles/server/src/lib/stubs/speakeasy.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import sinon = require("sinon"); - -export = { - totp: sinon.stub(), - generateSecret: sinon.stub() -}; diff --git a/themes/triangles/server/src/lib/stubs/u2f.spec.ts b/themes/triangles/server/src/lib/stubs/u2f.spec.ts deleted file mode 100644 index 234b28c11..000000000 --- a/themes/triangles/server/src/lib/stubs/u2f.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ - -import sinon = require("sinon"); - -export interface U2FMock { - request: sinon.SinonStub; - checkSignature: sinon.SinonStub; - checkRegistration: sinon.SinonStub; -} - -export function U2FMock(): U2FMock { - return { - request: sinon.stub(), - checkSignature: sinon.stub(), - checkRegistration: sinon.stub() - }; -} diff --git a/themes/triangles/server/src/lib/utils/HashGenerator.spec.ts b/themes/triangles/server/src/lib/utils/HashGenerator.spec.ts deleted file mode 100644 index f19619a65..000000000 --- a/themes/triangles/server/src/lib/utils/HashGenerator.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Assert = require("assert"); -import { HashGenerator } from "./HashGenerator"; - -describe("utils/HashGenerator", function () { - it("should compute correct ssha512 (password)", function () { - return HashGenerator.ssha512("password", 500000, "jgiCMRyGXzoqpxS3") - .then(function (hash: string) { - Assert.equal(hash, "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"); - }); - }); - - it("should compute correct ssha512 (test)", function () { - return HashGenerator.ssha512("test", 500000, "abcdefghijklmnop") - .then(function (hash: string) { - Assert.equal(hash, "{CRYPT}$6$rounds=500000$abcdefghijklmnop$sTlNGf0VO/HTQIOXemmaBbV28HUch/qhWOA1/4dsDj6CDQYhUgXbYSPL6gccAsWMr2zD5fFWwhKmPdG.yxphs."); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/utils/HashGenerator.ts b/themes/triangles/server/src/lib/utils/HashGenerator.ts deleted file mode 100644 index e67de32b7..000000000 --- a/themes/triangles/server/src/lib/utils/HashGenerator.ts +++ /dev/null @@ -1,23 +0,0 @@ -import BluebirdPromise = require("bluebird"); -import RandomString = require("randomstring"); -import Util = require("util"); -const crypt = require("crypt3"); - -export class HashGenerator { - static ssha512( - password: string, - rounds: number = 500000, - salt?: string): BluebirdPromise { - const saltSize = 16; - // $6 means SHA512 - const _salt = Util.format("$6$rounds=%d$%s", rounds, - (salt) ? salt : RandomString.generate(16)); - - const cryptAsync = BluebirdPromise.promisify(crypt); - - return cryptAsync(password, _salt) - .then(function (hash: string) { - return BluebirdPromise.resolve(Util.format("{CRYPT}%s", hash)); - }); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/utils/ObjectCloner.ts b/themes/triangles/server/src/lib/utils/ObjectCloner.ts deleted file mode 100644 index 3e125d749..000000000 --- a/themes/triangles/server/src/lib/utils/ObjectCloner.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export class ObjectCloner { - static clone(obj: any): any { - return JSON.parse(JSON.stringify(obj)); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/utils/SafeRedirection.spec.ts b/themes/triangles/server/src/lib/utils/SafeRedirection.spec.ts deleted file mode 100644 index 4126949fd..000000000 --- a/themes/triangles/server/src/lib/utils/SafeRedirection.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import Assert = require("assert"); -import Sinon = require("sinon"); -import { SafeRedirector } from "./SafeRedirection"; - -describe("web_server/middlewares/SafeRedirection", () => { - describe("Url is in protected domain", () => { - before(() => { - this.redirector = new SafeRedirector("example.com"); - this.res = {redirect: Sinon.stub()}; - }); - - it("should redirect to provided url", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.example.com:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://mysubdomain.example.com:8080/abc")); - }); - - it("should redirect to default url when wrong domain", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.domain.rtf:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://authelia.example.com")); - }); - - it("should redirect to default url when not terminating by domain", () => { - this.redirector.redirectOrElse(this.res, - "https://mysubdomain.example.com.rtf:8080/abc", - "https://authelia.example.com"); - Assert(this.res.redirect.calledWith("https://authelia.example.com")); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/utils/SafeRedirection.ts b/themes/triangles/server/src/lib/utils/SafeRedirection.ts deleted file mode 100644 index 9e6a32e0c..000000000 --- a/themes/triangles/server/src/lib/utils/SafeRedirection.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Express = require("express"); -import { DomainExtractor } from "../../../../shared/DomainExtractor"; -import { BelongToDomain } from "../../../../shared/BelongToDomain"; - - -export class SafeRedirector { - private domain: string; - - constructor(domain: string) { - this.domain = domain; - } - - redirectOrElse( - res: Express.Response, - url: string, - defaultUrl: string): void { - if (BelongToDomain(url, this.domain)) { - res.redirect(url); - } - res.redirect(defaultUrl); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/utils/URLDecomposer.spec.ts b/themes/triangles/server/src/lib/utils/URLDecomposer.spec.ts deleted file mode 100644 index cbb038738..000000000 --- a/themes/triangles/server/src/lib/utils/URLDecomposer.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { URLDecomposer } from "./URLDecomposer"; -import Assert = require("assert"); - -describe("utils/URLDecomposer", function () { - describe("test fromUrl", function () { - it("should return domain from https url", function () { - const d = URLDecomposer.fromUrl("https://www.example.com/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return domain from http url", function () { - const d = URLDecomposer.fromUrl("http://www.example.com/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return domain when url contains port", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080/test/abc"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/test/abc"); - }); - - it("should return default path when no path provided", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/"); - }); - - it("should return default path when provided", function () { - const d = URLDecomposer.fromUrl("https://www.example.com:8080/"); - Assert.equal(d.domain, "www.example.com"); - Assert.equal(d.path, "/"); - }); - - it("should return undefined when does not match", function () { - const d = URLDecomposer.fromUrl("https:///abc/test"); - Assert.equal(d, undefined); - }); - - it("should return undefined when does not match", function () { - const d = URLDecomposer.fromUrl("https:///abc/test"); - Assert.equal(d, undefined); - }); - }); -}); \ No newline at end of file diff --git a/themes/triangles/server/src/lib/utils/URLDecomposer.ts b/themes/triangles/server/src/lib/utils/URLDecomposer.ts deleted file mode 100644 index 9bdf2e9d2..000000000 --- a/themes/triangles/server/src/lib/utils/URLDecomposer.ts +++ /dev/null @@ -1,15 +0,0 @@ -export class URLDecomposer { - static fromUrl(url: string): {domain: string, path: string} { - if (!url) return; - const match = url.match(/https?:\/\/([a-z0-9_.-]+)(:[0-9]+)?(.*)/); - - if (!match) return; - - if (match[1] && !match[3]) { - return {domain: match[1], path: "/"}; - } else if (match[1] && match[3]) { - return {domain: match[1], path: match[3]}; - } - return; - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/web_server/Configurator.ts b/themes/triangles/server/src/lib/web_server/Configurator.ts deleted file mode 100644 index 6e404874a..000000000 --- a/themes/triangles/server/src/lib/web_server/Configurator.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Configuration } from "../configuration/schema/Configuration"; -import { GlobalDependencies } from "../../../types/Dependencies"; -import { SessionConfigurationBuilder } from - "../configuration/SessionConfigurationBuilder"; -import Path = require("path"); -import Express = require("express"); -import * as BodyParser from "body-parser"; -import { RestApi } from "./RestApi"; -import { WithHeadersLogged } from "./middlewares/WithHeadersLogged"; -import { ServerVariables } from "../ServerVariables"; -import Helmet = require("helmet"); - -const addRequestId = require("express-request-id")(); - -// Constants -const TRUST_PROXY = "trust proxy"; -const X_POWERED_BY = "x-powered-by"; -const VIEWS = "views"; -const VIEW_ENGINE = "view engine"; -const PUG = "pug"; - -export class Configurator { - static configure(config: Configuration, - app: Express.Application, - vars: ServerVariables, - deps: GlobalDependencies): void { - const viewsDirectory = Path.resolve(__dirname, "../../views"); - const publicHtmlDirectory = Path.resolve(__dirname, "../../public_html"); - - const expressSessionOptions = SessionConfigurationBuilder.build(config, deps); - - app.use(Express.static(publicHtmlDirectory)); - app.use(BodyParser.urlencoded({ extended: false })); - app.use(BodyParser.json()); - app.use(deps.session(expressSessionOptions)); - app.use(addRequestId); - app.use(WithHeadersLogged.middleware(vars.logger)); - app.disable(X_POWERED_BY); - app.enable(TRUST_PROXY); - app.use(Helmet()); - - app.set(VIEWS, viewsDirectory); - app.set(VIEW_ENGINE, PUG); - - RestApi.setup(app, vars); - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/web_server/RestApi.ts b/themes/triangles/server/src/lib/web_server/RestApi.ts deleted file mode 100644 index 9144a15b9..000000000 --- a/themes/triangles/server/src/lib/web_server/RestApi.ts +++ /dev/null @@ -1,125 +0,0 @@ -import Express = require("express"); - -import FirstFactorGet = require("../routes/firstfactor/get"); -import SecondFactorGet = require("../routes/secondfactor/get"); - -import FirstFactorPost = require("../routes/firstfactor/post"); -import LogoutGet = require("../routes/logout/get"); -import VerifyGet = require("../routes/verify/get"); -import TOTPSignGet = require("../routes/secondfactor/totp/sign/post"); - -import IdentityCheckMiddleware = require("../IdentityCheckMiddleware"); - -import TOTPRegistrationIdentityHandler from "../routes/secondfactor/totp/identity/RegistrationHandler"; -import U2FRegistrationIdentityHandler from "../routes/secondfactor/u2f/identity/RegistrationHandler"; -import ResetPasswordIdentityHandler from "../routes/password-reset/identity/PasswordResetHandler"; - -import U2FSignPost = require("../routes/secondfactor/u2f/sign/post"); -import U2FSignRequestGet = require("../routes/secondfactor/u2f/sign_request/get"); - -import U2FRegisterPost = require("../routes/secondfactor/u2f/register/post"); -import U2FRegisterRequestGet = require("../routes/secondfactor/u2f/register_request/get"); - -import ResetPasswordFormPost = require("../routes/password-reset/form/post"); -import ResetPasswordRequestPost = require("../routes/password-reset/request/get"); - -import Error401Get = require("../routes/error/401/get"); -import Error403Get = require("../routes/error/403/get"); -import Error404Get = require("../routes/error/404/get"); - -import LoggedIn = require("../routes/loggedin/get"); - -import { ServerVariables } from "../ServerVariables"; -import Endpoints = require("../../../../shared/api"); -import { RequireValidatedFirstFactor } from "./middlewares/RequireValidatedFirstFactor"; - -function setupTotp(app: Express.Application, vars: ServerVariables) { - app.post(Endpoints.SECOND_FACTOR_TOTP_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - TOTPSignGet.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - app.get(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - IdentityCheckMiddleware.register(app, - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET, - new TOTPRegistrationIdentityHandler(vars.logger, - vars.userDataStore, vars.totpHandler, vars.config.totp), - vars); -} - -function setupU2f(app: Express.Application, vars: ServerVariables) { - app.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FSignRequestGet.default(vars)); - - app.post(Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FSignPost.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FRegisterRequestGet.default(vars)); - - app.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, - RequireValidatedFirstFactor.middleware(vars.logger), - U2FRegisterPost.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - app.get(Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, - RequireValidatedFirstFactor.middleware(vars.logger)); - - IdentityCheckMiddleware.register(app, - Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, - Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, - new U2FRegistrationIdentityHandler(vars.logger), vars); -} - -function setupResetPassword(app: Express.Application, vars: ServerVariables) { - IdentityCheckMiddleware.register(app, - Endpoints.RESET_PASSWORD_IDENTITY_START_GET, - Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET, - new ResetPasswordIdentityHandler(vars.logger, vars.usersDatabase), - vars); - - app.get(Endpoints.RESET_PASSWORD_REQUEST_GET, - ResetPasswordRequestPost.default); - app.post(Endpoints.RESET_PASSWORD_FORM_POST, - ResetPasswordFormPost.default(vars)); -} - -function setupErrors(app: Express.Application, vars: ServerVariables) { - app.get(Endpoints.ERROR_401_GET, Error401Get.default(vars)); - app.get(Endpoints.ERROR_403_GET, Error403Get.default(vars)); - app.get(Endpoints.ERROR_404_GET, Error404Get.default); -} - -export class RestApi { - static setup(app: Express.Application, vars: ServerVariables): void { - app.get(Endpoints.FIRST_FACTOR_GET, FirstFactorGet.default(vars)); - - app.get(Endpoints.SECOND_FACTOR_GET, - RequireValidatedFirstFactor.middleware(vars.logger), - SecondFactorGet.default(vars)); - - app.get(Endpoints.LOGOUT_GET, LogoutGet.default(vars)); - - app.get(Endpoints.VERIFY_GET, VerifyGet.default(vars)); - app.post(Endpoints.FIRST_FACTOR_POST, FirstFactorPost.default(vars)); - - setupTotp(app, vars); - setupU2f(app, vars); - setupResetPassword(app, vars); - setupErrors(app, vars); - - app.get(Endpoints.LOGGED_IN, - RequireValidatedFirstFactor.middleware(vars.logger), - LoggedIn.default(vars)); - } -} diff --git a/themes/triangles/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts b/themes/triangles/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts deleted file mode 100644 index ecfd75765..000000000 --- a/themes/triangles/server/src/lib/web_server/middlewares/RequireValidatedFirstFactor.ts +++ /dev/null @@ -1,27 +0,0 @@ -import Express = require("express"); -import BluebirdPromise = require("bluebird"); -import ErrorReplies = require("../../ErrorReplies"); -import { IRequestLogger } from "../../logging/IRequestLogger"; -import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler"; -import Exceptions = require("../../Exceptions"); -import { Level } from "../../authentication/Level"; - -export class RequireValidatedFirstFactor { - static middleware(logger: IRequestLogger) { - return function (req: Express.Request, res: Express.Response, - next: Express.NextFunction): BluebirdPromise { - - return new BluebirdPromise(function (resolve, reject) { - const authSession = AuthenticationSessionHandler.get(req, logger); - if (!authSession.userid || authSession.authentication_level < Level.ONE_FACTOR) - return reject( - new Exceptions.FirstFactorValidationError( - "First factor has not been validated yet.")); - - next(); - resolve(); - }) - .catch(ErrorReplies.replyWithError401(req, res, logger)); - }; - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/lib/web_server/middlewares/WithHeadersLogged.ts b/themes/triangles/server/src/lib/web_server/middlewares/WithHeadersLogged.ts deleted file mode 100644 index 139db1141..000000000 --- a/themes/triangles/server/src/lib/web_server/middlewares/WithHeadersLogged.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Express = require("express"); -import { IRequestLogger } from "../../logging/IRequestLogger"; - -export class WithHeadersLogged { - static middleware(logger: IRequestLogger) { - return function (req: Express.Request, res: Express.Response, - next: Express.NextFunction): void { - logger.debug(req, "Headers = %s", JSON.stringify(req.headers)); - next(); - }; - } -} \ No newline at end of file diff --git a/themes/triangles/server/src/resources/email-template.ejs b/themes/triangles/server/src/resources/email-template.ejs old mode 100644 new mode 100755 diff --git a/themes/triangles/server/src/views/already-logged-in.pug b/themes/triangles/server/src/views/already-logged-in.pug old mode 100644 new mode 100755 diff --git a/themes/triangles/server/src/views/errors/.directory b/themes/triangles/server/src/views/errors/.directory old mode 100644 new mode 100755 diff --git a/themes/triangles/server/src/views/errors/401.pug b/themes/triangles/server/src/views/errors/401.pug old mode 100644 new mode 100755 diff --git a/themes/triangles/server/src/views/errors/403.pug b/themes/triangles/server/src/views/errors/403.pug old mode 100644 new mode 100755 diff --git a/themes/triangles/server/src/views/errors/404.pug b/themes/triangles/server/src/views/errors/404.pug old mode 100644 new mode 100755 diff --git a/themes/triangles/server/src/views/firstfactor.pug b/themes/triangles/server/src/views/firstfactor.pug old mode 100644 new mode 100755 diff --git a/themes/triangles/server/src/views/layout/layout.pug b/themes/triangles/server/src/views/layout/layout.pug old mode 100644 new mode 100755 diff --git a/themes/triangles/server/src/views/need-identity-validation.pug b/themes/triangles/server/src/views/need-identity-validation.pug old mode 100644 new mode 100755 diff --git a/themes/triangles/server/src/views/password-reset-form.pug b/themes/triangles/server/src/views/password-reset-form.pug old mode 100644 new mode 100755 diff --git a/themes/triangles/server/src/views/password-reset-request.pug b/themes/triangles/server/src/views/password-reset-request.pug old mode 100644 new mode 100755 diff --git a/themes/triangles/server/src/views/secondfactor.pug b/themes/triangles/server/src/views/secondfactor.pug old mode 100644 new mode 100755 diff --git a/themes/triangles/server/src/views/totp-register.pug b/themes/triangles/server/src/views/totp-register.pug old mode 100644 new mode 100755 diff --git a/themes/triangles/server/src/views/u2f-register.pug b/themes/triangles/server/src/views/u2f-register.pug old mode 100644 new mode 100755 diff --git a/themes/triangles/server/test/requests.ts b/themes/triangles/server/test/requests.ts deleted file mode 100644 index 93fa0de47..000000000 --- a/themes/triangles/server/test/requests.ts +++ /dev/null @@ -1,94 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import request = require("request"); -import assert = require("assert"); -import express = require("express"); -import nodemailer = require("nodemailer"); -import Endpoints = require("../../shared/api"); - -declare module "request" { - export interface RequestAPI { - getAsync(uri: string, options?: RequiredUriUrl): BluebirdPromise; - getAsync(uri: string): BluebirdPromise; - getAsync(options: RequiredUriUrl & CoreOptions): BluebirdPromise; - - postAsync(uri: string, options?: CoreOptions): BluebirdPromise; - postAsync(uri: string): BluebirdPromise; - postAsync(options: RequiredUriUrl & CoreOptions): BluebirdPromise; - } -} - -const requestAsync: typeof request = BluebirdPromise.promisifyAll(request) as typeof request; - -export = function (port: number) { - const PORT = port; - const BASE_URL = "http://localhost:" + PORT; - - function execute_totp(jar: request.CookieJar, token: string) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_TOTP_POST, - jar: jar, - form: { - token: token - } - }); - } - - function execute_u2f_authentication(jar: request.CookieJar) { - return requestAsync.getAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, - jar: jar - }) - .then(function (res: request.RequestResponse) { - assert.equal(res.statusCode, 200); - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_POST, - jar: jar, - form: { - } - }); - }); - } - - function execute_verification(jar: request.CookieJar) { - return requestAsync.getAsync({ url: BASE_URL + Endpoints.VERIFY_GET, jar: jar }); - } - - function execute_login(jar: request.CookieJar) { - return requestAsync.getAsync({ url: BASE_URL + Endpoints.FIRST_FACTOR_GET, jar: jar }); - } - - function execute_first_factor(jar: request.CookieJar) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.FIRST_FACTOR_POST, - jar: jar, - form: { - username: "test_ok", - password: "password" - } - }); - } - - function execute_failing_first_factor(jar: request.CookieJar) { - return requestAsync.postAsync({ - url: BASE_URL + Endpoints.FIRST_FACTOR_POST, - jar: jar, - form: { - username: "test_nok", - password: "password" - } - }); - } - - return { - login: execute_login, - verify: execute_verification, - u2f_authentication: execute_u2f_authentication, - first_factor: execute_first_factor, - failing_first_factor: execute_failing_first_factor, - totp: execute_totp, - }; -}; - diff --git a/themes/triangles/server/tsconfig.json b/themes/triangles/server/tsconfig.json deleted file mode 100644 index ebe98c5ed..000000000 --- a/themes/triangles/server/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "moduleResolution": "node", - "noImplicitAny": true, - "sourceMap": true, - "removeComments": true, - "outDir": "../dist", - "baseUrl": ".", - "paths": { - "*": [ - "./types/*", - "../shared/types/*" - ] - } - }, - "exclude": [ - "src/**/*.spec.ts" - ] -} diff --git a/themes/triangles/server/tslint.json b/themes/triangles/server/tslint.json deleted file mode 100644 index c2c1b7501..000000000 --- a/themes/triangles/server/tslint.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "no-var-keyword": true, - "quotemark": [ - true, - "double", - "avoid-escape" - ], - "semicolon": [ - true, - "always", - "ignore-bound-class-methods" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-module", - "check-separator", - "check-type" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "onespace", - "index-signature": "onespace", - "parameter": "onespace", - "property-declaration": "onespace", - "variable-declaration": "onespace" - } - ], - "no-internal-module": true, - "no-trailing-whitespace": true, - "no-null-keyword": true, - "prefer-const": true, - "jsdoc-format": true - } -} diff --git a/themes/triangles/server/types/.directory b/themes/triangles/server/types/.directory deleted file mode 100644 index 1e65000ea..000000000 --- a/themes/triangles/server/types/.directory +++ /dev/null @@ -1,4 +0,0 @@ -[Dolphin] -Timestamp=2018,12,17,20,58,27 -Version=3 -ViewMode=1 diff --git a/themes/triangles/server/types/AuthenticationSession.ts b/themes/triangles/server/types/AuthenticationSession.ts deleted file mode 100644 index bbed0e715..000000000 --- a/themes/triangles/server/types/AuthenticationSession.ts +++ /dev/null @@ -1,18 +0,0 @@ -import U2f = require("u2f"); -import { Level } from "../src/lib/authentication/Level"; - -export interface AuthenticationSession { - userid: string; - authentication_level: Level; - keep_me_logged_in: boolean; - last_activity_datetime: number; - identity_check?: { - challenge: string; - userid: string; - }; - register_request?: U2f.Request; - sign_request?: U2f.Request; - email: string; - groups: string[]; - redirect?: string; -} \ No newline at end of file diff --git a/themes/triangles/server/types/Dependencies.ts b/themes/triangles/server/types/Dependencies.ts deleted file mode 100644 index f20404dbc..000000000 --- a/themes/triangles/server/types/Dependencies.ts +++ /dev/null @@ -1,29 +0,0 @@ -import winston = require("winston"); -import speakeasy = require("speakeasy"); -import nodemailer = require("nodemailer"); -import session = require("express-session"); -import nedb = require("nedb"); -import ldapjs = require("ldapjs"); -import u2f = require("u2f"); -import RedisSession = require("connect-redis"); -import Redis = require("redis"); - -export type Speakeasy = typeof speakeasy; -export type Winston = typeof winston; -export type Session = typeof session; -export type Nedb = typeof nedb; -export type Ldapjs = typeof ldapjs; -export type U2f = typeof u2f; -export type ConnectRedis = typeof RedisSession; -export type Redis = typeof Redis; - -export interface GlobalDependencies { - u2f: U2f; - ldapjs: Ldapjs; - session: Session; - Redis: Redis; - ConnectRedis: ConnectRedis; - winston: Winston; - speakeasy: Speakeasy; - nedb: Nedb; -} \ No newline at end of file diff --git a/themes/triangles/server/types/Identity.ts b/themes/triangles/server/types/Identity.ts deleted file mode 100644 index e985984e2..000000000 --- a/themes/triangles/server/types/Identity.ts +++ /dev/null @@ -1,6 +0,0 @@ - - -export interface Identity { - userid: string; - email: string; -} \ No newline at end of file diff --git a/themes/triangles/server/types/TOTPSecret.ts b/themes/triangles/server/types/TOTPSecret.ts deleted file mode 100644 index d6775f2f0..000000000 --- a/themes/triangles/server/types/TOTPSecret.ts +++ /dev/null @@ -1,11 +0,0 @@ - -export interface TOTPSecret { - ascii: string; - hex: string; - base32: string; - qr_code_ascii: string; - qr_code_hex: string; - qr_code_base32: string; - google_auth_qr: string; - otpauth_url: string; - } \ No newline at end of file diff --git a/themes/triangles/server/types/U2FRegistration.ts b/themes/triangles/server/types/U2FRegistration.ts deleted file mode 100644 index b6080af07..000000000 --- a/themes/triangles/server/types/U2FRegistration.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export interface U2FRegistration { - keyHandle: string; - publicKey: string; -} \ No newline at end of file diff --git a/themes/triangles/server/types/dovehash.d.ts b/themes/triangles/server/types/dovehash.d.ts deleted file mode 100644 index c354609c0..000000000 --- a/themes/triangles/server/types/dovehash.d.ts +++ /dev/null @@ -1,4 +0,0 @@ - -declare module "dovehash" { - function encode(algo: string, text: string): string; -} \ No newline at end of file diff --git a/themes/triangles/server/types/speakeasy.d.ts b/themes/triangles/server/types/speakeasy.d.ts deleted file mode 100644 index 6ea06948b..000000000 --- a/themes/triangles/server/types/speakeasy.d.ts +++ /dev/null @@ -1,96 +0,0 @@ -declare module "speakeasy" { - export = speakeasy - - interface SharedOptions { - encoding?: string - algorithm?: string - } - - interface DigestOptions extends SharedOptions { - secret: string - counter: number - } - - interface HOTPOptions extends SharedOptions { - secret: string - counter: number - digest?: Buffer - digits?: number - } - - interface HOTPVerifyOptions extends SharedOptions { - secret: string - token: string - counter: number - digits?: number - window?: number - } - - interface TOTPOptions extends SharedOptions { - secret: string - time?: number - step?: number - epoch?: number - counter?: number - digits?: number - } - - interface TOTPVerifyOptions extends SharedOptions { - secret: string - token: string - time?: number - step?: number - epoch?: number - counter?: number - digits?: number - window?: number - } - - interface GenerateSecretOptions { - length?: number - symbols?: boolean - otpauth_url?: boolean - name?: string - issuer?: string - } - - interface GeneratedSecret { - ascii: string - hex: string - base32: string - qr_code_ascii: string - qr_code_hex: string - qr_code_base32: string - google_auth_qr: string - otpauth_url: string - } - - interface OTPAuthURLOptions extends SharedOptions { - secret: string - label: string - type?: string - counter?: number - issuer?: string - digits?: number - period?: number - } - - interface Speakeasy { - digest: (options: DigestOptions) => Buffer - hotp: { - (options: HOTPOptions): string, - verifyDelta: (options: HOTPVerifyOptions) => boolean, - verify: (options: HOTPVerifyOptions) => boolean, - } - totp: { - (options: TOTPOptions): string - verifyDelta: (options: TOTPVerifyOptions) => boolean, - verify: (options: TOTPVerifyOptions) => boolean, - } - generateSecret: (options?: GenerateSecretOptions) => GeneratedSecret - generateSecretASCII: (length?: number, symbols?: boolean) => string - otpauthURL: (options: OTPAuthURLOptions) => string - } - - const speakeasy: Speakeasy -} \ No newline at end of file diff --git a/users_database.yml b/users_database.yml old mode 100644 new mode 100755