From ddf1e48535330205407505a8c746b34a9788eddb Mon Sep 17 00:00:00 2001 From: Clement Michaud Date: Thu, 25 May 2017 15:09:29 +0200 Subject: [PATCH] Refactor client to make it responsive and testable --- .gitignore | 5 + .travis.yml | 2 +- Dockerfile | 2 +- Gruntfile.js | 127 +- README.md | 2 + config.template.yml | 2 +- doc/api_data.js | 1078 ++++++++--------- doc/api_data.json | 1078 ++++++++--------- doc/api_project.js | 8 +- doc/api_project.json | 8 +- doc/css/style.css | 1 + doc/index.html | 4 +- doc/locales/locale.js | 2 + doc/locales/tr.js | 25 + doc/locales/vi.js | 25 + doc/utils/send_sample_request.js | 4 +- docker-compose.dev.yml | 4 +- example/ldap/base.ldif | 2 +- example/nginx_conf/nginx.conf | 23 +- images/email_confirmation.png | Bin 0 -> 26317 bytes images/first_factor.png | Bin 37121 -> 20857 bytes images/reset_password.png | Bin 0 -> 25508 bytes images/second_factor.png | Bin 64926 -> 23759 bytes images/secret-key.png | Bin 2081 -> 0 bytes images/totp.png | Bin 76258 -> 27934 bytes images/u2f.png | Bin 42811 -> 22513 bytes package.json | 61 +- src/client/css/00-bootstrap.min.css | 6 + src/client/css/01-main.css | 4 + src/client/css/02-login.css | 101 ++ src/client/css/03-errors.css | 12 + src/client/css/03-password-reset-form.css | 4 + src/client/css/03-password-reset-request.css | 4 + src/client/css/03-totp-register.css | 12 + src/client/css/03-u2f-register.css | 5 + .../firstfactor/FirstFactorValidator.ts | 20 + src/client/firstfactor/UISelectors.ts | 3 + src/client/firstfactor/index.ts | 39 + src/client/img/icon.png | Bin 0 -> 814 bytes src/client/img/mail.png | Bin 0 -> 3545 bytes src/client/img/padlock.png | Bin 0 -> 3265 bytes src/client/img/password.png | Bin 0 -> 2178 bytes src/{public_html => client}/img/pendrive.png | Bin src/client/img/success.png | Bin 0 -> 3147 bytes src/client/img/user.png | Bin 0 -> 2933 bytes src/client/img/warning.png | Bin 0 -> 4038 bytes src/client/index.ts | 38 + src/client/reset-password/constants.ts | 2 + .../reset-password/reset-password-form.ts | 49 + .../reset-password/reset-password-request.ts | 49 + src/client/secondfactor/TOTPValidator.ts | 22 + src/client/secondfactor/U2FValidator.ts | 61 + src/client/secondfactor/constants.ts | 5 + src/client/secondfactor/index.ts | 57 + .../js => client/thirdparties}/qrcode.min.js | 0 src/client/totp-register/totp-register.ts | 11 + src/client/totp-register/ui-selector.ts | 2 + src/client/u2f-register/u2f-register.ts | 53 + src/lib/IdentityValidator.ts | 156 --- src/lib/RestApi.ts | 282 ----- src/lib/Server.ts | 94 -- src/lib/routes.ts | 41 - src/lib/routes/AuthenticationValidator.ts | 53 - src/lib/routes/DenyNotLogged.ts | 19 - src/lib/routes/FirstFactor.ts | 82 -- src/lib/routes/PasswordReset.ts | 81 -- src/lib/routes/SecondFactorRoutes.ts | 28 - src/lib/routes/TOTPAuthenticator.ts | 49 - src/lib/routes/TOTPRegistration.ts | 86 -- src/lib/routes/U2FAuthenticationProcess.ts | 84 -- src/lib/routes/U2FRegistration.ts | 51 - src/lib/routes/U2FRegistrationProcess.ts | 89 -- src/lib/routes/U2FRoutes.ts | 19 - src/lib/routes/u2f_common.ts | 39 - src/public_html/css/login.css | 126 -- src/public_html/js/jquery.min.js | 4 - src/public_html/js/login.js | 286 ----- src/public_html/js/notify.min.js | 1 - src/public_html/js/reset-password-form.js | 47 - src/public_html/js/reset-password.js | 51 - src/public_html/js/totp-register.js | 42 - src/public_html/js/u2f-api.js | 748 ------------ src/public_html/js/u2f-register.js | 67 - src/server/endpoints.ts | 296 +++++ src/{ => server}/index.ts | 2 +- .../lib/AuthenticationRegulator.ts | 2 +- src/server/lib/AuthenticationSession.ts | 40 + src/server/lib/AuthenticationValidator.ts | 18 + src/{ => server}/lib/ConfigurationAdapter.ts | 2 +- src/server/lib/ErrorReplies.ts | 26 + src/{ => server}/lib/Exceptions.ts | 22 +- src/server/lib/FirstFactorValidator.ts | 14 + src/server/lib/IdentityCheckMiddleware.ts | 130 ++ .../lib/IdentityCheckPreValidationTemplate.ts | 3 + src/{ => server}/lib/LdapClient.ts | 24 +- src/server/lib/RestApi.ts | 73 ++ src/server/lib/Server.ts | 70 ++ src/server/lib/ServerVariables.ts | 103 ++ src/{ => server}/lib/TOTPGenerator.ts | 6 +- src/{ => server}/lib/TOTPValidator.ts | 4 +- src/{ => server}/lib/UserDataStore.ts | 54 +- .../lib/access_control/AccessController.ts | 10 +- .../lib/access_control/PatternBuilder.ts | 8 +- .../lib/notifiers/FileSystemNotifier.ts | 6 +- .../lib/notifiers/GMailNotifier.ts | 9 +- src/{ => server}/lib/notifiers/INotifier.ts | 2 +- .../lib/notifiers/NotifierFactory.ts | 4 +- src/server/lib/routes/FirstFactorBlocker.ts | 25 + src/server/lib/routes/error/401/get.ts | 7 + src/server/lib/routes/error/403/get.ts | 7 + src/server/lib/routes/error/404/get.ts | 7 + src/server/lib/routes/firstfactor/get.ts | 23 + src/server/lib/routes/firstfactor/post.ts | 69 ++ src/server/lib/routes/logout/get.ts | 10 + .../lib/routes/password-reset/constants.ts | 2 + .../lib/routes/password-reset/form/post.ts | 38 + .../identity/PasswordResetHandler.ts | 56 + .../lib/routes/password-reset/request/get.ts | 13 + src/server/lib/routes/secondfactor/get.ts | 17 + .../lib/routes/secondfactor/redirect.ts | 15 + .../lib/routes/secondfactor/totp/constants.ts | 4 + .../totp/identity/RegistrationHandler.ts | 89 ++ .../lib/routes/secondfactor/totp/sign/post.ts | 42 + .../lib/routes/secondfactor/u2f/U2FCommon.ts | 11 + .../u2f/identity/RegistrationHandler.ts | 63 + .../routes/secondfactor/u2f/register/post.ts | 65 + .../secondfactor/u2f/register_request/get.ts | 41 + .../lib/routes/secondfactor/u2f/sign/post.ts | 51 + .../u2f/sign_request/SignMessage.ts | 7 + .../secondfactor/u2f/sign_request/get.ts | 53 + src/server/lib/routes/verify/get.ts | 51 + src/{ => server}/resources/email-template.ejs | 0 src/server/views/already-logged-in.pug | 9 + src/server/views/errors/401.pug | 11 + src/server/views/errors/403.pug | 11 + src/server/views/errors/404.pug | 11 + src/server/views/firstfactor.pug | 20 + src/server/views/layout/layout.pug | 27 + src/server/views/need-identity-validation.pug | 8 + src/server/views/password-reset-form.pug | 22 + src/server/views/password-reset-request.pug | 22 + src/server/views/secondfactor.pug | 26 + src/server/views/totp-register.pug | 19 + src/server/views/u2f-register.pug | 14 + src/{lib => types}/Configuration.ts | 0 src/types/Dependencies.ts | 4 +- src/types/ILogger.ts | 7 - src/types/TOTPSecret.ts | 2 +- src/types/authdog.d.ts | 69 -- src/types/jquery-notify.d.ts | 4 + src/types/request-async.d.ts | 5 +- src/types/u2f-api.d.ts | 63 + src/types/u2f.d.ts | 45 + src/views/head.ejs | 1 - src/views/login.ejs | 35 - src/views/reset-password-form.ejs | 18 - src/views/reset-password.ejs | 19 - src/views/scripts.ejs | 2 - src/views/totp-register.ejs | 19 - src/views/u2f-register.ejs | 16 - .../firstfactor/FirstFactorValidator.test.ts | 48 + test/client/firstfactor/login.test.ts | 87 ++ test/client/mocks/jquery.ts | 39 + test/client/mocks/u2f-api.ts | 14 + .../client/secondfactor/TOTPValidator.test.ts | 37 + test/client/secondfactor/U2FValidator.test.ts | 110 ++ .../totp-register/totp-register.test.ts | 31 + test/integration/test_server.js | 156 --- test/integration/test_server.ts | 157 +++ .../AuthenticationRegulator.test.ts | 6 +- test/server/IdentityCheckMiddleware.test.ts | 173 +++ test/{unitary => server}/LdapClient.test.ts | 20 +- test/{unitary => server}/Server.test.ts | 131 +- .../{unitary => server}/TOTPValidator.test.ts | 2 +- .../{unitary => server}/UserDataStore.test.ts | 27 +- .../access_control/AccessController.test.ts | 4 +- .../access_control/PatternBuilder.test.ts | 4 +- .../config_adapter.test.ts | 4 +- .../data_persistence.test.ts | 38 +- .../mocks/AccessController.ts | 0 .../mocks/AuthenticationRegulator.ts | 0 .../mocks/IdentityValidator.ts | 14 +- test/{unitary => server}/mocks/LdapClient.ts | 0 test/{unitary => server}/mocks/Notifier.ts | 0 test/server/mocks/ServerVariablesMock.ts | 34 + .../mocks/TOTPValidator.ts | 0 .../mocks/UserDataStore.ts | 0 test/{unitary => server}/mocks/express.ts | 2 +- test/{unitary => server}/mocks/ldapjs.ts | 0 test/{unitary => server}/mocks/nodemailer.ts | 0 test/{unitary => server}/mocks/speakeasy.ts | 0 test/server/mocks/u2f.ts | 16 + .../notifiers/FileSystemNotifier.test.ts | 5 +- .../notifiers/GMailNotifier.test.ts | 5 +- .../notifiers/NotifierFactory.test.ts | 6 +- test/{unitary => server}/requests.ts | 63 +- .../routes/firstfactor/post.test.ts} | 84 +- .../identity/PasswordResetHandler.test.ts | 110 ++ .../server/routes/password-reset/post.test.ts | 123 ++ .../totp/register/RegistrationHandler.test.ts | 90 ++ .../secondfactor/totp/sign/post.test.ts | 93 ++ .../u2f/identity/RegistrationHandler.test.ts | 91 ++ .../secondfactor/u2f/register/post.test.ts | 145 +++ .../u2f/register_request/get.test.ts | 96 ++ .../routes/secondfactor/u2f/sign/post.test.ts | 100 ++ .../secondfactor/u2f/sign_request/get.test.ts | 83 ++ .../routes/verify/get.test.ts} | 82 +- .../{unitary => server}/server_config.test.ts | 6 +- .../authentication_audit.test.ts | 2 +- .../user_data_store/totp_secret.test.ts | 2 +- test/unitary/IdentityValidator.test.ts | 242 ---- test/unitary/mocks/authdog.ts | 19 - test/unitary/routes/DenyNotLogged.test.ts | 82 -- test/unitary/routes/PasswordReset.test.ts | 151 --- test/unitary/routes/TOTPAuthenticator.test.ts | 90 -- test/unitary/routes/TOTPRegistration.test.ts | 137 --- test/unitary/routes/U2FRegistration.test.ts | 83 -- test/unitary/routes/U2FRoutes.test.ts | 278 ----- tsconfig.json | 9 +- 219 files changed, 5708 insertions(+), 5573 deletions(-) create mode 100644 doc/locales/tr.js create mode 100644 doc/locales/vi.js create mode 100644 images/email_confirmation.png create mode 100644 images/reset_password.png delete mode 100644 images/secret-key.png create mode 100644 src/client/css/00-bootstrap.min.css create mode 100644 src/client/css/01-main.css create mode 100644 src/client/css/02-login.css create mode 100644 src/client/css/03-errors.css create mode 100644 src/client/css/03-password-reset-form.css create mode 100644 src/client/css/03-password-reset-request.css create mode 100644 src/client/css/03-totp-register.css create mode 100644 src/client/css/03-u2f-register.css create mode 100644 src/client/firstfactor/FirstFactorValidator.ts create mode 100644 src/client/firstfactor/UISelectors.ts create mode 100644 src/client/firstfactor/index.ts create mode 100644 src/client/img/icon.png create mode 100644 src/client/img/mail.png create mode 100644 src/client/img/padlock.png create mode 100644 src/client/img/password.png rename src/{public_html => client}/img/pendrive.png (100%) create mode 100644 src/client/img/success.png create mode 100644 src/client/img/user.png create mode 100644 src/client/img/warning.png create mode 100644 src/client/index.ts create mode 100644 src/client/reset-password/constants.ts create mode 100644 src/client/reset-password/reset-password-form.ts create mode 100644 src/client/reset-password/reset-password-request.ts create mode 100644 src/client/secondfactor/TOTPValidator.ts create mode 100644 src/client/secondfactor/U2FValidator.ts create mode 100644 src/client/secondfactor/constants.ts create mode 100644 src/client/secondfactor/index.ts rename src/{public_html/js => client/thirdparties}/qrcode.min.js (100%) create mode 100644 src/client/totp-register/totp-register.ts create mode 100644 src/client/totp-register/ui-selector.ts create mode 100644 src/client/u2f-register/u2f-register.ts delete mode 100644 src/lib/IdentityValidator.ts delete mode 100644 src/lib/RestApi.ts delete mode 100644 src/lib/Server.ts delete mode 100644 src/lib/routes.ts delete mode 100644 src/lib/routes/AuthenticationValidator.ts delete mode 100644 src/lib/routes/DenyNotLogged.ts delete mode 100644 src/lib/routes/FirstFactor.ts delete mode 100644 src/lib/routes/PasswordReset.ts delete mode 100644 src/lib/routes/SecondFactorRoutes.ts delete mode 100644 src/lib/routes/TOTPAuthenticator.ts delete mode 100644 src/lib/routes/TOTPRegistration.ts delete mode 100644 src/lib/routes/U2FAuthenticationProcess.ts delete mode 100644 src/lib/routes/U2FRegistration.ts delete mode 100644 src/lib/routes/U2FRegistrationProcess.ts delete mode 100644 src/lib/routes/U2FRoutes.ts delete mode 100644 src/lib/routes/u2f_common.ts delete mode 100644 src/public_html/css/login.css delete mode 100644 src/public_html/js/jquery.min.js delete mode 100644 src/public_html/js/login.js delete mode 100644 src/public_html/js/notify.min.js delete mode 100644 src/public_html/js/reset-password-form.js delete mode 100644 src/public_html/js/reset-password.js delete mode 100644 src/public_html/js/totp-register.js delete mode 100644 src/public_html/js/u2f-api.js delete mode 100644 src/public_html/js/u2f-register.js create mode 100644 src/server/endpoints.ts rename src/{ => server}/index.ts (96%) rename src/{ => server}/lib/AuthenticationRegulator.ts (96%) create mode 100644 src/server/lib/AuthenticationSession.ts create mode 100644 src/server/lib/AuthenticationValidator.ts rename src/{ => server}/lib/ConfigurationAdapter.ts (95%) create mode 100644 src/server/lib/ErrorReplies.ts rename src/{ => server}/lib/Exceptions.ts (68%) create mode 100644 src/server/lib/FirstFactorValidator.ts create mode 100644 src/server/lib/IdentityCheckMiddleware.ts create mode 100644 src/server/lib/IdentityCheckPreValidationTemplate.ts rename src/{ => server}/lib/LdapClient.ts (90%) create mode 100644 src/server/lib/RestApi.ts create mode 100644 src/server/lib/Server.ts create mode 100644 src/server/lib/ServerVariables.ts rename src/{ => server}/lib/TOTPGenerator.ts (62%) rename src/{ => server}/lib/TOTPValidator.ts (84%) rename src/{ => server}/lib/UserDataStore.ts (80%) rename src/{ => server}/lib/access_control/AccessController.ts (80%) rename src/{ => server}/lib/access_control/PatternBuilder.ts (94%) rename src/{ => server}/lib/notifiers/FileSystemNotifier.ts (70%) rename src/{ => server}/lib/notifiers/GMailNotifier.ts (75%) rename src/{ => server}/lib/notifiers/INotifier.ts (76%) rename src/{ => server}/lib/notifiers/NotifierFactory.ts (78%) create mode 100644 src/server/lib/routes/FirstFactorBlocker.ts create mode 100644 src/server/lib/routes/error/401/get.ts create mode 100644 src/server/lib/routes/error/403/get.ts create mode 100644 src/server/lib/routes/error/404/get.ts create mode 100644 src/server/lib/routes/firstfactor/get.ts create mode 100644 src/server/lib/routes/firstfactor/post.ts create mode 100644 src/server/lib/routes/logout/get.ts create mode 100644 src/server/lib/routes/password-reset/constants.ts create mode 100644 src/server/lib/routes/password-reset/form/post.ts create mode 100644 src/server/lib/routes/password-reset/identity/PasswordResetHandler.ts create mode 100644 src/server/lib/routes/password-reset/request/get.ts create mode 100644 src/server/lib/routes/secondfactor/get.ts create mode 100644 src/server/lib/routes/secondfactor/redirect.ts create mode 100644 src/server/lib/routes/secondfactor/totp/constants.ts create mode 100644 src/server/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts create mode 100644 src/server/lib/routes/secondfactor/totp/sign/post.ts create mode 100644 src/server/lib/routes/secondfactor/u2f/U2FCommon.ts create mode 100644 src/server/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts create mode 100644 src/server/lib/routes/secondfactor/u2f/register/post.ts create mode 100644 src/server/lib/routes/secondfactor/u2f/register_request/get.ts create mode 100644 src/server/lib/routes/secondfactor/u2f/sign/post.ts create mode 100644 src/server/lib/routes/secondfactor/u2f/sign_request/SignMessage.ts create mode 100644 src/server/lib/routes/secondfactor/u2f/sign_request/get.ts create mode 100644 src/server/lib/routes/verify/get.ts rename src/{ => server}/resources/email-template.ejs (100%) create mode 100644 src/server/views/already-logged-in.pug create mode 100644 src/server/views/errors/401.pug create mode 100644 src/server/views/errors/403.pug create mode 100644 src/server/views/errors/404.pug create mode 100644 src/server/views/firstfactor.pug create mode 100644 src/server/views/layout/layout.pug create mode 100644 src/server/views/need-identity-validation.pug create mode 100644 src/server/views/password-reset-form.pug create mode 100644 src/server/views/password-reset-request.pug create mode 100644 src/server/views/secondfactor.pug create mode 100644 src/server/views/totp-register.pug create mode 100644 src/server/views/u2f-register.pug rename src/{lib => types}/Configuration.ts (100%) delete mode 100644 src/types/ILogger.ts delete mode 100644 src/types/authdog.d.ts create mode 100644 src/types/jquery-notify.d.ts create mode 100644 src/types/u2f-api.d.ts create mode 100644 src/types/u2f.d.ts delete mode 100644 src/views/head.ejs delete mode 100644 src/views/login.ejs delete mode 100644 src/views/reset-password-form.ejs delete mode 100644 src/views/reset-password.ejs delete mode 100644 src/views/scripts.ejs delete mode 100644 src/views/totp-register.ejs delete mode 100644 src/views/u2f-register.ejs create mode 100644 test/client/firstfactor/FirstFactorValidator.test.ts create mode 100644 test/client/firstfactor/login.test.ts create mode 100644 test/client/mocks/jquery.ts create mode 100644 test/client/mocks/u2f-api.ts create mode 100644 test/client/secondfactor/TOTPValidator.test.ts create mode 100644 test/client/secondfactor/U2FValidator.test.ts create mode 100644 test/client/totp-register/totp-register.test.ts delete mode 100644 test/integration/test_server.js create mode 100644 test/integration/test_server.ts rename test/{unitary => server}/AuthenticationRegulator.test.ts (89%) create mode 100644 test/server/IdentityCheckMiddleware.test.ts rename test/{unitary => server}/LdapClient.test.ts (93%) rename test/{unitary => server}/Server.test.ts (70%) rename test/{unitary => server}/TOTPValidator.test.ts (91%) rename test/{unitary => server}/UserDataStore.test.ts (88%) rename test/{unitary => server}/access_control/AccessController.test.ts (92%) rename test/{unitary => server}/access_control/PatternBuilder.test.ts (96%) rename test/{unitary => server}/config_adapter.test.ts (95%) rename test/{unitary => server}/data_persistence.test.ts (78%) rename test/{unitary => server}/mocks/AccessController.ts (100%) rename test/{unitary => server}/mocks/AuthenticationRegulator.ts (100%) rename test/{unitary => server}/mocks/IdentityValidator.ts (58%) rename test/{unitary => server}/mocks/LdapClient.ts (100%) rename test/{unitary => server}/mocks/Notifier.ts (100%) create mode 100644 test/server/mocks/ServerVariablesMock.ts rename test/{unitary => server}/mocks/TOTPValidator.ts (100%) rename test/{unitary => server}/mocks/UserDataStore.ts (100%) rename test/{unitary => server}/mocks/express.ts (98%) rename test/{unitary => server}/mocks/ldapjs.ts (100%) rename test/{unitary => server}/mocks/nodemailer.ts (100%) rename test/{unitary => server}/mocks/speakeasy.ts (100%) create mode 100644 test/server/mocks/u2f.ts rename test/{unitary => server}/notifiers/FileSystemNotifier.test.ts (84%) rename test/{unitary => server}/notifiers/GMailNotifier.test.ts (87%) rename test/{unitary => server}/notifiers/NotifierFactory.test.ts (77%) rename test/{unitary => server}/requests.ts (70%) rename test/{unitary/routes/FirstFactor.test.ts => server/routes/firstfactor/post.test.ts} (57%) create mode 100644 test/server/routes/password-reset/identity/PasswordResetHandler.test.ts create mode 100644 test/server/routes/password-reset/post.test.ts create mode 100644 test/server/routes/secondfactor/totp/register/RegistrationHandler.test.ts create mode 100644 test/server/routes/secondfactor/totp/sign/post.test.ts create mode 100644 test/server/routes/secondfactor/u2f/identity/RegistrationHandler.test.ts create mode 100644 test/server/routes/secondfactor/u2f/register/post.test.ts create mode 100644 test/server/routes/secondfactor/u2f/register_request/get.test.ts create mode 100644 test/server/routes/secondfactor/u2f/sign/post.test.ts create mode 100644 test/server/routes/secondfactor/u2f/sign_request/get.test.ts rename test/{unitary/routes/AuthenticationValidator.test.ts => server/routes/verify/get.test.ts} (59%) rename test/{unitary => server}/server_config.test.ts (91%) rename test/{unitary => server}/user_data_store/authentication_audit.test.ts (97%) rename test/{unitary => server}/user_data_store/totp_secret.test.ts (96%) delete mode 100644 test/unitary/IdentityValidator.test.ts delete mode 100644 test/unitary/mocks/authdog.ts delete mode 100644 test/unitary/routes/DenyNotLogged.test.ts delete mode 100644 test/unitary/routes/PasswordReset.test.ts delete mode 100644 test/unitary/routes/TOTPAuthenticator.test.ts delete mode 100644 test/unitary/routes/TOTPRegistration.test.ts delete mode 100644 test/unitary/routes/U2FRegistration.test.ts delete mode 100644 test/unitary/routes/U2FRoutes.test.ts diff --git a/.gitignore b/.gitignore index ca068bf0f..a8d1d2b98 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ # NodeJs modules node_modules/ +# npm debug logs +npm-debug.log* + # Coverage reports coverage/ @@ -24,3 +27,5 @@ notifications/ # Generated by TypeScript compiler dist/ + +.nyc_output/ diff --git a/.travis.yml b/.travis.yml index 7503d1ed4..c26d76f1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ addons: before_install: npm install -g npm@'>=2.13.5' script: - grunt test -- grunt build +- grunt dist - grunt docker-build - docker-compose build - docker-compose up -d diff --git a/Dockerfile b/Dockerfile index eef8b58f0..aec7ddcfd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ WORKDIR /usr/src COPY package.json /usr/src/package.json RUN npm install --production -COPY dist/src /usr/src +COPY dist/src/server /usr/src ENV PORT=80 EXPOSE 80 diff --git a/Gruntfile.js b/Gruntfile.js index 4b2484052..a4d08ccc7 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,10 +1,12 @@ -module.exports = function(grunt) { +module.exports = function (grunt) { + const buildDir = "dist"; + grunt.initConfig({ run: { options: {}, - "build-ts": { + "build": { cmd: "npm", - args: ['run', 'build-ts'] + args: ['run', 'build'] }, "tslint": { cmd: "npm", @@ -17,39 +19,136 @@ module.exports = function(grunt) { "docker-build": { cmd: "docker", args: ['build', '-t', 'clems4ever/authelia', '.'] + }, + "docker-restart": { + cmd: "docker-compose", + args: ['-f', 'docker-compose.yml', '-f', 'docker-compose.dev.yml', 'restart', 'auth'] + }, + "minify": { + cmd: "./node_modules/.bin/uglifyjs", + args: [`${buildDir}/src/server/public_html/js/authelia.js`, '-o', `${buildDir}/src/server/public_html/js/authelia.min.js`] + }, + "apidoc": { + cmd: "./node_modules/.bin/apidoc", + args: ["-i", "src/server", "-o", "doc"] } }, copy: { resources: { expand: true, - cwd: 'src/resources/', + cwd: 'src/server/resources/', src: '**', - dest: 'dist/src/resources/' + dest: `${buildDir}/src/server/resources/` }, views: { expand: true, - cwd: 'src/views/', + cwd: 'src/server/views/', src: '**', - dest: 'dist/src/views/' + dest: `${buildDir}/src/server/views/` }, - public_html: { + images: { expand: true, - cwd: 'src/public_html/', + cwd: 'src/client/img', src: '**', - dest: 'dist/src/public_html/' + dest: `${buildDir}/src/server/public_html/img/` + }, + thirdparties: { + expand: true, + cwd: 'src/client/thirdparties', + src: '**', + dest: `${buildDir}/src/server/public_html/js/` + }, + }, + browserify: { + dist: { + src: ['dist/src/client/index.js'], + dest: `${buildDir}/src/server/public_html/js/authelia.js`, + options: { + browserifyOptions: { + standalone: 'authelia' + }, + }, + }, + }, + watch: { + views: { + files: ['src/server/views/**/*.pug'], + tasks: ['copy:views'], + options: { + interrupt: false, + atBegin: true + } + }, + resources: { + files: ['src/server/resources/*.ejs'], + tasks: ['copy:resources'], + options: { + interrupt: false, + atBegin: true + } + }, + images: { + files: ['src/client/img/**'], + tasks: ['copy:images'], + options: { + interrupt: false, + atBegin: true + } + }, + css: { + files: ['src/client/**/*.css'], + tasks: ['concat:css', 'cssmin'], + options: { + interrupt: true, + atBegin: true + } + }, + client: { + files: ['src/client/**/*.ts', 'test/client/**/*.ts'], + tasks: ['build'], + options: { + interrupt: true, + atBegin: true + } + }, + server: { + files: ['src/server/**/*.ts', 'test/server/**/*.ts'], + tasks: ['build', 'run:docker-restart'], + options: { + interrupt: true, + } + } + }, + concat: { + css: { + src: ['src/client/css/*.css'], + dest: `${buildDir}/src/server/public_html/css/authelia.css` + }, + }, + cssmin: { + target: { + files: { + [`${buildDir}/src/server/public_html/css/authelia.min.css`]: [`${buildDir}/src/server/public_html/css/authelia.css`] + } } } }); + grunt.loadNpmTasks('grunt-browserify'); + grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-contrib-copy'); + grunt.loadNpmTasks('grunt-contrib-cssmin'); + grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-run'); - grunt.loadNpmTasks('grunt-contrib-copy'); grunt.registerTask('default', ['build']); - - grunt.registerTask('res', ['copy:resources', 'copy:views', 'copy:public_html']); - grunt.registerTask('build', ['run:tslint', 'run:build-ts', 'res']); + grunt.registerTask('build-resources', ['copy:resources', 'copy:views', 'copy:images', 'copy:thirdparties', 'concat:css', 'cssmin']); + grunt.registerTask('build', ['run:tslint', 'run:build', 'browserify:dist']); + grunt.registerTask('dist', ['build', 'build-resources', 'run:minify', 'cssmin']); + grunt.registerTask('docker-build', ['run:docker-build']); + grunt.registerTask('docker-restart', ['run:docker-restart']); grunt.registerTask('test', ['run:test']); }; diff --git a/README.md b/README.md index 57311feb9..ab5aaeead 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,8 @@ email address. For the sake of the example, the email is delivered in the file ./notifications/notification.txt. Paste the link in your browser and you should be able to reset the password. +![reset-password](https://raw.githubusercontent.com/clems4ever/authelia/master/images/reset_password.png) + ### Access Control With **Authelia**, you can define your own access control rules for restricting the access to certain subdomains to your users. Those rules are defined in the diff --git a/config.template.yml b/config.template.yml index 4a112c929..2b234c111 100644 --- a/config.template.yml +++ b/config.template.yml @@ -76,7 +76,7 @@ session: # The directory where the DB files will be saved -store_directory: /var/lib/auth-server/store +store_directory: /var/lib/authelia/store # Notifications are sent to users when they require a password reset, a u2f diff --git a/doc/api_data.js b/doc/api_data.js index 3591eab6f..2eacc2aaf 100644 --- a/doc/api_data.js +++ b/doc/api_data.js @@ -1,67 +1,9 @@ define({ "api": [ - { - "type": "post", - "url": "/authentication/2ndfactor/u2f/sign", - "title": "U2F Complete authentication", - "name": "CompleteU2FAuthentication", - "group": "Authentication", - "version": "1.0.0", - "success": { - "fields": { - "Success 204": [ - { - "group": "Success 204", - "optional": false, - "field": "status", - "description": "

The U2F authentication succeeded.

" - } - ] - } - }, - "error": { - "fields": { - "Error 403": [ - { - "group": "Error 403", - "type": "none", - "optional": false, - "field": "error", - "description": "

No authentication request has been provided.

" - } - ], - "Error 500": [ - { - "group": "Error 500", - "type": "String", - "optional": false, - "field": "error", - "description": "

Internal error message.

" - } - ] - } - }, - "description": "

Complete authentication request of the U2F device.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Authentication", - "header": { - "fields": { - "Header": [ - { - "group": "Header", - "type": "String", - "optional": false, - "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - } - }, { "type": "get", - "url": "/authentication/2ndfactor/u2f/sign_request", - "title": "U2F Start authentication", - "name": "StartU2FAuthentication", + "url": "/", + "title": "First factor page", + "name": "Login", "group": "Authentication", "version": "1.0.0", "success": { @@ -69,56 +11,82 @@ define({ "api": [ "Success 200": [ { "group": "Success 200", + "type": "String", "optional": false, - "field": "authentication_request", - "description": "

The U2F authentication request.

" + "field": "Content", + "description": "

The content of the first factor page.

" } ] } }, - "error": { + "description": "

Serves the login page and create a create a cookie for the client.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "Authentication" + }, + { + "type": "get", + "url": "/logout", + "title": "Serves logout page", + "name": "Logout", + "group": "Authentication", + "version": "1.0.0", + "parameter": { "fields": { - "Error 401": [ + "Parameter": [ { - "group": "Error 401", - "type": "none", - "optional": false, - "field": "error", - "description": "

There is no key registered for user in session.

" - } - ], - "Error 500": [ - { - "group": "Error 500", + "group": "Parameter", "type": "String", "optional": false, - "field": "error", - "description": "

Internal error message.

" + "field": "redirect", + "description": "

Redirect to this URL when user is deauthenticated.

" } ] } }, - "description": "

Initiate an authentication request using a U2F device.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Authentication", - "header": { + "success": { "fields": { - "Header": [ + "Success 302": [ { - "group": "Header", - "type": "String", + "group": "Success 302", "optional": false, - "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" + "field": "redirect", + "description": "

Redirect to the URL.

" } ] } - } + }, + "description": "

Log out the user and redirect to the URL.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "Authentication" + }, + { + "type": "get", + "url": "/secondfactor", + "title": "Second factor page", + "name": "SecondFactor", + "group": "Authentication", + "version": "1.0.0", + "success": { + "fields": { + "Success 200": [ + { + "group": "Success 200", + "type": "String", + "optional": false, + "field": "Content", + "description": "

The content of second factor page.

" + } + ] + } + }, + "description": "

Serves the second factor page

", + "filename": "src/server/endpoints.ts", + "groupTitle": "Authentication" }, { "type": "post", - "url": "/authentication/1stfactor", - "title": "LDAP authentication", + "url": "/1stfactor", + "title": "Bind user against LDAP", "name": "ValidateFirstFactor", "group": "Authentication", "version": "1.0.0", @@ -165,15 +133,6 @@ define({ "api": [ "description": "

1st factor is not validated.

" } ], - "Error 403": [ - { - "group": "Error 403", - "type": "none", - "optional": false, - "field": "error", - "description": "

Access has been restricted after too many authentication attempts

" - } - ], "Error 500": [ { "group": "Error 500", @@ -186,7 +145,7 @@ define({ "api": [ } }, "description": "

Verify credentials against the LDAP.

", - "filename": "src/lib/setup_endpoints.js", + "filename": "src/server/endpoints.ts", "groupTitle": "Authentication", "header": { "fields": { @@ -196,7 +155,7 @@ define({ "api": [ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } @@ -204,10 +163,343 @@ define({ "api": [ }, { "type": "post", - "url": "/authentication/2ndfactor/totp", - "title": "TOTP authentication", + "url": "/reset-password/request", + "title": "Finish password reset request", + "name": "FinishPasswordResetRequest", + "group": "PasswordReset", + "version": "1.0.0", + "description": "

Start password reset request.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "PasswordReset", + "header": { + "fields": { + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Cookie", + "description": "

Cookie containing "connect.sid", the user session token.

" + } + ] + } + }, + "parameter": { + "fields": { + "Parameter": [ + { + "group": "Parameter", + "type": "String", + "optional": false, + "field": "identity_token", + "description": "

The one-time identity validation token provided in the email.

" + } + ] + } + }, + "success": { + "fields": { + "Success 200": [ + { + "group": "Success 200", + "type": "String", + "optional": false, + "field": "content", + "description": "

The content of the page.

" + } + ] + } + }, + "error": { + "fields": { + "Error 403": [ + { + "group": "Error 403", + "optional": false, + "field": "AccessDenied", + "description": "

Access is denied.

" + } + ], + "Error 500": [ + { + "group": "Error 500", + "type": "String", + "optional": false, + "field": "error", + "description": "

Internal error message.

" + } + ] + } + } + }, + { + "type": "get", + "url": "/password-reset/request", + "title": "Request username", + "name": "ServePasswordResetPage", + "group": "PasswordReset", + "version": "1.0.0", + "description": "

Serve a page that requires the username.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "PasswordReset", + "header": { + "fields": { + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Cookie", + "description": "

Cookie containing "connect.sid", the user session token.

" + } + ] + } + } + }, + { + "type": "post", + "url": "/api/password-reset", + "title": "Set new password", + "name": "SetNewLDAPPassword", + "group": "PasswordReset", + "version": "1.0.0", + "parameter": { + "fields": { + "Parameter": [ + { + "group": "Parameter", + "type": "String", + "optional": false, + "field": "password", + "description": "

New password

" + } + ] + } + }, + "description": "

Set a new password for the user.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "PasswordReset", + "header": { + "fields": { + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Cookie", + "description": "

Cookie containing "connect.sid", the user session token.

" + } + ] + } + } + }, + { + "type": "get", + "url": "/password-reset/identity/start", + "title": "Start password reset request", + "name": "StartPasswordResetRequest", + "group": "PasswordReset", + "version": "1.0.0", + "description": "

Start password reset request.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "PasswordReset", + "header": { + "fields": { + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Cookie", + "description": "

Cookie containing "connect.sid", the user session token.

" + } + ] + } + }, + "success": { + "fields": { + "Success 204": [ + { + "group": "Success 204", + "optional": false, + "field": "status", + "description": "

Identity validation has been initiated.

" + } + ] + } + }, + "error": { + "fields": { + "Error 403": [ + { + "group": "Error 403", + "optional": false, + "field": "AccessDenied", + "description": "

Access is denied.

" + } + ], + "Error 400": [ + { + "group": "Error 400", + "optional": false, + "field": "InvalidIdentity", + "description": "

User identity is invalid.

" + } + ], + "Error 500": [ + { + "group": "Error 500", + "type": "String", + "optional": false, + "field": "error", + "description": "

Internal error message.

" + } + ] + } + } + }, + { + "type": "get", + "url": "/secondfactor/totp/identity/finish", + "title": "Finish TOTP registration identity validation", + "name": "FinishTOTPRegistration", + "group": "TOTP", + "version": "1.0.0", + "description": "

Serves the TOTP registration page that displays the secret. The secret is a QRCode and a base32 secret.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "TOTP", + "header": { + "fields": { + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Cookie", + "description": "

Cookie containing "connect.sid", the user session token.

" + } + ] + } + }, + "parameter": { + "fields": { + "Parameter": [ + { + "group": "Parameter", + "type": "String", + "optional": false, + "field": "identity_token", + "description": "

The one-time identity validation token provided in the email.

" + } + ] + } + }, + "success": { + "fields": { + "Success 200": [ + { + "group": "Success 200", + "type": "String", + "optional": false, + "field": "content", + "description": "

The content of the page.

" + } + ] + } + }, + "error": { + "fields": { + "Error 403": [ + { + "group": "Error 403", + "optional": false, + "field": "AccessDenied", + "description": "

Access is denied.

" + } + ], + "Error 500": [ + { + "group": "Error 500", + "type": "String", + "optional": false, + "field": "error", + "description": "

Internal error message.

" + } + ] + } + } + }, + { + "type": "get", + "url": "/secondfactor/totp/identity/start", + "title": "Start TOTP registration identity validation", + "name": "StartTOTPRegistration", + "group": "TOTP", + "version": "1.0.0", + "description": "

Initiates the identity validation

", + "filename": "src/server/endpoints.ts", + "groupTitle": "TOTP", + "header": { + "fields": { + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Cookie", + "description": "

Cookie containing "connect.sid", the user session token.

" + } + ] + } + }, + "success": { + "fields": { + "Success 204": [ + { + "group": "Success 204", + "optional": false, + "field": "status", + "description": "

Identity validation has been initiated.

" + } + ] + } + }, + "error": { + "fields": { + "Error 403": [ + { + "group": "Error 403", + "optional": false, + "field": "AccessDenied", + "description": "

Access is denied.

" + } + ], + "Error 400": [ + { + "group": "Error 400", + "optional": false, + "field": "InvalidIdentity", + "description": "

User identity is invalid.

" + } + ], + "Error 500": [ + { + "group": "Error 500", + "type": "String", + "optional": false, + "field": "error", + "description": "

Internal error message.

" + } + ] + } + } + }, + { + "type": "post", + "url": "/api/totp", + "title": "Complete TOTP authentication", "name": "ValidateTOTPSecondFactor", - "group": "Authentication", + "group": "TOTP", "version": "1.0.0", "parameter": { "fields": { @@ -224,12 +516,12 @@ define({ "api": [ }, "success": { "fields": { - "Success 204": [ + "Success 302": [ { - "group": "Success 204", + "group": "Success 302", "optional": false, - "field": "status", - "description": "

TOTP token is valid.

" + "field": "Redirect", + "description": "

to the URL that has been stored during last call to /verify.

" } ] } @@ -257,8 +549,8 @@ define({ "api": [ } }, "description": "

Verify TOTP token. The user is authenticated upon success.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Authentication", + "filename": "src/server/endpoints.ts", + "groupTitle": "TOTP", "header": { "fields": { "Header": [ @@ -267,222 +559,7 @@ define({ "api": [ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - } - }, - { - "type": "get", - "url": "/authentication/login", - "title": "Serve login page", - "name": "Login", - "group": "Pages", - "version": "1.0.0", - "parameter": { - "fields": { - "Parameter": [ - { - "group": "Parameter", - "type": "String", - "optional": false, - "field": "redirect", - "description": "

Redirect to this URL when user is authenticated.

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "Content", - "description": "

The content of the login page.

" - } - ] - } - }, - "description": "

Create a user session and serve the login page along with a cookie.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Pages" - }, - { - "type": "get", - "url": "/authentication/logout", - "title": "Server logout page", - "name": "Logout", - "group": "Pages", - "version": "1.0.0", - "parameter": { - "fields": { - "Parameter": [ - { - "group": "Parameter", - "type": "String", - "optional": false, - "field": "redirect", - "description": "

Redirect to this URL when user is deauthenticated.

" - } - ] - } - }, - "success": { - "fields": { - "Success 301": [ - { - "group": "Success 301", - "optional": false, - "field": "redirect", - "description": "

Redirect to the URL.

" - } - ] - } - }, - "description": "

Deauthenticate the user and redirect him.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Pages" - }, - { - "type": "get", - "url": "/authentication/reset-password", - "title": "Serve password reset form.", - "name": "ServePasswordResetForm", - "group": "Pages", - "version": "1.0.0", - "description": "

Serves password reset form that allow the user to provide the new password.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Pages", - "header": { - "fields": { - "Header": [ - { - "group": "Header", - "type": "String", - "optional": false, - "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - }, - "parameter": { - "fields": { - "Parameter": [ - { - "group": "Parameter", - "type": "String", - "optional": false, - "field": "identity_token", - "description": "

The one-time identity validation token provided in the email.

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "content", - "description": "

The content of the page.

" - } - ] - } - }, - "error": { - "fields": { - "Error 403": [ - { - "group": "Error 403", - "optional": false, - "field": "AccessDenied", - "description": "

Access is denied.

" - } - ], - "Error 500": [ - { - "group": "Error 500", - "type": "String", - "optional": false, - "field": "error", - "description": "

Internal error message.

" - } - ] - } - } - }, - { - "type": "get", - "url": "/authentication/u2f-register", - "title": "Serve U2F registration page", - "name": "ServeU2FRegistrationPage", - "group": "Pages", - "version": "1.0.0", - "description": "

Serves the U2F registration page that asks the user to touch the token of the U2F device.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Pages", - "header": { - "fields": { - "Header": [ - { - "group": "Header", - "type": "String", - "optional": false, - "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - }, - "parameter": { - "fields": { - "Parameter": [ - { - "group": "Parameter", - "type": "String", - "optional": false, - "field": "identity_token", - "description": "

The one-time identity validation token provided in the email.

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "content", - "description": "

The content of the page.

" - } - ] - } - }, - "error": { - "fields": { - "Error 403": [ - { - "group": "Error 403", - "optional": false, - "field": "AccessDenied", - "description": "

Access is denied.

" - } - ], - "Error 500": [ - { - "group": "Error 500", - "type": "String", - "optional": false, - "field": "error", - "description": "

Internal error message.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } @@ -490,19 +567,19 @@ define({ "api": [ }, { "type": "post", - "url": "/authentication/2ndfactor/u2f/register", - "title": "U2F Complete device registration", - "name": "CompleteU2FRegistration", - "group": "Registration", + "url": "/api/u2f/sign", + "title": "Complete U2F authentication", + "name": "CompleteU2FAuthentication", + "group": "U2F", "version": "1.0.0", "success": { "fields": { - "Success 204": [ + "Success 302": [ { - "group": "Success 204", + "group": "Success 302", "optional": false, - "field": "status", - "description": "

The U2F registration succeeded.

" + "field": "Redirect", + "description": "

to the URL that has been stored during last call to /verify.

" } ] } @@ -515,7 +592,7 @@ define({ "api": [ "type": "none", "optional": false, "field": "error", - "description": "

Unexpected identity validation challenge.

" + "description": "

No authentication request has been provided.

" } ], "Error 500": [ @@ -529,9 +606,45 @@ define({ "api": [ ] } }, + "description": "

Complete authentication request of the U2F device.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "U2F", + "header": { + "fields": { + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Cookie", + "description": "

Cookie containing "connect.sid", the user session token.

" + } + ] + } + } + }, + { + "type": "post", + "url": "/api/secondfactor/u2f/register", + "title": "Complete U2F registration", + "name": "FinishU2FRegistration", + "group": "U2F", + "version": "1.0.0", + "success": { + "fields": { + "Success 302": [ + { + "group": "Success 302", + "optional": false, + "field": "Redirect", + "description": "

to the URL that has been stored during last call to /verify.

" + } + ] + } + }, "description": "

Complete U2F registration request.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", + "filename": "src/server/endpoints.ts", + "groupTitle": "U2F", "header": { "fields": { "Header": [ @@ -540,137 +653,13 @@ define({ "api": [ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - } - }, - { - "type": "post", - "url": "/authentication/new-totp-secret", - "title": "Generate TOTP secret", - "name": "GenerateTOTPSecret", - "group": "Registration", - "version": "1.0.0", - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "base32", - "description": "

The base32 representation of the secret.

" - }, - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "ascii", - "description": "

The ASCII representation of the secret.

" - }, - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "qrcode", - "description": "

The QRCode of the secret in URI format.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } }, "error": { "fields": { - "Error 403": [ - { - "group": "Error 403", - "type": "String", - "optional": false, - "field": "error", - "description": "

No user provided in the session or unexpected identity validation challenge in the session.

" - } - ], - "Error 500": [ - { - "group": "Error 500", - "type": "String", - "optional": false, - "field": "error", - "description": "

Internal error message

" - } - ] - } - }, - "description": "

Generate a new TOTP secret and returns it.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", - "header": { - "fields": { - "Header": [ - { - "group": "Header", - "type": "String", - "optional": false, - "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - } - }, - { - "type": "post", - "url": "/authentication/reset-password", - "title": "Request for password reset", - "name": "RequestPasswordReset", - "group": "Registration", - "version": "1.0.0", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", - "header": { - "fields": { - "Header": [ - { - "group": "Header", - "type": "String", - "optional": false, - "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - }, - "success": { - "fields": { - "Success 204": [ - { - "group": "Success 204", - "optional": false, - "field": "status", - "description": "

Identity validation has been initiated.

" - } - ] - } - }, - "error": { - "fields": { - "Error 403": [ - { - "group": "Error 403", - "optional": false, - "field": "AccessDenied", - "description": "

Access is denied.

" - } - ], - "Error 400": [ - { - "group": "Error 400", - "optional": false, - "field": "InvalidIdentity", - "description": "

User identity is invalid.

" - } - ], "Error 500": [ { "group": "Error 500", @@ -681,83 +670,17 @@ define({ "api": [ } ] } - }, - "description": "

This request issue an identity validation token for the user bound to the session. It sends a challenge to the email address set in the user LDAP entry. The user must visit the sent URL to complete the validation and continue the registration process.

" + } }, { - "type": "post", - "url": "/authentication/totp-register", - "title": "Request TOTP registration", - "name": "RequestTOTPRegistration", - "group": "Registration", - "version": "1.0.0", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", - "header": { - "fields": { - "Header": [ - { - "group": "Header", - "type": "String", - "optional": false, - "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - }, - "success": { - "fields": { - "Success 204": [ - { - "group": "Success 204", - "optional": false, - "field": "status", - "description": "

Identity validation has been initiated.

" - } - ] - } - }, - "error": { - "fields": { - "Error 403": [ - { - "group": "Error 403", - "optional": false, - "field": "AccessDenied", - "description": "

Access is denied.

" - } - ], - "Error 400": [ - { - "group": "Error 400", - "optional": false, - "field": "InvalidIdentity", - "description": "

User identity is invalid.

" - } - ], - "Error 500": [ - { - "group": "Error 500", - "type": "String", - "optional": false, - "field": "error", - "description": "

Internal error message.

" - } - ] - } - }, - "description": "

This request issue an identity validation token for the user bound to the session. It sends a challenge to the email address set in the user LDAP entry. The user must visit the sent URL to complete the validation and continue the registration process.

" - }, - { - "type": "post", - "url": "/authentication/u2f-register", - "title": "Request U2F registration", + "type": "get", + "url": "/secondfactor/u2f/identity/start", + "title": "Start U2F registration identity validation", "name": "RequestU2FRegistration", - "group": "Registration", + "group": "U2F", "version": "1.0.0", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", + "filename": "src/server/endpoints.ts", + "groupTitle": "U2F", "header": { "fields": { "Header": [ @@ -766,7 +689,7 @@ define({ "api": [ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } @@ -816,14 +739,14 @@ define({ "api": [ }, { "type": "get", - "url": "/authentication/totp-register", - "title": "Serve TOTP registration page", - "name": "ServeTOTPRegistrationPage", - "group": "Registration", + "url": "/secondfactor/u2f/identity/finish", + "title": "Finish U2F registration identity validation", + "name": "ServeU2FRegistrationPage", + "group": "U2F", "version": "1.0.0", - "description": "

Serves the TOTP registration page that displays the secret. The secret is a QRCode and a base32 secret.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", + "description": "

Serves the U2F registration page that asks the user to touch the token of the U2F device.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "U2F", "header": { "fields": { "Header": [ @@ -832,7 +755,7 @@ define({ "api": [ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } @@ -886,28 +809,49 @@ define({ "api": [ } }, { - "type": "post", - "url": "/authentication/new-password", - "title": "Set LDAP password", - "name": "SetLDAPPassword", - "group": "Registration", + "type": "get", + "url": "/api/u2f/sign_request", + "title": "Start U2F authentication", + "name": "StartU2FAuthentication", + "group": "U2F", "version": "1.0.0", - "parameter": { + "success": { "fields": { - "Parameter": [ + "Success 200": [ { - "group": "Parameter", - "type": "String", + "group": "Success 200", "optional": false, - "field": "password", - "description": "

New password

" + "field": "authentication_request", + "description": "

The U2F authentication request.

" } ] } }, - "description": "

Set a new password for the user.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", + "error": { + "fields": { + "Error 401": [ + { + "group": "Error 401", + "type": "none", + "optional": false, + "field": "error", + "description": "

There is no key registered for user in session.

" + } + ], + "Error 500": [ + { + "group": "Error 500", + "type": "String", + "optional": false, + "field": "error", + "description": "

Internal error message.

" + } + ] + } + }, + "description": "

Initiate an authentication request using a U2F device.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "U2F", "header": { "fields": { "Header": [ @@ -916,7 +860,7 @@ define({ "api": [ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } @@ -924,10 +868,10 @@ define({ "api": [ }, { "type": "get", - "url": "/authentication/2ndfactor/u2f/register_request", - "title": "U2F Start device registration", + "url": "/api/u2f/register_request", + "title": "Start U2F registration", "name": "StartU2FRegistration", - "group": "Registration", + "group": "U2F", "version": "1.0.0", "success": { "fields": { @@ -964,8 +908,8 @@ define({ "api": [ } }, "description": "

Initiate a U2F device registration request.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", + "filename": "src/server/endpoints.ts", + "groupTitle": "U2F", "header": { "fields": { "Header": [ @@ -974,7 +918,7 @@ define({ "api": [ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } @@ -982,7 +926,7 @@ define({ "api": [ }, { "type": "get", - "url": "/authentication/verify", + "url": "/verify", "title": "Verify user authentication", "name": "VerifyAuthentication", "group": "Verification", @@ -1012,7 +956,7 @@ define({ "api": [ } }, "description": "

Verify that the user is authenticated, i.e., the two factors have been validated

", - "filename": "src/lib/setup_endpoints.js", + "filename": "src/server/endpoints.ts", "groupTitle": "Verification", "header": { "fields": { @@ -1022,7 +966,7 @@ define({ "api": [ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } diff --git a/doc/api_data.json b/doc/api_data.json index 60247d1f4..527b67508 100644 --- a/doc/api_data.json +++ b/doc/api_data.json @@ -1,67 +1,9 @@ [ - { - "type": "post", - "url": "/authentication/2ndfactor/u2f/sign", - "title": "U2F Complete authentication", - "name": "CompleteU2FAuthentication", - "group": "Authentication", - "version": "1.0.0", - "success": { - "fields": { - "Success 204": [ - { - "group": "Success 204", - "optional": false, - "field": "status", - "description": "

The U2F authentication succeeded.

" - } - ] - } - }, - "error": { - "fields": { - "Error 403": [ - { - "group": "Error 403", - "type": "none", - "optional": false, - "field": "error", - "description": "

No authentication request has been provided.

" - } - ], - "Error 500": [ - { - "group": "Error 500", - "type": "String", - "optional": false, - "field": "error", - "description": "

Internal error message.

" - } - ] - } - }, - "description": "

Complete authentication request of the U2F device.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Authentication", - "header": { - "fields": { - "Header": [ - { - "group": "Header", - "type": "String", - "optional": false, - "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - } - }, { "type": "get", - "url": "/authentication/2ndfactor/u2f/sign_request", - "title": "U2F Start authentication", - "name": "StartU2FAuthentication", + "url": "/", + "title": "First factor page", + "name": "Login", "group": "Authentication", "version": "1.0.0", "success": { @@ -69,56 +11,82 @@ "Success 200": [ { "group": "Success 200", + "type": "String", "optional": false, - "field": "authentication_request", - "description": "

The U2F authentication request.

" + "field": "Content", + "description": "

The content of the first factor page.

" } ] } }, - "error": { + "description": "

Serves the login page and create a create a cookie for the client.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "Authentication" + }, + { + "type": "get", + "url": "/logout", + "title": "Serves logout page", + "name": "Logout", + "group": "Authentication", + "version": "1.0.0", + "parameter": { "fields": { - "Error 401": [ + "Parameter": [ { - "group": "Error 401", - "type": "none", - "optional": false, - "field": "error", - "description": "

There is no key registered for user in session.

" - } - ], - "Error 500": [ - { - "group": "Error 500", + "group": "Parameter", "type": "String", "optional": false, - "field": "error", - "description": "

Internal error message.

" + "field": "redirect", + "description": "

Redirect to this URL when user is deauthenticated.

" } ] } }, - "description": "

Initiate an authentication request using a U2F device.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Authentication", - "header": { + "success": { "fields": { - "Header": [ + "Success 302": [ { - "group": "Header", - "type": "String", + "group": "Success 302", "optional": false, - "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" + "field": "redirect", + "description": "

Redirect to the URL.

" } ] } - } + }, + "description": "

Log out the user and redirect to the URL.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "Authentication" + }, + { + "type": "get", + "url": "/secondfactor", + "title": "Second factor page", + "name": "SecondFactor", + "group": "Authentication", + "version": "1.0.0", + "success": { + "fields": { + "Success 200": [ + { + "group": "Success 200", + "type": "String", + "optional": false, + "field": "Content", + "description": "

The content of second factor page.

" + } + ] + } + }, + "description": "

Serves the second factor page

", + "filename": "src/server/endpoints.ts", + "groupTitle": "Authentication" }, { "type": "post", - "url": "/authentication/1stfactor", - "title": "LDAP authentication", + "url": "/1stfactor", + "title": "Bind user against LDAP", "name": "ValidateFirstFactor", "group": "Authentication", "version": "1.0.0", @@ -165,15 +133,6 @@ "description": "

1st factor is not validated.

" } ], - "Error 403": [ - { - "group": "Error 403", - "type": "none", - "optional": false, - "field": "error", - "description": "

Access has been restricted after too many authentication attempts

" - } - ], "Error 500": [ { "group": "Error 500", @@ -186,7 +145,7 @@ } }, "description": "

Verify credentials against the LDAP.

", - "filename": "src/lib/setup_endpoints.js", + "filename": "src/server/endpoints.ts", "groupTitle": "Authentication", "header": { "fields": { @@ -196,7 +155,7 @@ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } @@ -204,10 +163,343 @@ }, { "type": "post", - "url": "/authentication/2ndfactor/totp", - "title": "TOTP authentication", + "url": "/reset-password/request", + "title": "Finish password reset request", + "name": "FinishPasswordResetRequest", + "group": "PasswordReset", + "version": "1.0.0", + "description": "

Start password reset request.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "PasswordReset", + "header": { + "fields": { + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Cookie", + "description": "

Cookie containing "connect.sid", the user session token.

" + } + ] + } + }, + "parameter": { + "fields": { + "Parameter": [ + { + "group": "Parameter", + "type": "String", + "optional": false, + "field": "identity_token", + "description": "

The one-time identity validation token provided in the email.

" + } + ] + } + }, + "success": { + "fields": { + "Success 200": [ + { + "group": "Success 200", + "type": "String", + "optional": false, + "field": "content", + "description": "

The content of the page.

" + } + ] + } + }, + "error": { + "fields": { + "Error 403": [ + { + "group": "Error 403", + "optional": false, + "field": "AccessDenied", + "description": "

Access is denied.

" + } + ], + "Error 500": [ + { + "group": "Error 500", + "type": "String", + "optional": false, + "field": "error", + "description": "

Internal error message.

" + } + ] + } + } + }, + { + "type": "get", + "url": "/password-reset/request", + "title": "Request username", + "name": "ServePasswordResetPage", + "group": "PasswordReset", + "version": "1.0.0", + "description": "

Serve a page that requires the username.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "PasswordReset", + "header": { + "fields": { + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Cookie", + "description": "

Cookie containing "connect.sid", the user session token.

" + } + ] + } + } + }, + { + "type": "post", + "url": "/api/password-reset", + "title": "Set new password", + "name": "SetNewLDAPPassword", + "group": "PasswordReset", + "version": "1.0.0", + "parameter": { + "fields": { + "Parameter": [ + { + "group": "Parameter", + "type": "String", + "optional": false, + "field": "password", + "description": "

New password

" + } + ] + } + }, + "description": "

Set a new password for the user.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "PasswordReset", + "header": { + "fields": { + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Cookie", + "description": "

Cookie containing "connect.sid", the user session token.

" + } + ] + } + } + }, + { + "type": "get", + "url": "/password-reset/identity/start", + "title": "Start password reset request", + "name": "StartPasswordResetRequest", + "group": "PasswordReset", + "version": "1.0.0", + "description": "

Start password reset request.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "PasswordReset", + "header": { + "fields": { + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Cookie", + "description": "

Cookie containing "connect.sid", the user session token.

" + } + ] + } + }, + "success": { + "fields": { + "Success 204": [ + { + "group": "Success 204", + "optional": false, + "field": "status", + "description": "

Identity validation has been initiated.

" + } + ] + } + }, + "error": { + "fields": { + "Error 403": [ + { + "group": "Error 403", + "optional": false, + "field": "AccessDenied", + "description": "

Access is denied.

" + } + ], + "Error 400": [ + { + "group": "Error 400", + "optional": false, + "field": "InvalidIdentity", + "description": "

User identity is invalid.

" + } + ], + "Error 500": [ + { + "group": "Error 500", + "type": "String", + "optional": false, + "field": "error", + "description": "

Internal error message.

" + } + ] + } + } + }, + { + "type": "get", + "url": "/secondfactor/totp/identity/finish", + "title": "Finish TOTP registration identity validation", + "name": "FinishTOTPRegistration", + "group": "TOTP", + "version": "1.0.0", + "description": "

Serves the TOTP registration page that displays the secret. The secret is a QRCode and a base32 secret.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "TOTP", + "header": { + "fields": { + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Cookie", + "description": "

Cookie containing "connect.sid", the user session token.

" + } + ] + } + }, + "parameter": { + "fields": { + "Parameter": [ + { + "group": "Parameter", + "type": "String", + "optional": false, + "field": "identity_token", + "description": "

The one-time identity validation token provided in the email.

" + } + ] + } + }, + "success": { + "fields": { + "Success 200": [ + { + "group": "Success 200", + "type": "String", + "optional": false, + "field": "content", + "description": "

The content of the page.

" + } + ] + } + }, + "error": { + "fields": { + "Error 403": [ + { + "group": "Error 403", + "optional": false, + "field": "AccessDenied", + "description": "

Access is denied.

" + } + ], + "Error 500": [ + { + "group": "Error 500", + "type": "String", + "optional": false, + "field": "error", + "description": "

Internal error message.

" + } + ] + } + } + }, + { + "type": "get", + "url": "/secondfactor/totp/identity/start", + "title": "Start TOTP registration identity validation", + "name": "StartTOTPRegistration", + "group": "TOTP", + "version": "1.0.0", + "description": "

Initiates the identity validation

", + "filename": "src/server/endpoints.ts", + "groupTitle": "TOTP", + "header": { + "fields": { + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Cookie", + "description": "

Cookie containing "connect.sid", the user session token.

" + } + ] + } + }, + "success": { + "fields": { + "Success 204": [ + { + "group": "Success 204", + "optional": false, + "field": "status", + "description": "

Identity validation has been initiated.

" + } + ] + } + }, + "error": { + "fields": { + "Error 403": [ + { + "group": "Error 403", + "optional": false, + "field": "AccessDenied", + "description": "

Access is denied.

" + } + ], + "Error 400": [ + { + "group": "Error 400", + "optional": false, + "field": "InvalidIdentity", + "description": "

User identity is invalid.

" + } + ], + "Error 500": [ + { + "group": "Error 500", + "type": "String", + "optional": false, + "field": "error", + "description": "

Internal error message.

" + } + ] + } + } + }, + { + "type": "post", + "url": "/api/totp", + "title": "Complete TOTP authentication", "name": "ValidateTOTPSecondFactor", - "group": "Authentication", + "group": "TOTP", "version": "1.0.0", "parameter": { "fields": { @@ -224,12 +516,12 @@ }, "success": { "fields": { - "Success 204": [ + "Success 302": [ { - "group": "Success 204", + "group": "Success 302", "optional": false, - "field": "status", - "description": "

TOTP token is valid.

" + "field": "Redirect", + "description": "

to the URL that has been stored during last call to /verify.

" } ] } @@ -257,8 +549,8 @@ } }, "description": "

Verify TOTP token. The user is authenticated upon success.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Authentication", + "filename": "src/server/endpoints.ts", + "groupTitle": "TOTP", "header": { "fields": { "Header": [ @@ -267,222 +559,7 @@ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - } - }, - { - "type": "get", - "url": "/authentication/login", - "title": "Serve login page", - "name": "Login", - "group": "Pages", - "version": "1.0.0", - "parameter": { - "fields": { - "Parameter": [ - { - "group": "Parameter", - "type": "String", - "optional": false, - "field": "redirect", - "description": "

Redirect to this URL when user is authenticated.

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "Content", - "description": "

The content of the login page.

" - } - ] - } - }, - "description": "

Create a user session and serve the login page along with a cookie.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Pages" - }, - { - "type": "get", - "url": "/authentication/logout", - "title": "Server logout page", - "name": "Logout", - "group": "Pages", - "version": "1.0.0", - "parameter": { - "fields": { - "Parameter": [ - { - "group": "Parameter", - "type": "String", - "optional": false, - "field": "redirect", - "description": "

Redirect to this URL when user is deauthenticated.

" - } - ] - } - }, - "success": { - "fields": { - "Success 301": [ - { - "group": "Success 301", - "optional": false, - "field": "redirect", - "description": "

Redirect to the URL.

" - } - ] - } - }, - "description": "

Deauthenticate the user and redirect him.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Pages" - }, - { - "type": "get", - "url": "/authentication/reset-password", - "title": "Serve password reset form.", - "name": "ServePasswordResetForm", - "group": "Pages", - "version": "1.0.0", - "description": "

Serves password reset form that allow the user to provide the new password.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Pages", - "header": { - "fields": { - "Header": [ - { - "group": "Header", - "type": "String", - "optional": false, - "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - }, - "parameter": { - "fields": { - "Parameter": [ - { - "group": "Parameter", - "type": "String", - "optional": false, - "field": "identity_token", - "description": "

The one-time identity validation token provided in the email.

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "content", - "description": "

The content of the page.

" - } - ] - } - }, - "error": { - "fields": { - "Error 403": [ - { - "group": "Error 403", - "optional": false, - "field": "AccessDenied", - "description": "

Access is denied.

" - } - ], - "Error 500": [ - { - "group": "Error 500", - "type": "String", - "optional": false, - "field": "error", - "description": "

Internal error message.

" - } - ] - } - } - }, - { - "type": "get", - "url": "/authentication/u2f-register", - "title": "Serve U2F registration page", - "name": "ServeU2FRegistrationPage", - "group": "Pages", - "version": "1.0.0", - "description": "

Serves the U2F registration page that asks the user to touch the token of the U2F device.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Pages", - "header": { - "fields": { - "Header": [ - { - "group": "Header", - "type": "String", - "optional": false, - "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - }, - "parameter": { - "fields": { - "Parameter": [ - { - "group": "Parameter", - "type": "String", - "optional": false, - "field": "identity_token", - "description": "

The one-time identity validation token provided in the email.

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "content", - "description": "

The content of the page.

" - } - ] - } - }, - "error": { - "fields": { - "Error 403": [ - { - "group": "Error 403", - "optional": false, - "field": "AccessDenied", - "description": "

Access is denied.

" - } - ], - "Error 500": [ - { - "group": "Error 500", - "type": "String", - "optional": false, - "field": "error", - "description": "

Internal error message.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } @@ -490,19 +567,19 @@ }, { "type": "post", - "url": "/authentication/2ndfactor/u2f/register", - "title": "U2F Complete device registration", - "name": "CompleteU2FRegistration", - "group": "Registration", + "url": "/api/u2f/sign", + "title": "Complete U2F authentication", + "name": "CompleteU2FAuthentication", + "group": "U2F", "version": "1.0.0", "success": { "fields": { - "Success 204": [ + "Success 302": [ { - "group": "Success 204", + "group": "Success 302", "optional": false, - "field": "status", - "description": "

The U2F registration succeeded.

" + "field": "Redirect", + "description": "

to the URL that has been stored during last call to /verify.

" } ] } @@ -515,7 +592,7 @@ "type": "none", "optional": false, "field": "error", - "description": "

Unexpected identity validation challenge.

" + "description": "

No authentication request has been provided.

" } ], "Error 500": [ @@ -529,9 +606,45 @@ ] } }, + "description": "

Complete authentication request of the U2F device.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "U2F", + "header": { + "fields": { + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Cookie", + "description": "

Cookie containing "connect.sid", the user session token.

" + } + ] + } + } + }, + { + "type": "post", + "url": "/api/secondfactor/u2f/register", + "title": "Complete U2F registration", + "name": "FinishU2FRegistration", + "group": "U2F", + "version": "1.0.0", + "success": { + "fields": { + "Success 302": [ + { + "group": "Success 302", + "optional": false, + "field": "Redirect", + "description": "

to the URL that has been stored during last call to /verify.

" + } + ] + } + }, "description": "

Complete U2F registration request.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", + "filename": "src/server/endpoints.ts", + "groupTitle": "U2F", "header": { "fields": { "Header": [ @@ -540,137 +653,13 @@ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - } - }, - { - "type": "post", - "url": "/authentication/new-totp-secret", - "title": "Generate TOTP secret", - "name": "GenerateTOTPSecret", - "group": "Registration", - "version": "1.0.0", - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "base32", - "description": "

The base32 representation of the secret.

" - }, - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "ascii", - "description": "

The ASCII representation of the secret.

" - }, - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "qrcode", - "description": "

The QRCode of the secret in URI format.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } }, "error": { "fields": { - "Error 403": [ - { - "group": "Error 403", - "type": "String", - "optional": false, - "field": "error", - "description": "

No user provided in the session or unexpected identity validation challenge in the session.

" - } - ], - "Error 500": [ - { - "group": "Error 500", - "type": "String", - "optional": false, - "field": "error", - "description": "

Internal error message

" - } - ] - } - }, - "description": "

Generate a new TOTP secret and returns it.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", - "header": { - "fields": { - "Header": [ - { - "group": "Header", - "type": "String", - "optional": false, - "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - } - }, - { - "type": "post", - "url": "/authentication/reset-password", - "title": "Request for password reset", - "name": "RequestPasswordReset", - "group": "Registration", - "version": "1.0.0", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", - "header": { - "fields": { - "Header": [ - { - "group": "Header", - "type": "String", - "optional": false, - "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - }, - "success": { - "fields": { - "Success 204": [ - { - "group": "Success 204", - "optional": false, - "field": "status", - "description": "

Identity validation has been initiated.

" - } - ] - } - }, - "error": { - "fields": { - "Error 403": [ - { - "group": "Error 403", - "optional": false, - "field": "AccessDenied", - "description": "

Access is denied.

" - } - ], - "Error 400": [ - { - "group": "Error 400", - "optional": false, - "field": "InvalidIdentity", - "description": "

User identity is invalid.

" - } - ], "Error 500": [ { "group": "Error 500", @@ -681,83 +670,17 @@ } ] } - }, - "description": "

This request issue an identity validation token for the user bound to the session. It sends a challenge to the email address set in the user LDAP entry. The user must visit the sent URL to complete the validation and continue the registration process.

" + } }, { - "type": "post", - "url": "/authentication/totp-register", - "title": "Request TOTP registration", - "name": "RequestTOTPRegistration", - "group": "Registration", - "version": "1.0.0", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", - "header": { - "fields": { - "Header": [ - { - "group": "Header", - "type": "String", - "optional": false, - "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" - } - ] - } - }, - "success": { - "fields": { - "Success 204": [ - { - "group": "Success 204", - "optional": false, - "field": "status", - "description": "

Identity validation has been initiated.

" - } - ] - } - }, - "error": { - "fields": { - "Error 403": [ - { - "group": "Error 403", - "optional": false, - "field": "AccessDenied", - "description": "

Access is denied.

" - } - ], - "Error 400": [ - { - "group": "Error 400", - "optional": false, - "field": "InvalidIdentity", - "description": "

User identity is invalid.

" - } - ], - "Error 500": [ - { - "group": "Error 500", - "type": "String", - "optional": false, - "field": "error", - "description": "

Internal error message.

" - } - ] - } - }, - "description": "

This request issue an identity validation token for the user bound to the session. It sends a challenge to the email address set in the user LDAP entry. The user must visit the sent URL to complete the validation and continue the registration process.

" - }, - { - "type": "post", - "url": "/authentication/u2f-register", - "title": "Request U2F registration", + "type": "get", + "url": "/secondfactor/u2f/identity/start", + "title": "Start U2F registration identity validation", "name": "RequestU2FRegistration", - "group": "Registration", + "group": "U2F", "version": "1.0.0", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", + "filename": "src/server/endpoints.ts", + "groupTitle": "U2F", "header": { "fields": { "Header": [ @@ -766,7 +689,7 @@ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } @@ -816,14 +739,14 @@ }, { "type": "get", - "url": "/authentication/totp-register", - "title": "Serve TOTP registration page", - "name": "ServeTOTPRegistrationPage", - "group": "Registration", + "url": "/secondfactor/u2f/identity/finish", + "title": "Finish U2F registration identity validation", + "name": "ServeU2FRegistrationPage", + "group": "U2F", "version": "1.0.0", - "description": "

Serves the TOTP registration page that displays the secret. The secret is a QRCode and a base32 secret.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", + "description": "

Serves the U2F registration page that asks the user to touch the token of the U2F device.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "U2F", "header": { "fields": { "Header": [ @@ -832,7 +755,7 @@ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } @@ -886,28 +809,49 @@ } }, { - "type": "post", - "url": "/authentication/new-password", - "title": "Set LDAP password", - "name": "SetLDAPPassword", - "group": "Registration", + "type": "get", + "url": "/api/u2f/sign_request", + "title": "Start U2F authentication", + "name": "StartU2FAuthentication", + "group": "U2F", "version": "1.0.0", - "parameter": { + "success": { "fields": { - "Parameter": [ + "Success 200": [ { - "group": "Parameter", - "type": "String", + "group": "Success 200", "optional": false, - "field": "password", - "description": "

New password

" + "field": "authentication_request", + "description": "

The U2F authentication request.

" } ] } }, - "description": "

Set a new password for the user.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", + "error": { + "fields": { + "Error 401": [ + { + "group": "Error 401", + "type": "none", + "optional": false, + "field": "error", + "description": "

There is no key registered for user in session.

" + } + ], + "Error 500": [ + { + "group": "Error 500", + "type": "String", + "optional": false, + "field": "error", + "description": "

Internal error message.

" + } + ] + } + }, + "description": "

Initiate an authentication request using a U2F device.

", + "filename": "src/server/endpoints.ts", + "groupTitle": "U2F", "header": { "fields": { "Header": [ @@ -916,7 +860,7 @@ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } @@ -924,10 +868,10 @@ }, { "type": "get", - "url": "/authentication/2ndfactor/u2f/register_request", - "title": "U2F Start device registration", + "url": "/api/u2f/register_request", + "title": "Start U2F registration", "name": "StartU2FRegistration", - "group": "Registration", + "group": "U2F", "version": "1.0.0", "success": { "fields": { @@ -964,8 +908,8 @@ } }, "description": "

Initiate a U2F device registration request.

", - "filename": "src/lib/setup_endpoints.js", - "groupTitle": "Registration", + "filename": "src/server/endpoints.ts", + "groupTitle": "U2F", "header": { "fields": { "Header": [ @@ -974,7 +918,7 @@ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } @@ -982,7 +926,7 @@ }, { "type": "get", - "url": "/authentication/verify", + "url": "/verify", "title": "Verify user authentication", "name": "VerifyAuthentication", "group": "Verification", @@ -1012,7 +956,7 @@ } }, "description": "

Verify that the user is authenticated, i.e., the two factors have been validated

", - "filename": "src/lib/setup_endpoints.js", + "filename": "src/server/endpoints.ts", "groupTitle": "Verification", "header": { "fields": { @@ -1022,7 +966,7 @@ "type": "String", "optional": false, "field": "Cookie", - "description": "

Cookie containing 'connect.sid', the user session token.

" + "description": "

Cookie containing "connect.sid", the user session token.

" } ] } diff --git a/doc/api_project.js b/doc/api_project.js index e51a09d65..9b4ecf09d 100644 --- a/doc/api_project.js +++ b/doc/api_project.js @@ -1,15 +1,15 @@ define({ "title": "Authelia API documentation", "name": "authelia", - "version": "1.0.11", - "description": "2-factor authentication server using LDAP as 1st factor and TOTP or U2F as 2nd factor", + "version": "2.1.3", + "description": "2FA Single Sign-On server for nginx using LDAP, TOTP and U2F", "sampleUrl": false, "defaultVersion": "0.0.0", "apidoc": "0.3.0", "generator": { "name": "apidoc", - "time": "2017-01-29T00:44:17.687Z", + "time": "2017-06-11T20:41:36.025Z", "url": "http://apidocjs.com", - "version": "0.17.5" + "version": "0.17.6" } }); diff --git a/doc/api_project.json b/doc/api_project.json index 8962ef15d..b27e7e63e 100644 --- a/doc/api_project.json +++ b/doc/api_project.json @@ -1,15 +1,15 @@ { "title": "Authelia API documentation", "name": "authelia", - "version": "1.0.11", - "description": "2-factor authentication server using LDAP as 1st factor and TOTP or U2F as 2nd factor", + "version": "2.1.3", + "description": "2FA Single Sign-On server for nginx using LDAP, TOTP and U2F", "sampleUrl": false, "defaultVersion": "0.0.0", "apidoc": "0.3.0", "generator": { "name": "apidoc", - "time": "2017-01-29T00:44:17.687Z", + "time": "2017-06-11T20:41:36.025Z", "url": "http://apidocjs.com", - "version": "0.17.5" + "version": "0.17.6" } } diff --git a/doc/css/style.css b/doc/css/style.css index eb953166b..6468b2b22 100644 --- a/doc/css/style.css +++ b/doc/css/style.css @@ -172,6 +172,7 @@ pre { border-radius: 6px; position: relative; margin: 10px 0 20px 0; + overflow-x: auto; } pre.prettyprint { diff --git a/doc/index.html b/doc/index.html index d6347f26e..5f04deda5 100644 --- a/doc/index.html +++ b/doc/index.html @@ -224,7 +224,7 @@
{{#each params.examples}}
-
{{{reformat content type}}}
+
{{reformat content type}}
{{/each}}
@@ -274,7 +274,7 @@ {{#each this}}
- +
{{{type}}}
{{/each}} diff --git a/doc/locales/locale.js b/doc/locales/locale.js index efe980ab6..ba82385ad 100644 --- a/doc/locales/locale.js +++ b/doc/locales/locale.js @@ -9,6 +9,8 @@ define([ './locales/pt_br.js', './locales/ro.js', './locales/ru.js', + './locales/tr.js', + './locales/vi.js', './locales/zh.js', './locales/zh_cn.js' ], function() { diff --git a/doc/locales/tr.js b/doc/locales/tr.js new file mode 100644 index 000000000..5c64e52da --- /dev/null +++ b/doc/locales/tr.js @@ -0,0 +1,25 @@ +define({ + tr: { + 'Allowed values:' : 'İzin verilen değerler:', + 'Compare all with predecessor': 'Tümünü öncekiler ile karşılaştır', + 'compare changes to:' : 'değişiklikleri karşılaştır:', + 'compared to' : 'karşılaştır', + 'Default value:' : 'Varsayılan değer:', + 'Description' : 'Açıklama', + 'Field' : 'Alan', + 'General' : 'Genel', + 'Generated with' : 'Oluşturan', + 'Name' : 'İsim', + 'No response values.' : 'Dönüş verisi yok.', + 'optional' : 'opsiyonel', + 'Parameter' : 'Parametre', + 'Permission:' : 'İzin:', + 'Response' : 'Dönüş', + 'Send' : 'Gönder', + 'Send a Sample Request' : 'Örnek istek gönder', + 'show up to version:' : 'bu versiyona kadar göster:', + 'Size range:' : 'Boyut aralığı:', + 'Type' : 'Tip', + 'url' : 'url' + } +}); diff --git a/doc/locales/vi.js b/doc/locales/vi.js new file mode 100644 index 000000000..7ce770507 --- /dev/null +++ b/doc/locales/vi.js @@ -0,0 +1,25 @@ +define({ + vi: { + 'Allowed values:' : 'Giá trị chấp nhận:', + 'Compare all with predecessor': 'So sánh với tất cả phiên bản trước', + 'compare changes to:' : 'so sánh sự thay đổi với:', + 'compared to' : 'so sánh với', + 'Default value:' : 'Giá trị mặc định:', + 'Description' : 'Chú thích', + 'Field' : 'Trường dữ liệu', + 'General' : 'Tổng quan', + 'Generated with' : 'Được tạo bởi', + 'Name' : 'Tên', + 'No response values.' : 'Không có kết quả trả về.', + 'optional' : 'Tùy chọn', + 'Parameter' : 'Tham số', + 'Permission:' : 'Quyền hạn:', + 'Response' : 'Kết quả', + 'Send' : 'Gửi', + 'Send a Sample Request' : 'Gửi một yêu cầu mẫu', + 'show up to version:' : 'hiển thị phiên bản:', + 'Size range:' : 'Kích cỡ:', + 'Type' : 'Kiểu', + 'url' : 'liên kết' + } +}); diff --git a/doc/utils/send_sample_request.js b/doc/utils/send_sample_request.js index a03877ec6..f2396ea92 100755 --- a/doc/utils/send_sample_request.js +++ b/doc/utils/send_sample_request.js @@ -50,7 +50,9 @@ define([ var paramType = {}; $root.find(".sample-request-param:checked").each(function(i, element) { var group = $(element).data("sample-request-param-group-id"); - $root.find("[data-sample-request-param-group=\"" + group + "\"]").each(function(i, element) { + $root.find("[data-sample-request-param-group=\"" + group + "\"]").not(function(){ + return $(this).val() == "" && $(this).is("[data-sample-request-param-optional='true']"); + }).each(function(i, element) { var key = $(element).data("sample-request-param-name"); var value = element.value; if ( ! element.optional && element.defaultValue !== '') { diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index bbdd7bc4d..79b5208b5 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -4,8 +4,8 @@ services: auth: volumes: - ./test:/usr/src/test - - ./src/views:/usr/src/views - - ./src/public_html:/usr/src/public_html + - ./dist/src/server:/usr/src + - ./node_modules:/usr/src/node_modules - ./config.yml:/etc/auth-server/config.yml:ro ldap-admin: diff --git a/example/ldap/base.ldif b/example/ldap/base.ldif index 07d4e5a89..97ca0356d 100644 --- a/example/ldap/base.ldif +++ b/example/ldap/base.ldif @@ -25,7 +25,7 @@ dn: cn=john,ou=users,dc=example,dc=com cn: john objectclass: inetOrgPerson objectclass: top -mail: john.doe@example.com +mail: clement.michaud34@gmail.com sn: John Doe userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g= diff --git a/example/nginx_conf/nginx.conf b/example/nginx_conf/nginx.conf index fc666447f..400eb115b 100644 --- a/example/nginx_conf/nginx.conf +++ b/example/nginx_conf/nginx.conf @@ -30,10 +30,6 @@ http { ssl_certificate /etc/ssl/server.crt; ssl_certificate_key /etc/ssl/server.key; - error_page 401 = @error401; - location @error401 { - return 302 https://auth.test.local:8080/login?redirect=$scheme://$http_host$request_uri; - } location / { proxy_set_header X-Original-URI $request_uri; @@ -41,18 +37,12 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_pass http://auth/; - } - location /js/ { - proxy_pass http://auth/js/; - } + proxy_intercept_errors on; - location /img/ { - proxy_pass http://auth/img/; - } - - location /css/ { - proxy_pass http://auth/css/; + error_page 401 = /error/401; + error_page 403 = /error/403; + error_page 404 = /error/404; } } @@ -61,8 +51,7 @@ http { root /usr/share/nginx/html; server_name secret1.test.local secret2.test.local secret.test.local - home.test.local mx1.mail.test.local mx2.mail.test.local - localhost; + home.test.local mx1.mail.test.local mx2.mail.test.local; ssl on; ssl_certificate /etc/ssl/server.crt; @@ -70,7 +59,7 @@ http { error_page 401 = @error401; location @error401 { - return 302 https://auth.test.local:8080/login?redirect=$scheme://$http_host$request_uri; + return 302 https://auth.test.local:8080; } location /auth_verify { diff --git a/images/email_confirmation.png b/images/email_confirmation.png new file mode 100644 index 0000000000000000000000000000000000000000..fd0d84e0c8d3713142633279353d3a71c53f8ce0 GIT binary patch literal 26317 zcmaI7WmsH6vo1OT65K67u)*D(KyU~iJh;2NO9<``L4v!xLvRo75D0_2J9m-q?7Po- z_PIAd$js{Q>grn6-EX~B6RIF5fsBBU00M!Kr6fhafIv_dAkdp}I4Iyr;cSg8@E^3J zu#_?!9NhAT{2$;env)@vy#1r3taA+Ww=+b?>E)9>cZ8fE?@^Tl~lGE0Ol9 zBB~>;uyveKe9P9^+SwCU>y9!R_^q{V4?;&)9zXPJ-^eQ92Y$=*qtJ1>yneL*Ed(b8 zN4nOT^@vWtmww=?y|e3e<=I>z{l}qs5Z9$OBc#`WWh|&?2g$+5aDbE5;O&M|BNb3mtS(gm>%Oo zZh9O<7^pR~ z;(G)S>XI&qw@t|6zqOb=#A1q>^oRZ(zTMaJI@3R~i28+Y8}!$Be%l|mAk07iOACF| zL64WX!dz#W7r?42$#5i8q6O1Vi!57mNxp(7(?}PtVU-BS;@d zR;Xt#VtVfbG4TKe3^0Z~B0Ad9lB|sXZmUfm5UO{kui+v#Ow~z_0eq{vbz|&ojQcmK zI^QROH-IdPIQe7(B{Yg9l#&>bKPM3VLT@qg1W=2apH=36wyUqTdE-CN7X z<+o7X4r^Q=fXC_vuUbC-@=P~!_+rtx{9Sm%zy<~yXek5TIW=pc1b=&2R-)-MsGf$7 zl_W=lV+PzAG}q$gflxTX%}`j7PB83^0S5eCZ|qlbc8Zdy|0fbG1n7_)Q~LQ0I}*~@ z7UW;au%SC!vSFfNIe}QTS1T2q(xPI7_igeJGNHk>VdVI)sseKL(5se@5bKWW3hmB) za=Ggq-6=(z2|rh_0#f`aT+xnPTGi=Ml9h0O@=Nk(U3gH4-f;rM{KSIP#_X_nPtE1B zOfe_c4FsGKV6i};E(|b6s3;g67c*m`Mlf>(_Kpq}Be>6iku;1Icp)I^a8rLdIF$;4 z>GY0_Rc3tTR|8gkIHmm!Z?c%Bp(dpM>7PlHP}$VzPkB@VEB%qbaFf0ocrGyHoDOHS zj23}%4F2CRoYYh_h|bOnoIWG=hs|C99W2mP{O1FY|0lEnJC9$42Q)eQYgMCkWf2sA zftl#DUCuqQa9ri6vEE6B1W~&^-ur7oJAS1?17xbh{i=}AqEA+mmERx(pJ^3_-y_lG zn!y3u^9Pk2}z@o+6+gy)K+nEMZ!f z#a#yXif5W4aFGoV-9;--jv=eM<{^g*fo|Q3IuPat1c2lVWVKRL2`Tb;fNy?X=R*At{so2iz3xhukLBM8FR}bQN$vtdi3_%qar5MDf+0$6$ zN!5(YVX*^&fEOjCN5Hn*g4|+r)S8UJDjk)yMP6xwOxWUj)StW26v=mEwOlp#6&7?3 z-?b8wIFkD)xidI8m@@Vx|LuCJ(Fq#c7Ry$P2w~WY->oI8mJ!TIe>R#uzP;^wg~btf z^{W9noQU*l-Q(610oas&cX2+ak;4qnZ|$A_bgz!c%6|Qbj=r~E-d+c}wHqR0@+Y6JRXkG?kRGe!JWl_66MJ8*TpM1f_huVxM@|+OppH zb=T8_WTwWyF%46_WKTO)e17^ccZD}+aR4flL2G+U=a*NmiJ7*}V&@a*KX#T%U=&Pj zZfmy0*ZOezj?o++J&{CSYGQct;<3T+duwic+Q_`k>d7%%1UkA{FYQ$0cH6CioJG26 zs@?Ep?LcF*Jllv9ip!twBZ9^yK*W74%W-~4Q6S8B14q?yxmg_42iS_IE3O|O>nvp+ zw&&|tvd(j+eH$IWnQDf`wb<_TMqaNqIqfZZuZBc|r*z9pN7LpSC!epEx!7-7oOydf zB5l8B#$_hfUffyh^r<;ote*5HPWpSg+OAlfq+76pX@x2z*V8j<+4yE9wvxRs+iUQT z-mjD805U9-ZOvMpQvCP3g&g~?PMO)#%OMpP*ZJeijXFg|)G~iYl5@b| zfEwpZq1=}1Re=+|BJ0tgC!D*jC!Na)=f5U%`!oaQzlRjTfeFwTtr{kuvpVj!`8=NU zIHO$_vac^VOzYS_NJZPbZJ3GKbH@)HDm+kJrM7wyGq|kJIdx;suzs<{?;a15bB z78c%DlX1=1ayrWsRK02mDAJnti;MP}Jl6moaBeu{Wo2ZWULKX&j>gp-^!$Z3 z4((Vhi5`*rj`0WYci@^mn|U9{tzAuDNcm5Z#u<*2rox}%lJUM4nb}>b=QY zTrDj&bz~dwEa$19xh!T*S|;9?E~ICIqa(rizy^6}X}vNOH9GKAE06L#i2NvpqRPqX`F${)Ge!IF}N7@k{TUJ=FJ*u+6V7=PqyW$fx zbfskVG;x0J&G>vvz0#Sm`<+vxP2Ed>YFfcKOHTEJ>3Cz74+Gu!#cyd@?|9!4UFynH zZYST#Z{qd$+S(WSu{=D6(Tuv8Gje^&>qWO=$@3hKyM-3xv$NVZ`(DrW){|HnZ46YY z3lh<3wg*b@^>G>@7`2KkJL#Wtz7e!?K%NjIR@uf>-(K$26un zey`@nJ|r{Z_2+OVE4S_Ql)7Fj{{6vmvL70P6P5^7AY&)j1=J^;1XpMm0=gebo}%e zWl7wai2j`D9VN|!p45HbJJ+>cqKp?YeNQGX`RD79h5M3Z7YEr64=0DMA-mS*Y11E^ z-?|H^J^pf3VzcCM;UpQ!^&bq}6aB1!?|OjIJC5G-Commvon=$4r|O$V)qk*E$?$3@ zz{0+h+h-|e!YNo0SeFnDYE65_ji+k=IcXEgH<)&wLuBbJx+)ky+)w-VG?+z9ck*0|2G3KgdjIUKs7Alx@)@Te7@)8m&qVcEQNMJ)p!)PH3?oX(&59wDw7$q)lS# z#ILQsXy<>|m?c}SS&Q-LZ6VU~@KJO9W7e9>%-o0X@}6V{m^p5aEHv1!hQwdpC(}hf z&sF>(O^f1jmz?{~Rf`GSJ+bM#f~k`oLed@?f)u@Sp=0%~-Scy()<)%LN=6E4Iq&ZyMFJMPVv)$02W$T8*@EFtW}np}~0J#{Lmq5U3(2=O}%61g5e z1w~$l0Wa&&@CfcOcvhn91zZbeCSN(aPbSfoQ21vs|7}e;F%x~Mq4Ct_UY2jm$#tuh zcOJ~bR%{V6va%Xj+Sj_IRA=VY*11z4@=jKUj@Qz+B33=ehGd)I$5wg8YIMg7A5NprYt>)% z(#eeLJ1VX+>*ceqmg^K@l9CfE&4s6T45W-5MChNaf==3>OpaE?N@~vDxBJ}>$h2D% zG*q*h9zp?loCIb{+KUly;+o6nLh}Sa#O&}n^0>N;#Ea8?8}Wx}R*@vSRM__$mrc&& zyV6ci{ia5x<+U}R7oV(dPROIny7hjCC)tHwU-u!}waS?#-}1|J8qHCH?bCT?x-|`g zyYPx~4saD+p*wRO^8g?~@9GyhZpVGw_1H3*=#1;$X!+QHqnC{^9ySD}Rh-5FA$*6O zhZ&qUpR)m4gDgmfgmO_bIxmmUSo4L45+0u%A6nVYueidhATM3mij%iFQ(LuGBCp#z zR2HCB-~G|7G=9LZurome>=Y0NsxmYb;<*FS6QKn6KpgCSmycYs6{xX-zKDenYgX~U zWv@R~na$(8*5-8_am7ARUb7x$R!a^4qvT>pX6S>cD1j8@UW$U5+(zL!kv;!hv2jQs zwilm)-@Kehh%#>$xzBFX_(f4U?#Q##a#`{{W{x7fjhb&#EHT?@I0i-UX2(0}@aes} z<)d_$^OB+VT_;}BEWJdu_Uhd~s*Qe}88r#{-|cKd6z0e3rD6u3Tf@R!d&G z+Bj6&AJ3Io9eJ~Rytlxd;VLu1G@OvRDUm1s@M|&A%pF1SJR0fwLEmj_95hfcs7MZz zgp@qeXPNhj1TDNT-7Bom!2i>q{IVGNs?E1fno>h1$FY_NHoTQ*q(7_ojsthpcgjH) z?3aJmzWMKstTDqYO?k@ic;aPlJj`z=(HjNBdJxw&3gV;u>I_N&6{>79X64~l&uf5dH9hl?M}-R!f!>=F6!&|ZHewI_5|zXGYmwAO>PpN$1O7i zU~DgiFgwX@6qXi!&(s&W)|In(9Jd`R1%>pH+sSNhT$dtHZ%R%LNdwgjH*?6Mkpz6; ztDw2Y*=5T!x5%`4cINQ<?PcsfotDhRG3ft60p!QnJSN#{!#La8iH2 zfoq3{nWjQ15=HTKp6<2}`EFY0)fjBasC6-cnU&ce#li+9E^(|x_sc&-yf4SAas=pm zKW5em#uuANIkgiLdjpp|wB|}H*B(sDT_OhOZpME|)a|AIP>PGsF?TEKtW;Vavn6f3 zn$7K?D}194(qhCt;X{Cds#G{S6vziQKSJ}qR^jxX6R#LJR(=nNGI>9s2oQ(U!Tyq; zLWJ!syPMf$4Ezzua*)H%aa4ioCkCyt=fP6{5iZvOoE>ylIE~tEITz*wDMUgVCKg7LwkXK9?|k$H%T$V=#9yA{CT&Y6Pina31AvD)qB zjWhORua_8POJP$ss6ht9Lm;zw2@Zpv0hV&ODLGShm?`sx7oAMxH{B~H_m zm5W~u%JnY#mNFdlI1k_$j%V{u+%jEYQBC#|K!3yH-blGddp8Xng_mqqUt<-dl*#Z@cwVIY{=)Z02ipL^bJJcUs11=f+PF4 z(aQ75CA9Nu5IvuG@Vzr{hMZtrOta~a%FU$W`gh>1Fc_rwAWJ;CD{ew3?hgjBl3Pqw z2TI&DG5{Bngx}D3Cu^gv9Y*+hz0KFe$Yu8!GA^~lMnQ0a^9FQW0uJsWO?NJkhO0FN zslziD^WAaa+LX3a`P7KqtX0@oa-ijV*juuTBY{3`Ws>riI}Nh&4V#m{*!O+lK`~1ewYn(P~MO3jvG~|aD z!W~TMBK%RO>qCF}^|8Q5L3x0P8uEp*o@RnBxVEQ`B00!ByT8muhm8W7zH}b&Q z9hDOOhCV*z+GELH0nnR%5Rx3PckF}4zWjpQ?oEQd?_a5~(zrir^l<6hfv*Ivx+9EE zHOZ|R!z*LjwPdgz_Ll9YF4OWE{Tx`U9&%{W{MWanYCFQOv}+kDKW5HFDnVE=B_QX2 zlB};oUXkW&v8uyjOvXo&$7I)O`+*o_r>wGv^ zLlt0;lwWxCZmRSxs`MQ^=yQ%8rGnTmrkZOuQh&+ve&@?i$cv%ILk2uRrOC47UT+!)Uq>nkJ_-00dLjVi?CtZu0FljYcgBx23B$jKaqp`bS;17ELBIpBJ8+#gU{SC5&r5xUZT?K!R9H@>V~Ijo*f z%7HEJl09|rPwE?AZhSXA5xStd_X<*Q<$#39r|LU(#+|}<@n~=Lu-OcV1YI^wJMW@V z4_yWvYL7zQ+a7x-o;CYx_y00|#3uM2{`S-RjgfDG>FoF6!{bRLv-oeTy*@x|43MY3)1i2b2>j$i?)05Q2ThES{X zEgVJ#5wG6eR}+8jFB@53s)k?IeY`^LbUkaZZXV_b=^Jfa4k{hq2AYJFqS!;k(oqkb zc!5(5gF$bWjdMJy<5~WO&zWx5rg52Y{pGCl>7MIpz4Wejml~cCtd5AXskQnGwc1(xcoiyMqtTxcg2$zx`I-@&kcJwkUl4K zTISZ#13lCFgV(s*8$IXK%<Ei_a&6e@QRYMi_mVPgyExfC2l80N0;+C6sx= ziThcAYT_+$fJRYmpM8Tw_K+Et=JJ`0^uF3ckcg-+@og!AJmf;T7vyGP%`89)g9_@5 zNEry1W_mBPJv&nzjU?X@#!szIFiQXXj3!6RTfy)3)b`_H)~_eL^G$Q=WGnBe@IKAn zOYAgHt6H*4?8Zk|6d>iTEg4i&eahzn9Cg%R$#mU&hOOzV=lm+4TgwJ6^$oOTU@E+# zO^0!exg4;qyU&k11={W}*2@f>?VGidHGMBGmbIJa^p5^0ku~Fr6s`JuLH!v!xg_p9 z%Xl$vtUQO*0iF~(J+BagIQNrkI2__ds{_m>)3agsJWIe}NzOC~aV114YlWYK(K?=X zOyxhe^`#I4w%m5tgzw$y6YAXWB`E0VVBhDF;HG0e1~@7~n(vjUn~l=;ycv$zihx#1 z5s|g)cgGV)mgfG>h0|9VC(kQZsa>fTux?bE4&rr z-quoqtwvnduN$o7uuBuqc9x(b->gl3CyLImKG{}C&~hCPYgv0z-uRsBN>RCs*d_BZ zZE5~1HIj+EC1cTuyiF=c5*<=*_QEY@nVChWuP1}6?&>Mx3bfuVT%l>2V}<8e*i^LF zyas+49jq$#>l$Xy*1i(?KV-CYM~mB0`C&&U+XeA6^-b$vC8K2|dwG0MtTy6TZ)Sf! zR0EERuS9Q>>R-0bEA4@<I{&qOOLxVgRBl zv;KT82wrhR%d};btqiG)HScJhsJe{sZ`aa?&f9s3_pnvM8r&bxQ_-*;U;f~utZ`gx zUBUr+j6h0W26TfU%6MnLiziKb!_L|#j3rrKv1wD;i|Jz-mCq_Pkug0Nd`E}oOt`du_2gZ!9D1ou$rOWz7KE* zsPw=>6Z;yuu};K`vIVFKnm<2Lc`Ldkr=+7Sdq|CsY(^m6crhkFyfDfU0y}(ii%#h! z{jo7zi%nqfJiDd0X54bVkN7!w8iJ1*(jt(5yA~|}F;y4p=Il4-YA|IVl~EH52#zEN zC0Jy1h_o;s+LG8P%buU9I8zc}E2}UTTx9r3rHSxlgjAM=fT-j$WMztT?f;;YUg}v8; zvHUnPOxCW*sG1#Wf<9Lz2cbNNCVYY)s<@te??(8x6cMQHnrp;@xWm`>Qq+iU3bxUc zKE2U%;hMQ1)>%}Xf%R93KKrZqx5_w^>MkJx%plP4p)TG@;0D;$lh|7mR%oEy=XK-grng|hk+;oWsG?LYE6D%%K ze!)LQA%qR7{@lETzAY5+ubsaNg!cCst zznyQr4r(0w<0;5N&~wR`(ZiV(QbtA`?Q^L=D6qSUga`r-099JoB~P3}(w#qlZc%-E z*V+kkT$T=_T$ew*I7EsMc=_l#J>A>H@16Y}koyrCG6+e#U3l++braP9yC?sefw~Q< z=Mnhu=r)<=m^$`X@V48{G2GHCJph*q$nkR?-!2OBeYK659Vo-1nv^iGpSN8nAavh! zkkM3*@Mbk|=SLI9x^aajmRGlK5&^;ay%zz^(VJY{-u4EB1d?#h8FR*0ehgV4IN>Gs z#k!k%q43(?`?)fQDp>b&p|X=}lXW5&(g&UUml!XCvGfF~!nqg7SLb9UA&NQP)&N4u z}%tA zGHpvbu@CqCP2thaar2{?Dj~{1kInU^V3W^HYig`c@+anwjlDN2<3$RRLWi{@0(hB# z$aWvBbtp7A7~Ae8h{zKZrNCS8WXq%1q~k=SZu8;ok*zm5pTq#A&%*n&d?6DFMwaPc zd5k|+eIyK9T{8Lyyx~ek&(8`Nh+m#n3HXBF-f!dg9le|yu8@KNCb$b$gmOff3%&ZT zcIsPj@7|uky0ZrTVZAFQqn4Av85|j3#-X5Y!&9XtJLk3%pWyv3+(_S;z0v2#`<~$Uw@`a=X^3lojVg`@`Ict{n<;b4a@U=v?E=r2RoM z`$qdofiy{#0LAo?xA_*^53NnKzvA5z_Q>jPaDILyZisV?(a7>R2c9_SS3ACppQTcp z92T$6s!+4eCv!uC8vA@7#U7qeOy_%2iR}=?5P3w;(#qSMwXk;?ALmh;7bbnTvgQ8^JkmPGBGLFfR-b ziHv;Go5(`(bqQAjXP+LebP3?0v@tTO0xF!t``Kb-#xeTj23)Zmpd6aI9FY&vSLB5c zrl0Sj>fW9=@tc#mn+FK>{iD%1Tn_ZA8xKfmH(#5M$g z@-$$BDN-!B(V?t+yPeF~$gKRnB@5SIYFJ!4lzqSgfno%&m`AC_xMh;hUXN$dO*qukJOp!8Jy6lq$EIH}?SuVPLIHvkaRjLJcx95Y_WZp9>PLXcMcM}1 zvR0GckkO%B$y>ku(`khLf|-Xw?P79oN+>#Jvzqd)sKJM0w5nk{fDO38K}N1-T>F*Kt&TMOu7@{0H(zVg%Jw&Rp5o0V?-iL60G%^?Cjri z;-#W2gT5!tR!E5qV&0#*N2@lI1O2}s2{?_G?5URH<(;a!5V0bZSjupoC6mKZEG$I^tfMw|$$rve*{1W{kY{_e=$!7Nb4E zVo>OlO|t^0%cy@h4Zvi%hc3xuHW^3@ah47p4G+HYzD-)}SIHw!H6u?#H=0^t36dD9 zVHx6Rlt}*U5V$ZsEzvS1=7iJYEa%u+f%QK}^BpaSu5Z<|VN$o#<3oU~h1@weI>a<2 z?8xOIU3tk=9ZwTFAaUe`f{MRZ|LJeG`w_}V3*5Fvf(!HQ=?QL$IrtBlCzlb=2WXXk z(;c6bnA{$1@9A{1E}pSYcw0Z9Cz(o8JI0v(rgb|loxF<{AHCn5coM=tMySF72s<3% zL{m!9K2d;v~HGKQSwhfIe5~(tP@rC-hrKrf!^u<=o z8?=3WbnV30yqK;t`E>%IZ-@o@%4McUtyU7LUK)PUWUTgn+I2{u~z-Ua>0dZpGL0*z)0- z`LMPjNd>FX=q35aiwAKYzf1s@CBg&iC%>6aXC+WhSeimc;rrOUj{2e%WsK3MV)Hag zV5^ryrLGdF9nJ5Uzy|Oq`ql3(G5lwP)yd1AuGOZLFmZ*2k~za3tQ@A~ea_!u9zQaI zMudcVzWn@~xP7TXghjM?Ark^sWucBcS>_WlMSKn z4D@7%SDc8x(|W7_27>9QGE|5KFIcwz;`HnOyTb3jh)wYdP1mE0MuCS#`Hi#y4&l?c zy#CYaz%44Q^4kp>@absC|B4YwoHh+|ebhT& zraNy&g-1;^cBzl4vb?wF$~)y{5;k}P>O&_2&46M_0KHyp7B3VL&z|_+^-Wa%#Ks1; zW>&}LBa^R2D=8_C8rmn(kC-{#g*@H?!_J^>KT(XcrBWuV#mmsYh0n&nED$RL#04_| znB6ZcqR&^TQ1txJNxyOjv&s{qSm{E_ws|W|EPSw}jAvUc`_l3lt7yfGfYV|(% z_5EW`2zXz(9H}9^ykCOv3K(P#0{Nl2aqbnTW$xsI z6XCr%qocCeiM|~wsbM2;=172i1yC=-nkoa1g^909@jb>ohyS$T#(%j*rRMYWccc>` zJcO4pYBKsAWl$uRT|HK;`s0r72;VJkYnJCRo)%CK05Hx~U+@SGD;u4CZZ?l%ss3K5 z{jMf$@BFGI{_D|ru|q_6J@Ty;!1F^@dE5@_b*#95UaT^6W=3jK^37f3 z-q0z>KgrvA)+n{cUVqLc*FA}^Is}cFH6nZ<3VQ!W5wO(~4^lED5u;gpHEd+yc@0o5*$VD#GpuXfTwem>G)whV!&N5oXzY zg~ZD}#!YT5Pm+9d3lm-u=xm8lc}`* zAY*`%1lVSW=Eoy~S8eIjzL_;H_ZiqXrMG|}LKjOX({!Z6jjz-{J!_jqQ0b>&dB=Nx z&G!u>+e(FYpMVB>QZRt^Fzi_-@8}=?HhaZ%0@JF~Z)4<8lL3pYs&m8H4OD>avyJU7 zFCyno0vjHvFMm@Bm7$mPl#<_cJzA2Z zH}!f>0Bec`XvAaZ=V&z5Hv#|h9GGsDqU}0J5UckJukFK_kShlP?8?)Bc@6j`4AM%q zkiiYRz^Z%te^tk&y4--x3NteEMZC&mruQxp_Wx6+TyUnPFt?LA_h`!P-PikSDocDH zPAaN+|C_pPk=93~Nq}B2b4LW!;`x)hjl^0x^w#-~0?R};B&^4nnppK6FeK@l-Mhb{ z_W+?S)O>M^ImtX&xRb=#rFdmBu2SW;1NVQU(3C7<>9%=hEtj`;v6V5F~6IHBXL4Zhl2E?7g zJDHF$b4E68{u^PDA|M~umF8eQL`F)xrNU;nN8bpBt~~uM_qrM-QJz(-_yH%yEH-W7 zl#l<4;SEH=lSYv89VbPBD(T}U#y}uj1UfZZ>P<{tR1{8o{RT&$)s`!DOq)%jHVzbg zgXXa8=w96cIAwu<|3^BAUTO4bz_nPJR%K82FNTI*n|qB>^5^_7?aIrBHi89TAj(ea zxQp)&FzQ>;t~L2+9h6H9j|A=wj<`Fd^J&`+b<_xY z)xXO_$>t^{LK00aXVrcz-y?>_u1 z(tfYn@{OUO%sYTCnlgNoU*)z^i@*StVe^ zVwc497M`n~^^#7<>xGdOK47|FIh{RLOmvM_x#MAm`Kmn2g~BgKyo<(HC~HA8go5AZ z9G{f;q+8ppQ+Q|FT16aD1b!?{5P61jb*#RZ!JK9kbcJ^7*h3a6JH8(pB3-(25Hk$s zNnwGv=nWuz^m`XPGd#*?n&x!xHUH8M1C(L)P5I_)NQ5@OEoK>NMZ{uXSX!|}KFxhN zq^m4iR)=g1@9z&on-|ROKMhpuHV=p&J*(}1Wf<<6z1rEIg)BxiWP7~~4Z#)!lGd2X z6WYcT71^4%FXfG!@_wZ)wZWIFDL!0j9+nBBt}lDu?7N3XFiDf{L(^zsoH2*=C8|JL zF)3E)(pr=Vhv%6ZbgWB;^P`f6r=|?mAh7$D4GlB66xTZCB^^WcmPZPmUlwOb#~3PA zOH1i)@{t=#r0Hz&L&=MS4@xhLMQa}S9dRcJe5`wsdr~ z(EqSj>O3D_9Ll26-ycu+W#>?xxuOj#Eh(i7XQd}(fY|(n=G1S~P${vgr$$vPKA4?N zgk_~K-=7*jcwRE~#E;Uh0-md=C7(zW%&?zkA@frTMX1MoBOd&tpb77_bL+-++72^^ z1h-bvK%%VXctApff}1B566c7hL@<-K9lVV>{9ZSU(_%l(Nyfza=|C&S*)!-aKc`@u z?&+;Wo^?ILx4g14UvF1_bj2W65#AKtMGsZS+oRPp(Oxu-t>Drqqdqi6uQ`9{A74va zmYn7Yhi|uLjA%r9$qNed0i${rxHBVF^EOmLME%oiQ$k2{>Utn=@mNF?w9|hq%;gv& z%Hf(0g;ICi`qdQINaWTY561k~>!h$(ve6%#Bs>!p)%dJo>(kuO+|y(z;pO$UM(eZa zd0hlMkLz}EX3$E8+sRC>hNf$F@=#fcVE5urWm@BJcZssjOPlvcELaW4qes6`YrZoS zHj}Pq3K8DT0w(3qpRISUk}=V6*G~!zNzhnkQ)2rHYyA9CtDq^rAjVQGl{-bZ!rW^5 zgfRh@C|*yTzNs74$Zofsy8R@5kHzW4HFDmxIT@gIs-Re0*&9-+>zR|KU59JK6OAU> z@9(s&0QuhEEn%gL-9H!4BFD!j3(ZV(LbjRgT)kqxri`H$D-#JCd;O#&uZa*f;A7Nknig~q z?lr~6!8Pi8=(JNR?={*gz}TyM+SceNZ$d}3IE8-ypQzt4`jxP>= zNzOcH=An6zd0MVl5^tyuSKT0#TvXNDQ@ZCn9bx-7B7Cxp@@{6A0R#3LQPk3NN7G*U z$g+o`x5rVa1q3IHfZB048Pq2w3S25U9I?vTqod<=)T1i>59L>pYLXve6~=#yPyjzhjiVyM%NDNV&E;0`FI0!pGC}=;c(#T!cW+tQOIHGAy349D; zX;eF5vNhy_B&!?^#*DlVIY7#I!{-BRumqWk%B?*LzKzT>Jisoz&gG1%n?6B!RhMG*R+G&F(sUCx6%B?uKv%DHDwh&yk zMP`cAaV+`8F%jvCv(a59hii@Bx#rrW)0woTHioRAQ^WId3mdf+GCV2@4)4=_XrC_> z|GknwiJoslH89FbssEuG^e#9XTi11b)HQiW4I1FpNez!ItO&^$JANsjR92CfGc%o8 zC|Nq5Y|OJ2bmGNptL81ND{Ft%=Z*O>cwNWAc{No~1C56PzR{#&2zD*(OL?f&Rd9{T zDWdm;w?zCq-9WdSUC^+xJxSD&4G-G>==7Zvs)abLalW<5OkZk#lci^o$s%H7Jpo;|3%=%A_SD9DMY zVAZ@^BkG`|Uo1iC(^=Vnu==NvLB|^i-IURY`9WB3Tg_&~P`jqOcXB~}eSS&N$c(aX zN(3QiKuc8rLGU$)jqSaC^#@lHbwaxqn(G;{LRc+MfGrT=?#>~VTJyfFH@@K?qf1%Z zF8^8Oc2k@)Rf>#E=)Tt4`6g?Aaw&Ju*qrb9Q`J&RK}aTtG-GnOyd$HQ08SvH2_8M?V^L!Od$s^#Sg zqNV&VN99LnDC!!^bPmd`at*H9JkRHhi2$xWVko!f`g+ABPq zw)6Kp&Fb zRMHb6F^MmHIL$!e%EG{Pht*T}7`mvO!eDJ_ZF9gx?PBXPxRI`1#>hG1h^zuga z&iN_~iTT>0XUik|l_tzB{b5rDB^JgePf_o|mcjRl!>S_%-jLbUP^0~(rQdmsgb58Z zCn?ryh7E-q>b?$Z8zlzC#ku(v`Pp?}TW_@=gO5= z=Mf21Gj07Y4b*=RPRh93J+h@=UH@G=_)$wG6Mf=(y-lIeCtuq4Y%`X9h@OyZt&CR1 z67yoX#9=zGtfR5KT1cCBux{ogDho1qq&V?kwE%T%oG>lFkywy4mP+K)!gK}IGHL6b zw2F$Xs6SF#x_dTyaXwHW#fo|EH_^0~bx?>{>{*mi+ZLA%H{tINOMO9|S7RHXt^DHS zEGy)YV-VEme-hjkiEpdgo-@aq8eAHdc($E+fEzWBT=OhbeVy(NNi67|Y3oT`49&A_ z|ExC7UzkVQnzgGUfq4MM0}xw)###RhQF8OP%= z6}`^h7%Le^i<_>-l4f`NNN*Ob{7TDR5#f@iIT&M_=X8DVI$DxN`3i^?i}oPky6d&?JUpfaOhx%gS4Sj~J=WX8rNV`9C1} z8$O%=EBY2npBq6$;Km}?4+)VK(9$T1pvt*DGOpaQ)M@s}F86h{KQBXO5yiRNC?V5e z5yi@K{?k>ER8qm2;VjYi(w)#-NHLNxN-jSldExvp>LLhZrd(-0QzPTztYWW=g_1B{L;Ho7%WRo#lnI-ILwApfJ5KU~N7T2yrq0|>X|b<_(1AZ_&ge8r z*E|z(UQsek)g_%q{JYq64CK%@kTv%jkk+Y7QpH&p-bc zy8kE?#Ejj_1}*pjYtcM@!XcDBl{C(n+StZEGM2@t+RnawDkI#A2*FQ{8GRTj*Lc!lFHk!VAw;9`2WERdEu*e&*U%LXQO!hH_#`A_1 z(_YPWgG*IHT4BQ1Rc1VQJkgKV?QzcQYOc6Gf}_3edJ9}f$!9FhZ? zeit|6JuYL2nXpd&Y*_!GB@(t0ky4B@h-VOK;-b@Njdt@bOML7pO(#b=n(Hqc;+H3F zF>Os;MT@lr&lImMQ`eK{TgjRiMB-l3f+(f@*`;3ZLiyYnD+Z6O6>Fl61|SzYl>*mqqiV~Q6HllMnVvE zv>|#YhS7Wdw!C@H`=0MQXa3sPUTfd`E^F`k-Rmx!qNi`O`E8qtbu)9sh()$ivr72KI5Mg)Qc66kDqp9Mf*}Gwh5$S;4Dj4+l9nQ9pr~+4juX9)n@cLF%u-~ zF0dDcw9M8Z-nVs*_cVw)n!WLJdb+#Wg?T@T{iQ3FO20R~sP2UZ{OvLFs2c_NHay`n zK{}@mSVAgi&1X9yJv^n82@I*c_on82%ivz$!aIlO92ps@H!)R%SqHk>dZvY0f$vo; z7Rf2>-E&bYTTGcfm^d3>30G)TnxPo?|KX+R4!BRAE@>%O^FDloyI&*}G}4xCjqxHtig$q-iYR?McHpHkVUnn0(+9UQXu|5@wM~`+HyiFKUKef}U3U=7mUBH`_ zO-57Tz-K)3_AQowRg>IK@Ed&jpq-?^&HZ6|qd?8$Edm9~rMkHp!z7Ju7pq0U!+H^9 z)u+JJtF&bKQj)p$Vi78?x^#6(ga(cCV|d9vGJbw6_9bZHn1$^OWF7j#L{ZVD0PWUVB_*|Jp@Lp7x9(5xhf5H!o*R~?%^+eS~w}n5_(zX8_Q~mNo zEjHa))}wXj`-k)32;~7V>0zC^`r?pd#m0p>EyU_%c0E&de?J=IU;bc^VK<%?g>$}dE^^i% zlvQ_b?=KKr9=poqq7t?8yW3QPD6mI;VyC!@t%l1nSDU=>9(I4_Lv6X~pj~g6*0<<% zTGqlptF7;x$G(X&PJV@;I}L#WU>Eq!J&+FSljvWuA|EQN;8_ci0y{F>)su zT5#l`Jtm})7daX!8)!{xkwe^&;mwjcVQbvaHki6r`` zj_Jh9;n8OJNTnAoM+=5qXP=jE7c<#Oh^9_?I8oTVmjQnpx;yrnB!OyF&*2CptS7*h z8EYdgoq7++$ZB$1G$&tZ-|1F}8B@IpeQU{F*;^?|NeExMlaTREU!X-D3#>@3ONir# zGxo7hcxLqqit=VK1sFsuJ4{jB!1><2dvXd2Jyv`lUi+F7HcEGK^skSkQiOLvQ196c zj1HW=%2^C&aX$^?;fyh6S?xu3G=dxPnX(o>Y0lc2G(y{_=Z1#R=*huBnF$a<{99a{ zGUxj%gq|$*Ygx{*-DRauI6nuzg?=w_EGu@ZeIB(59|5Jk10pmY231d7Ge+uPhCe+9 zqF(>hgh+4Ql*ur*uSq^7>sI6854C+1SaTEL#R;vwP1Sp-5|J3-!h6@A*XXyuE6AL0 z=1B40B%APekDL!XY7qmr5Vpb$z%{%{3j==t5gz=(YEb*D@>8)MPc~5(o!Z%IDySrt z`aCk?jK*9CW(FBUVT=DJ{k;A)n`qW%E-fH~b=6OtnKEXdjV-jdYDCP6Vn5(xUs;&>8A6N{ zJ_^`$Z|MLVBOIi<`r(m@R1FEf*1L{9(Dk{R!nNUvLFYQ};yA$rTd*1DW~DE*dqaK9 z!9qEuO04zCw3tblb3E0r6oXG<&XB)Z_!Br0&s_pC-bXB^ZgSt|xbv5be)C8%uqm~< zVrufsWxN+!RIxuK6|~d~xR!@Gv7J%fB%A)HClu!2WR?!e&`43*s*F98CnmyBxNEy^9d_|IqPPI4+`0BIYSONMk)Yc*9e1b(uRE z^OJVJ245VnYV|1UF}h-j7K~hP^cJ$pR8CCOpBVMD@W~kMIub7HQ2DL|m9b|X-%8FA ztA$R-Zv$*32bZSTGp#`_V@y{Ysi#B6#8iN-kAP3NOgu}k1`Mwh^sxS?KMl=&-lInF z8^VKTc6Gk93BLBRufyTh5Q zji!Wvs(UGx4uJqy3b$V{wCTzu2;>I8ayYyi=14-xKD)op{kRf~3cj>(+(3J*sOV;% z8MOD=>IQoEH}0v+)xa*MJE8?Ak~jv8Yl_%2>=g~Yyg-IIW#dZ*``JtKD@3%sjn?SH zLrh7apQh@{RY_*2$Rqk1KPw=DF*%rNq*uwUl*1xS25fuFno`d!^LAEBo2->ME-WH)?N&Y5@wp}1w>j)tiHp56Oe(9`Q8zUu zW$ef{*Uq8Ezlv`v-}y!G>sfq)=BKwzJsmY=?X7iF#arPZW@adnK5XJrh_REbtmeydQ`p+?yt+LZ6dGerW`)ahr)Jh+yl9+ewG! zlXYX9y!}8RZG8SXM_lkDAev%_s$P5f4hZ{=>(ejX3VqZgu&*kwPDItTXNHO=<(1>K z&Sjg+=48nK*o@~#CBQXFOb+cX%6+Dglku>flsq>rBZY;5k??+k_4D$=6)(uzpqgLY zYVugOJ!pZAnfvLY7uN!V!E#XP%NHMTXP8KO{J1CDl%>%ZWGAG0wp z1${f&Za4obB*p?Uq*U68Y&E$reLdj9TDu4XM&+*~1O6ld9ZxVdPKyg?BOTpxa#Q>_ zff(l)+VJG`cX=UHi6g^_P`NVB^lI3gv6OmK4Y__CyNKgKo;e{90_CJq19YU!?-S4f zjuhjwwpzzLTy7y*@kTzBXH-uiGdxIn67o>Bnoy`QBudrFp6|HIzuYF?YGR7KTl7wM zoo%n3)|BA5L&rp)<$0xEN?jTs2dZVuSbve40th_83-|+WbDvABM}!p zMHqsAQFeQC+L?H(D6YebfzRugbyzam!3*RUoS{6Tb=C9i`n6O#pX_RIVb_m`jPl+N zeFFTPlt$0r;hB5F7}FU{_ro|$6}G-7F3)syAVw_O+(x37qPyyZx(3L}Bn1Z0EY(2kGj(Z2veosZYHr zSN%H?Ya&G?!-wMHCraPn7?3J+j;Yw{f1uXm(+%Fv`^T5I(7J}}ZLGn4G$nJTV(4vf$r+&-3= zuoC6K9p4(r@l|*^JEmD@zlQJTty%e$MQ-0aTT*{p`o)eXbKR+=$nJJthFeJRXFUqH zrjnArq@z3LyM+M(wp5D5Yvfhk*=d;dJK+TQiUbA0$6iwH1N$Vi{ZV#T)Jq-MOG8&b z6L1TabYL5vOv?pga;L47Vf&PzI0H2t4BAqi99hzBWj5m`DHm`3LbV$G!NhSLMc_BL zRm7yvhH>-Hml-$W^WfXmw7F}wmh{UJ`l&X8wKdyapD1(yyE3D8)*RkpNg-FHip zMK6LbaShdMh~evH)Q^85p{-{cv2kc_4=u{Ox9_8&vspTlsT`wMV)<3VGuUlf|5>pE z6Aiu)HI0gnrS)(63`L;a*F~#7k$Ms*%P;h`5KWL@wzzbBAA@GCYqSiKOdK4yr!a5c z#dVk2EyRxvlT+R$lIWv}@?}n)KG`dmNk)|+gAqBBbw_`JT31sleqFq5-LUh3tb1TT zq=`NM$weS$JeB^Ihs-rPLX32GWmt?}itb0Fa(#K0Q|3?eOM6#er-h_bGKkaTNa}k5 zqDLEMhb4@SE;H&PXPhaDx1jedgIcbKf0he>W>% zUv59AOVlkK^jZtI6yDNE0yeBa-EaIrsZu{yiWPXw1)HDGwq;4xcRfv1X0CIW40&O6 ztsfiW*%aat9IzTDDsg|ev~eaIeR7mzYgHpbQ`-<=J9n?h+=-i0VQf~8CzjCrHbGz* zR8B{nsfb*(MoYeo1K5<9{je)~F(*?VzHcK=dRM5WW5wI4gQum8+oP2I-< zo~h=fX;ajBju5PkpP4e7?S>^R7$rNPM}&4C2}$noSS`B!NLF?J?&Cf=zps?~0>OD< zi1S2pTgnuVki$Woa?T{nl>B}wk9ge+vHMtutMsgILy@k0DN#Iyh%RtjT&3^KuhLqB zCzR&q8FQQw#W^`w6C@*XQ%9LSv>Czti5Y5~oRFuprdb9{M2g~Y@8!D+nq6kuIZDzq zY7~L8c@>ViPNmB}`j%Cr_0+<(l&X;tCxv7AxIX%xub-wEL{++QKP6-=8KZq__XJg)Jnbgj!9kdX+uAjGJ8!gfzRMw;-6Ot`FBX)L9VCdh9Y;Mpcy-a!m z-+{1Wkb8>+m;Hes5crf}W;)|pGoxwofK88}+5|5tLL8#kr9igM9FT3Y>N4q6$FbdI z3e(#=4HU|}+^B2~K3O2(=D@-6Osq6-mGnPQ8WAWOv9F$}2n>YjktJ}9ln-I5O@PLz z)|ZZ#%7<(~>9sdv24eg9rb*?4++-E`IE(wdtk8TmYjWc7f&@JR9Gr%=U3=nzj36Tk z^+UeVV(*(&HQ%&f_Qk&y#unf1diQRp*l=>H+gNx_bmP6hxG)3D9^!(l{7$@ z?g41z-2-`h2sWzUrj(kr+`JCRGV=_zsIvQReExQ%n8w)T*q)A*E44RSWo7XeP3g!>yCBus8!Ub&`GXThC z#pK$~*AQ;Szg~9Y(qf};cQLMYuDN!x5QAj<8nn0=T6`6>6iw-kn}7B`pmTR@1epNE zd&X>getwn%q0PNig!pV6qOLI;!0&u_y*I{Gm)GaQ&ueSF^$K=9my~{-OUwg1IMUBO zIJ|65p2#?dRaS0ybS0INNL?KSuNH;mATjst8tp) zr$$A(BMZ#cyJxnS)&s=IWd}V$m;G@1IU1hxr6eks`j3Pj4QF2}x7WuY{$5)LxKffL zAu~8zl-`KT#YC;kOs>9}Jb-fr{pJ4fiPWSj9*JuDU?PVc?(WElWgV9_oi{`49d05j z+&s=*HZ`vnQ$ux^mq&W5PHFGNsPQOVVs_WQ4^LiH7lhRc<56wbl$9{L*X(+uY12*r z%l|EgB7Du~IxS1{@#$F+6+uu~3w9-41SDrdwZ)h{7t@-ix!h*Cugg%GF{SC;IlgNC z06Z4l=qpM^ODt@|VredZsY^IAQ_-gFF6iy=I=_hik#lWq8SKOh{m`{q6mU7Pbl@+J zJaMXzKRLFnjA)Qses#KXK6v87u`hY}idBgG+#3)U^3o^`CX+D4r=Tqc`K{o-uy=7Wr+(YVn{6cUt1WumLo8KS2jJ z+O}0K;Ii;~K1zH`r1Uh)NSeHxc6E<29JZWbmr$&@R}?`RW9R;<$3#d3L_~C$1u_Rp z)qAYm7w?1%6bKNu?9FWAsk=&jpdJ%y*9-q6uxIC>-)Y`Xa zY;@X+a!gc*RxjQq@d_z@DuMS}-97yvfq^p0f8c6reGf5ELG#s=a6kw4#kR;#JE|%J zYQk+U#FL64r}GVTDA^CZ52JcDahatg#4D0kENLN}N(DoI@$~zMsk&M?Z41`;zBHb- zI&6@n7n9iN1Hrkjxg;&gPafH!u&Z=6;I_T9xB_Y%pLLEbcSlQ$=ek0udB@>L zz!h%ED#9vM69lAOUA7-*3~pd9S5Vh#n;{wy?B*ApOx5oGm7UEVMAs|_tyRcjNA*b# zE1%%G>W|7sy>6(vMkldv@4~kNRJEW5{7RxcW~>5fy_MXa5AI|Qm~G2$U%%TR2fyv2 z`TSSSg==b|u$cUS4MX;0IxG2_-W=5bf^ziB`MS3uPMeWJdp55x%7OyYAWCM0cJS<( z{?(2Raea;4N+sN_C#$aq@Vf*L3oG1csyGPbu;55)XX{X7nY_Wl_U60@MfdI)9@e=% zw(7GptpC}`S-2xZE4w1BhBwMTOXZ7$U$)hlIjZIQ0;@bdGZd=oKBw?#BMYe2B6ahN zVR4kKMmDiBhFwtT%MykOc0=oqJ`${wNU~Jtrz<$(ZoP5^6L|;;vi=BEy2RBcEYaNO zGWA`X7n(n6kmHN|4jxz|QQS~`^MApXsvtiM-4=1|F+yjms$;9zhi-{u3(P zcROukBHS*_-@@1EqRlpa+}lB>TCJ>IoAV9k6MYHyi#MAzOY80+bMZ&fN_uJpzj`en z)3ulxUF~>Q8p}1HA%{4*_l>q+gDH;l+1kxtIIgy_BVG!~+4_F>+Wv&oQDy7Z*+MwI zFBPss(p-?#9%o)Z%T-|Curdze>Dl4N+|^Ni$i)(U9G!#%v)Cc_>;W{78osJj(lU|E zJ?_MTC(f)fT*_m+I(8iMt;0z)nL_``*Po1`D>XcE4#W}c!+k1L`z?cZ=vyXXQ%j<1 zPkqe#=rxJLO@PwB1bq~?(6k!)f%A_L8T(6WL0&xEiCx79DhLh)7 z^fe3ePzi3!&TYbV0iY57wQ(LTJK8)f|8NyR3B~le3VF|LtKU;kivYcT=4E6IfwXq% zo5Xd}n{1lzH#yY1|D5{w(f=I$|EB$W@b}TU^a9XL{cC$30OZ&yE;j%iR1dV90<$YG z0(+Wtf33JTns9HJZzLE>7KD*Ly}>Ctx-dNzXyR3Ezm3O>#|jacj$%5X-9fue_5B;J z)|zUm@P-#$A1)ZNC-*I=+_3x2&)yn9eh=h_WNUO_528C1UXJ#h2>AB^^M#er&uy8O zGW;Vj1Q-URYiY+t-H@j@cZa0(eh{INi|bRsm*a{+0dHOc+a;z7;F=U&_T+8`!MYz)vPFb%>)OCN@ea`GNlX|_B^ z)B9z|`~^_h67Z*HSMVOorT@}Zf}D#F{;&N=K#088n}{L-kUDkdVDE&P{Jn@$}G(o{a&7wR6d-~&ziXYV!e3`NVw%nkJ#<}yeq8vU1G%E z1z(;e3j^mxb7Nzs3ns;%_-EJVMTN}*AaQwlx%K+eB~PpP^x@J6E=se?4f9*#yWhz* z#kXsUN=ibm&*xgNug*2NajG~afvTI55=gzVTvP|6i&T#9VkB!$_O9N84y<=+$z(T{ zpzI@R8+ooFhXgL^6=HDcGKMPF=CjpbFom(+Ot;MxKRm-CwvmX^U06v;KHIAD*XGOL zbbRZkbm=8V8v}M>tad?%-X`G73CQ(S-0$nwu)8%+_JCIbPQ*=E;6~<9;N1Z@nt+ji zpZfRF{~Y{1?e7oNVmpAnmQsLk6%5%_8zN3CLGu9fz@691BW{-ebcwG1C_1{sBHZOK zUHX4%V7J-O$^UPC;B~;w@4kSv1g*USytaLx_a@s4LjPiV(T_&l8R-8+TCPi#$?+=% zT3O(T{QS>YbxzdPgR}Cc4W%^4H2}md;McfS?c6AL_xnEB_i8)j+L66w;}aUIbe^K> z_2;fm1x4Dogu_k^sZ${;^|h&Af6pgJ5&PCX%moQLy$j(JD4u$CPIE&B7<1{-ZH$G8 zQ4g*j)q}83f2h=q?Hi$Ot3wn=H3L*xbQk_5tu29gd8B4G2hCvWy3z1e=O5CsenyXS z^*7TTcDT<(6aC8I@C7{EMw7bc%b4FPW6h!6sX?BVB`qsV`h5QH zlh}PH%Ro%p`474Vtn1@?N1Kdd%Wpo=7# z9RtftX=a6|h`RnTH71UArr=6eX)5*OPU00Ro#QCypRxQ$yg^<2q*C54WruQA^oI2% z@7bwm@Js%Gtx4ayP5Yog>QdVZ&Tv}daBUSD4tG&+nLjL5TWfMGjZA|ff5(+k4Rt1> z0n5Bm5czYd*@$kHNllm(x2OG#K}e2}smVWg{66ybM8IXmHM_(_YK+s_x#0011C;MI zg9C+T!4Q?nz5k|Tv@u~3ST_iU)Opu^(T@G2IP8vIMl#aIxY2Mug=Xa+O-pLZXBe(q zfQ%bqlZF{CpO*d@%UFV93CVRxGq_skzLtv2NUi;&#I=|Jb_*kJqiWolu{w9%f0i=W zuHMJR@pvI>g!|ZdvT-$*=Z{dc_?j#EHI2Ajv`!qt0dD`;2K>F~{-*m_C=x~+OoRK+ la_YPJ49&kEF9_{50iBjmUA^Jom%spyvb=^|#miT3{~z0YQ1So( literal 0 HcmV?d00001 diff --git a/images/first_factor.png b/images/first_factor.png index 9f388325376db8af8f372d156566b9738760c12e..195bc3c98a96c1d9abbf6c3ebf3862df5f6b77bf 100644 GIT binary patch literal 20857 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 z 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 E18nG$lK=n! literal 0 HcmV?d00001 diff --git a/images/second_factor.png b/images/second_factor.png index e98452fb5999149e75fc13c5114cff2b64b83498..7b4761caffacdb43a4f14178531c96e5984b5032 100644 GIT binary patch literal 23759 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≻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 literal 64926 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ℑ9y|>EwrFq|)-d#k_)l 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 diff --git a/images/totp.png b/images/totp.png index 30f84a7b82f353cbe6dd801c9d2680a870f585f3..3c58db6169aae33d32cb907e55b43262cb8ce90a 100644 GIT binary patch literal 27934 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 literal 76258 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

=lvrTCDb5DH%+hZoU^O!e%4nc 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 Jq4F~D{{VoH@P_~Z diff --git a/package.json b/package.json index 6347bcb8d..f0e70b083 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,18 @@ { "name": "authelia", "version": "2.1.9", - "description": "2-factor authentication server using LDAP as 1st factor and TOTP or U2F as 2nd factor", + "description": "2FA Single Sign-On server for nginx using LDAP, TOTP and U2F", "main": "src/index.js", "bin": { "authelia": "src/index.js" }, "scripts": { - "test": "./node_modules/.bin/mocha --compilers ts:ts-node/register --recursive test/unitary", - "test-dbg": "./node_modules/.bin/mocha --debug-brk --compilers ts:ts-node/register --recursive test/unitary", - "int-test": "./node_modules/.bin/mocha --recursive test/integration", - "coverage": "./node_modules/.bin/istanbul cover _mocha -- -R spec --recursive test", - "build-ts": "tsc", - "watch-ts": "tsc -w", + "test": "./node_modules/.bin/mocha --compilers ts:ts-node/register --recursive test/client test/server", + "int-test": "./node_modules/.bin/mocha --compilers ts:ts-node/register --recursive test/integration", + "cover": "NODE_ENV=test nyc npm t", + "build": "tsc", "tslint": "tslint -c tslint.json -p tsconfig.json", - "serve": "node dist/src/index.js" + "serve": "node dist/server/index.js" }, "repository": { "type": "git", @@ -29,7 +27,7 @@ "title": "Authelia API documentation" }, "dependencies": { - "authdog": "^0.1.1", + "@types/cors": "^2.8.1", "bluebird": "^3.4.7", "body-parser": "^1.15.2", "dovehash": "0.0.5", @@ -40,8 +38,10 @@ "nedb": "^1.8.0", "nodemailer": "^2.7.0", "object-path": "^0.11.3", + "pug": "^2.0.0-rc.2", "randomstring": "^1.1.5", "speakeasy": "^2.0.0", + "u2f": "^0.1.2", "winston": "^2.3.1", "yamljs": "^0.2.8" }, @@ -52,6 +52,8 @@ "@types/ejs": "^2.3.33", "@types/express": "^4.0.35", "@types/express-session": "0.0.32", + "@types/jquery": "^2.0.45", + "@types/jsdom": "^2.0.30", "@types/ldapjs": "^1.0.0", "@types/mocha": "^2.2.41", "@types/mockdate": "^2.0.0", @@ -59,6 +61,7 @@ "@types/nodemailer": "^1.3.32", "@types/object-path": "^0.9.28", "@types/proxyquire": "^1.3.27", + "@types/query-string": "^4.3.1", "@types/randomstring": "^1.1.5", "@types/request": "0.0.43", "@types/sinon": "^2.2.1", @@ -66,12 +69,25 @@ "@types/tmp": "0.0.33", "@types/winston": "^2.3.2", "@types/yamljs": "^0.2.30", + "apidoc": "^0.17.6", + "browserify": "^14.3.0", "grunt": "^1.0.1", + "grunt-browserify": "^5.0.0", + "grunt-contrib-concat": "^1.0.1", "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-cssmin": "^2.2.0", + "grunt-contrib-watch": "^1.0.0", "grunt-run": "^0.6.0", + "istanbul": "^0.4.5", + "jquery": "^3.2.1", + "js-logger": "^1.3.0", + "jsdom": "^11.0.0", "mocha": "^3.2.0", "mockdate": "^2.0.1", + "notifyjs-browser": "^0.4.2", + "nyc": "^10.3.2", "proxyquire": "^1.8.0", + "query-string": "^4.3.4", "request": "^2.79.0", "should": "^11.1.1", "sinon": "^1.17.6", @@ -79,6 +95,31 @@ "tmp": "0.0.31", "ts-node": "^3.0.4", "tslint": "^5.2.0", - "typescript": "^2.3.2" + "typescript": "^2.3.2", + "u2f-api": "0.0.9", + "uglify-es": "^3.0.15" + }, + "nyc": { + "include": [ + "src/*.ts", + "src/**/*.ts" + ], + "exclude": [ + "doc", + "src/types", + "dist", + "test" + ], + "extension": [ + ".ts" + ], + "require": [ + "ts-node/register" + ], + "reporter": [ + "json", + "html" + ], + "all": true } } diff --git a/src/client/css/00-bootstrap.min.css b/src/client/css/00-bootstrap.min.css new file mode 100644 index 000000000..ed3905e0e --- /dev/null +++ b/src/client/css/00-bootstrap.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/src/client/css/01-main.css b/src/client/css/01-main.css new file mode 100644 index 000000000..377215e96 --- /dev/null +++ b/src/client/css/01-main.css @@ -0,0 +1,4 @@ + +body { + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NiIgaGVpZ2h0PSIxMDAiPgo8cmVjdCB3aWR0aD0iNTYiIGhlaWdodD0iMTAwIiBmaWxsPSIjRkZGRkZGIj48L3JlY3Q+CjxwYXRoIGQ9Ik0yOCA2NkwwIDUwTDAgMTZMMjggMEw1NiAxNkw1NiA1MEwyOCA2NkwyOCAxMDAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZDRkNGQyIgc3Ryb2tlLXdpZHRoPSIyIj48L3BhdGg+CjxwYXRoIGQ9Ik0yOCAwTDI4IDM0TDAgNTBMMCA4NEwyOCAxMDBMNTYgODRMNTYgNTBMMjggMzQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZCRkJGQiIgc3Ryb2tlLXdpZHRoPSIyIj48L3BhdGg+Cjwvc3ZnPg=="); +} \ No newline at end of file diff --git a/src/client/css/02-login.css b/src/client/css/02-login.css new file mode 100644 index 000000000..9d7ec9225 --- /dev/null +++ b/src/client/css/02-login.css @@ -0,0 +1,101 @@ +.form-signin +{ + padding: 15px; + margin: 0 auto; +} + +.form-signin .form-signin-heading, .form-signin .checkbox +{ + margin-bottom: 10px; +} + +.form-signin .checkbox +{ + font-weight: normal; +} + +.form-signin .form-control +{ + position: relative; + font-size: 16px; + height: auto; + padding: 10px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.form-signin .form-control:focus +{ + z-index: 2; +} +.form-signin input[type="text"] +{ + margin-bottom: -1px; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} +.form-signin input[type="password"] +{ + /* margin-bottom: 10px; */ + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.account-wall +{ + border: 1px solid #DDD; + margin-top: 20px; + padding: 20px; + padding-bottom: 40px; + background-color: #f7f7f7; + -moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); + box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); +} +.account-wall h1 +{ + color: #555; + margin-bottom: 30px; + font-weight: 400; + display: block; + text-align: center; +} +.account-wall p +{ + text-align: center; + margin: 10px 10px; + margin-top: 30px; + font-size: 1.3em; +} +.account-wall .form-inputs +{ + margin-bottom: 10px; +} +.account-wall hr { + border-color: #c5c5c5; +} + +.header-img +{ + width: 96px; + height: 96px; + margin: 0 auto 10px; + display: block; + -moz-border-radius: 50%; + -webkit-border-radius: 50%; + border-radius: 50%; +} + +.link +{ + margin-top: 10px; +} + +.btn-primary.totp +{ + background-color: rgb(102, 135, 162); +} + +.btn-primary.u2f +{ + background-color: rgb(83, 149, 204); +} \ No newline at end of file diff --git a/src/client/css/03-errors.css b/src/client/css/03-errors.css new file mode 100644 index 000000000..e9f97f33b --- /dev/null +++ b/src/client/css/03-errors.css @@ -0,0 +1,12 @@ + +.error-401 .header-img { + border-radius: 0%; +} + +.error-403 .header-img { + border-radius: 0%; +} + +.error-404 .header-img { + border-radius: 0%; +} \ No newline at end of file diff --git a/src/client/css/03-password-reset-form.css b/src/client/css/03-password-reset-form.css new file mode 100644 index 000000000..34066bc24 --- /dev/null +++ b/src/client/css/03-password-reset-form.css @@ -0,0 +1,4 @@ + +.password-reset-form .header-img { + border-radius: 0%; +} diff --git a/src/client/css/03-password-reset-request.css b/src/client/css/03-password-reset-request.css new file mode 100644 index 000000000..1a2ad4df8 --- /dev/null +++ b/src/client/css/03-password-reset-request.css @@ -0,0 +1,4 @@ + +.password-reset-request .header-img { + border-radius: 0%; +} diff --git a/src/client/css/03-totp-register.css b/src/client/css/03-totp-register.css new file mode 100644 index 000000000..b51fa6dba --- /dev/null +++ b/src/client/css/03-totp-register.css @@ -0,0 +1,12 @@ +.totp-register #secret { + background-color: white; + font-size: 0.9em; + font-weight: bold; + padding: 5px; + border: 1px solid #c7c7c7; + word-wrap: break-word; +} + +.totp-register #qrcode img { + margin: 20px auto; +} \ No newline at end of file diff --git a/src/client/css/03-u2f-register.css b/src/client/css/03-u2f-register.css new file mode 100644 index 000000000..e54cddf89 --- /dev/null +++ b/src/client/css/03-u2f-register.css @@ -0,0 +1,5 @@ + +.u2f-register img { + display: block; + margin: 20px auto; +} \ No newline at end of file diff --git a/src/client/firstfactor/FirstFactorValidator.ts b/src/client/firstfactor/FirstFactorValidator.ts new file mode 100644 index 000000000..369cd535c --- /dev/null +++ b/src/client/firstfactor/FirstFactorValidator.ts @@ -0,0 +1,20 @@ + +import BluebirdPromise = require("bluebird"); +import Endpoints = require("../../server/endpoints"); + +export function validate(username: string, password: string, $: JQueryStatic): BluebirdPromise < void> { + return new BluebirdPromise(function (resolve, reject) { + $.post(Endpoints.FIRST_FACTOR_POST, { + username: username, + password: password, + }) + .done(function () { + resolve(); + }) + .fail(function (xhr: JQueryXHR, textStatus: string) { + if (xhr.status == 401) + reject(new Error("Authetication failed. Please check your credentials")); + reject(new Error(textStatus)); + }); + }); +} diff --git a/src/client/firstfactor/UISelectors.ts b/src/client/firstfactor/UISelectors.ts new file mode 100644 index 000000000..25dc81ff0 --- /dev/null +++ b/src/client/firstfactor/UISelectors.ts @@ -0,0 +1,3 @@ + +export const USERNAME_FIELD_ID = "#username"; +export const PASSWORD_FIELD_ID = "#password"; \ No newline at end of file diff --git a/src/client/firstfactor/index.ts b/src/client/firstfactor/index.ts new file mode 100644 index 000000000..fea6b4e31 --- /dev/null +++ b/src/client/firstfactor/index.ts @@ -0,0 +1,39 @@ +import FirstFactorValidator = require("./FirstFactorValidator"); +import JSLogger = require("js-logger"); +import UISelectors = require("./UISelectors"); + +import Endpoints = require("../../server/endpoints"); + +export default function (window: Window, $: JQueryStatic, firstFactorValidator: typeof FirstFactorValidator, jslogger: typeof JSLogger) { + function onFormSubmitted() { + const username: string = $(UISelectors.USERNAME_FIELD_ID).val(); + const password: string = $(UISelectors.PASSWORD_FIELD_ID).val(); + jslogger.debug("Form submitted"); + firstFactorValidator.validate(username, password, $) + .then(onFirstFactorSuccess, onFirstFactorFailure); + return false; + } + + function onFirstFactorSuccess() { + jslogger.debug("First factor validated."); + $(UISelectors.USERNAME_FIELD_ID).val(""); + $(UISelectors.PASSWORD_FIELD_ID).val(""); + + // Redirect to second factor + window.location.href = Endpoints.SECOND_FACTOR_GET; + } + + function onFirstFactorFailure(err: Error) { + jslogger.debug("First factor failed."); + + $(UISelectors.PASSWORD_FIELD_ID).val(""); + $.notify("Error during authentication: " + err.message, "error"); + } + + + $(window.document).ready(function () { + jslogger.info("Enter first factor"); + $("form").on("submit", onFormSubmitted); + }); +} + diff --git a/src/client/img/icon.png b/src/client/img/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..145a2751314d35f0073d62f012a72429350eb9cd GIT binary patch literal 814 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy%*9TgAsieWw;%dH0CG7CJR*x3 z7#M+i7`Yh5mh3qV6qGD+jVKAuPb(=;EJ|f4FE7{2%*!rLPAo{(%P&fw{mw=TsOV>a zPl)UP|Nj~O{|{aDKlJMVg#GtJk3A0E|H|{k+ko@$Ll1opI`i7+*qfjeZ~afb4LJJ2 z@7Tw{lb?c*z41Ts*7wv~|6}h0PQCX#`N9AA$AIJS{Z70OJozE;*xSI9?*dP}4>

  • ;!hfhFMQd&k3Y~92MO1Q!lK+q6Wre@X_RePU~(~{DJW(C z|HBm%8+iiP{av4S_McI9m{4X;(9Hn~%NZABZ*7 zJ++YLejR(6U+(X|IN`+~XP2{WY3}Vmy(NV)qkTc)#{Ew`gbx;ctv}Guv|fba{+p|k zE%~e;?yS^ic$K!E=k%l{Uzf1V(_Z_P@f4d#V)P`lTWgnmh{^oQ+pvhSy+Fm2|AJT4 zTxVvZ0yR%=(~QWuJ&c?cMQw*(sqhrcOZ0Y&_c0{QY{po!<}s{QmBKY!P402Ywmfc@--~cK+j^ Y5ZCx_;|lR@z$jzzboFyt=akR{0F=<7rT_o{ literal 0 HcmV?d00001 diff --git a/src/client/img/mail.png b/src/client/img/mail.png new file mode 100644 index 0000000000000000000000000000000000000000..834bfce9107a94be10da4c011fb78e37a192888f GIT binary patch literal 3545 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 G$o~PE>i-e| literal 0 HcmV?d00001 diff --git a/src/public_html/img/pendrive.png b/src/client/img/pendrive.png similarity index 100% rename from src/public_html/img/pendrive.png rename to src/client/img/pendrive.png diff --git a/src/client/img/success.png b/src/client/img/success.png new file mode 100644 index 0000000000000000000000000000000000000000..ee9d6841bbb7208220a831c1c42917eb8facffa5 GIT binary patch literal 3147 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 literal 0 HcmV?d00001 diff --git a/src/client/img/user.png b/src/client/img/user.png new file mode 100644 index 0000000000000000000000000000000000000000..00941399d34f0f2d85d323f61daa67c2c00c494d GIT binary patch literal 2933 zcmV-*3ySoKP)>+Y_1buC zheF*1yf*GR)m>@otnGcw%sqGZe)vfU+q>t^|F^R<_ujcTYIJlo%4jV9WAaR39I1IcZ1yCpS zIY0`~LOAsCXaEv{=cNF_2S1lxgn0!F-rv{6d0@U2VDjK!2d)?(^zpb(O#~{z<1L%h zz`>Ihfp_q;fNbDV1ck2z&vq|HoB+`1Xdnp~jL_hfe8nynpNI$nprH$ZHW~ak_zd`^ zVFiH3O##fnhzxsNhFJ&KlZFxi8uS9|F_R$&2KI2yF@ylnkj21V8EVk<1HThm0M?5q z!wen#x-@Ay#-LU3Q-KpQK-IVm#0Vh(6gr)CbjaY;1?MwNCoKRJI2)*zVQI!~;5m{4 zKw;knT4hL@Q-{ZzlmKWYD~#$v`_Iw@bH~becUG-S%q-brIc!hQ%CTE>@`@1rH~t^~ z41WiI7ypKEd+>ucn}h)DJO4JN0|WtfTGFky((<~-_O71$J^cftp@4sjuYs?HuSraT zYBjD2!VN!$U7T<~AWPy*p3UYD9S&zzZK#1y2EI1F2fi1fQN^iT1YmRXE_461$D3Uj zt`?WMJ^jO41wR&iFMLmYZvqmW;3a^GcjNIpyUo>oPp{y|g71wr5Rf26R{^k#lJlU> z;E|J`7V-*yELaPHq3y-G2*4(4_H+EMA1JSBYNZms53C7m<0t@*x4fXK060v^Myhx< z^3f-GCAX-B?*nV&Ji7@;T&HL$KpM}}+_t|Sw^I$@2iC~>tH7*@061HPO<3U4u(#Ll zsCIk$ywnB28nIT+BZv!B6#(a}u_;U3%(j=$=Do z#TDQ{E@c6dl>%I)Smctph;09_P|%>BeJ>}(((xfktJ z7s0;&KLKzvB#H$t|I_wH(ZlzFZ6PYb%qX@a7P;cyPd?-@iU8Oaq5{l|V%wsTEAFGy z*NTiH0Jepw04t){-gxASJA0|%rcngIwottjh+=#5NC6^K0BaQ6B~uCznF16>v8__2 z0Ffy`B??e41&CMy8c~2ADL}*u(1`*(ASuA*0;d!}b9zyLhw=;%u>#yj0luVI+|iR) z3XLLwHTR!XJA#H#02jsLHvDC;VR{11e>-6#D!>C2powB}vGJxJqX>XY&=VEFjRI6q zowRWAT5*}^;a|S)yiN7^;*#@GRF-=mZ=;7B#TC_}hF@9V(it0Xb`w6nHnOvU6tjZv zQCu5%GVtsb8)v#heEXR*D8MP|`{X294%5Wjv`g+KzVLjis&La`#O6b=`CF3!uv2g`gE;3*M` z#;x4QFBQM@Sb;#{W9HL+)51GMDB4+3-$eJ~&*NPZXX+P7fL8JkhK%LUCb~0)Po2vz z6DWM-oDh$Ojp1QkbGw`1CJ`gcS6NyF6W{}-0KXTd@WgEg$(;rE9XVelSop{vlmbkn zIxoQA5N1tN8<~EArgql=;phJ*x&WLxc7cc+qBP*Wtyu+B1lX9gzgWOezti6eA)5ti zfVZT)u7UgBK&6$ng5CG~*y2xspNrIpH&+Myu$D9>mOOMdV9Tq;UhoxC%|Js zpGXZc?K;kFCGqsE9J`?5w@)Hw&vy)6A®tx`|Cc!K{R~B2jc1N|ng#YPY z01+^g*}>)+v;a(kg*ZQ7)0sYS5YJw62)S4!2N7^)@}f|(3`qbcL4r0*!k9bJT`jgp}PPI5fXrZ%isracGF5;IdBtzN$?_Y z46k-0FnAp9&IZ2EGXt&yC`6b8>;=AxfZ)FdvVb2@IQAq2P>3)S*uhSVMm*K;1fHjK z%t;BL5Mc(81b7S(JhovA@HE9^O;P}b2rkn{6#(u#g!>KLW&ICB?B8ANXZSm~9Q{dlI$z1o fINvtfu|oa}IMcn@FFyTa00000NkvXXu0mjfV5=n> literal 0 HcmV?d00001 diff --git a/src/client/img/warning.png b/src/client/img/warning.png new file mode 100644 index 0000000000000000000000000000000000000000..c6acd953b142a8e43e293c353a60e6313a8d39eb GIT binary patch literal 4038 zcmZ8kc{CK>7k@Km%nZhmB)g(al4J@g%*Ybi@{R0UV<~HPGGh#)vXiopt-hj2MImcp zY$;2&EGf;GWSK0JWq#AYzjw}k@4R>Jx$oW2UEdvZQv)=Q7!LpdZFo`Fg0oWo5|W!U zw`;lda28mgj^Py~XG9{237kF3|DsJG09+w|2|^mC_HqtIgY>L}{_%4U3Uvu^1EHaz zO4oh80%A^>WZHKCL(h;uQN z$(4fz2^gkydwnSstYvq?z;jH?4dHpjOIsi?F~5mv-=X~B1+IfT=c@TV0XW+s38jp3 zIm`8ol6U1Y3*FZ4G|uCdEr5XCymsumB+=8f9+kg$8eP;^u9f!rnogW5jie4Qx&UZq%;ioEF43zF#7yy~zM-eI zO?nd_=$d}*1Zdj89sj6I?h<22KC>b!%CwF;CBL2iff7f&(SHL%Y3gOm9%rm>z`BQ< zags$B!oM7@imA=`0F#zKMyK{>K{K;0WBK$OE(4l07$MAiJTzu}?T}$Eu6~)JEgc2l zf=SEgU@ShC_l!E7E{fG`pTiY)xdsO#!EcwQcM9VpAIi>eEYTaUlkwl)(ssK%=%={( zyLCh_G^+Jtx;(pcl>ozzx!j?_lkK#SDH7&caoLxQxBIRw{7*%VTL*&Jr`VX~UcGAs z20}q%Os|>1VApPeKz|^ki=-gYS z9Y@17L~Ki*qd&(F9GL7rg&nPk`xaYE{WG~yqtOG|AGd;=yeICQ3?P8Wl(?P zFZ7V8e_`3g%Z~$IWK8`8B+2Uaw1D@FC+7 z-$J8b@&2r&R`X87_vAo=1n`SqVsgn1)n)~(GkKZ^+y9#v%9lDe!PdCl<}d?4l&Z)= zLn&Pk*;lOwuF)@Xq&>Lrv$yIxxEBk9P*f6q;C&Gxctr z>UH@f5#<~SKVIH7{8TvYp}h5)?t8@rj)0$ z=(|UP)`a4gb}Nfh$}-Aot~qRv-2NG&Pw?!YJ_L{-cI0;gx6C17f#<(JU-shN4s}Xm zsr`bk<+y&nAzmdTfd%Fm{E6d?o6#b_8AlL0OZR6X0~$FN0^=Va&iV>~OZ62FfY!fr z+G@7W*EE5ULNUF6+k=dc=OuP(y~w^QFmG}IU~z1DYm-#g5&k$r%25&3Dt&RF(P6Qy z!hED53NQMhpoEN40!T@U!U(PZ!f|Zebkh%KFy7&Ljg0S%@80Y9{`L*_p0Gq(-Xa_n zWwVhZa3tlb{&BvDJc*;YmC03wmleLmlfhCIJloH!f053eR9|}uD3}(DuE;rORKDF~ z8OF`gF;dSjm&vc;j;H-8dLiCxQey{Hz=`iQ)BzERRTZji7F7reYFTV&VRD5#mM;P( zOnzY7S=<_1pZRu#%rT>*GCwrZtj>RI@Q?16O+_|h&X4@_qT z{7RG|7N$Y~RTfv^0XLX@uwvGl4+Up|W7er#Ssc0MwmZo`pUyz>8<(%}N)*8iLT<*$ zO7nn-T=u0({;o{JGM*TzB-T(t)5aqsCE#OXne$tyGP|&oKl$KN3NP@5DkD^g0|1p@ z4(AS`US%VnTz+8Z^%`KGKzv~a`sOjR&m!IZiOqmEvGch4*@fGRIo{5gpM0we7|#}f z^CTPH)gw)SBkBRwS>h6OBTukHNYUs@{TU=wUvp<|w38svkaGfXF17 zWRj^r4BA@-?+x!da`Cm2p9d}nF!q(p6NOeC5{5={Kr7n#qM8v0M{@o_vGlW5W}(rbH_w)a22X{DsMW>q`VKlT zXk-Y086nM7%Po?soy))lmj}z#H&QEp9}4F_KI_@4j-*^)ygEts*RkiKn62eo$G03- zOi`)M-~=((Ir@vJgpNwW2eeFB3y_uX|F=QcfJs>&llMuD- z;!BoDIu2Ioq}!HTPb2l-x0v3yVVB%8h?y?A85*|%wy3iFW1CtV+P>P+YD!=Qp&6nh zpIe}j|SXo%pJ_yAdzPv0Hd-05QBz!d!eVX!e_ z-js2+CCJYh%8^@Wg9~fkF6Qu>bT&Ro^dIN&*5>tgan0OP%w$V%fA-%-^*%>}*H>=U z!jTaxwGN=$~V1I2jb9T`^h|OQK?o+N?D|4 z%z=m!488|@gJ9jym%RV<;yy5CvE=1Zp4VzOXMit87iM+GU@MG@F&tH|m(TSZJC+>> zVV7QsOjvSjJ`@G(vuKq+ovB&m7i#;On+EK^sDZq)O`%>Aot~S$T6tk$6miZ z37nON_ttN@aBChO!q9G=2`91_S^1OMbP-+Qzb~7M7k%ZmY*tzbI>2DcboU%unRGe_ z^g6>eZQIZhx<$yz=BE-TqN+ya*KDr>e5)V=r(T0o09y}kAUswax2W8N6l5)zy{FXT zBCBMbJKg8c8{RJV4?lJfI=NJj3;36BD84{BOqIIaih|ifAHPMW@E@HB`s-w(SAV(o z8BYmb{tLZdgx&WRQ_Flp++DL9J%{!bU5L_O@OV^m-WN_@GfqN+t#6a>ZJ)fIEc54H z_Ahf?jW>B!W)Hfw>LIikDG8h3(g;8IH78vtMT}9pZk9gqaafF_b|zx9h71z{H7U<{ z+UCstV>w|G^N5qtm{_m!4V!Cc_#$GTw0CkB!PSFBD(8Zm(v9+Jmfk6V{CR2+G|9x>_Jus|!Ap5u#`Lp(Fwz*3I*OOGUT(q-M!Afy{tE@U zJ2F=Cu$IgfDXw+ZF2+tPekHA`bg@1(%I2vkB|8wwf zRU;Ls;@*5M8xL*Febm=t`gFhF?0_;=b~>tWNj@wC1yr}RMl6=> zX6-I}2_osksR_lEmhYexY@erD5yZS_@mPt{mq!Rag+PIruP1DNV3J?1b5O|b#ua+1 zKK*Qlxf{Zjnf!_bXYF=)Kg>LK0X;cl;x&6Us;m43gbnAydy`^s!rJ|y34o}Mlo51w#*DkxKu*ZSO^rjzY4`S%iTMPk?%#D9Hq0u zXO7WS9c7da`CWXmSEK4H`Zo6u$TGFji_X0wF zYHis}o|pf`w?RCV<8AhzFDtAF=BVX(@&7)kpvZHP-ER?mQZc1eo%81ghI*#D&vcxr F{{xrIJYfI; literal 0 HcmV?d00001 diff --git a/src/client/index.ts b/src/client/index.ts new file mode 100644 index 000000000..8d7e37ce5 --- /dev/null +++ b/src/client/index.ts @@ -0,0 +1,38 @@ + +import FirstFactorValidator = require("./firstfactor/FirstFactorValidator"); + +import FirstFactor from "./firstfactor/index"; +import SecondFactor from "./secondfactor/index"; +import TOTPRegister from "./totp-register/totp-register"; +import U2fRegister from "./u2f-register/u2f-register"; +import ResetPasswordRequest from "./reset-password/reset-password-request"; +import ResetPasswordForm from "./reset-password/reset-password-form"; +import jslogger = require("js-logger"); +import jQuery = require("jquery"); +import u2fApi = require("u2f-api"); + +jslogger.useDefaults(); +jslogger.setLevel(jslogger.INFO); + +require("notifyjs-browser")(jQuery); + +export = { + firstfactor: function () { + FirstFactor(window, jQuery, FirstFactorValidator, jslogger); + }, + secondfactor: function () { + SecondFactor(window, jQuery, u2fApi); + }, + register_totp: function() { + TOTPRegister(window, jQuery); + }, + register_u2f: function () { + U2fRegister(window, jQuery); + }, + reset_password_request: function () { + ResetPasswordRequest(window, jQuery); + }, + reset_password_form: function () { + ResetPasswordForm(window, jQuery); + } +}; \ No newline at end of file diff --git a/src/client/reset-password/constants.ts b/src/client/reset-password/constants.ts new file mode 100644 index 000000000..d48d4e67d --- /dev/null +++ b/src/client/reset-password/constants.ts @@ -0,0 +1,2 @@ + +export const FORM_SELECTOR = ".form-signin"; \ No newline at end of file diff --git a/src/client/reset-password/reset-password-form.ts b/src/client/reset-password/reset-password-form.ts new file mode 100644 index 000000000..dfd48e45d --- /dev/null +++ b/src/client/reset-password/reset-password-form.ts @@ -0,0 +1,49 @@ +import BluebirdPromise = require("bluebird"); + +import Endpoints = require("../../server/endpoints"); +import Constants = require("./constants"); + +export default function (window: Window, $: JQueryStatic) { + function modifyPassword(newPassword: string) { + return new BluebirdPromise(function (resolve, reject) { + $.post(Endpoints.RESET_PASSWORD_FORM_POST, { + password: newPassword, + }) + .done(function (data) { + resolve(data); + }) + .fail(function (xhr, status) { + reject(status); + }); + }); + } + + function onFormSubmitted() { + const password1 = $("#password1").val(); + const password2 = $("#password2").val(); + + if (!password1 || !password2) { + $.notify("You must enter your new password twice.", "warn"); + return false; + } + + if (password1 != password2) { + $.notify("The passwords are different", "warn"); + return false; + } + + modifyPassword(password1) + .then(function () { + $.notify("Your password has been changed. Please login again", "success"); + window.location.href = Endpoints.FIRST_FACTOR_GET; + }) + .error(function () { + $.notify("An error occurred during password change.", "warn"); + }); + return false; + } + + $(document).ready(function () { + $(Constants.FORM_SELECTOR).on("submit", onFormSubmitted); + }); +} diff --git a/src/client/reset-password/reset-password-request.ts b/src/client/reset-password/reset-password-request.ts new file mode 100644 index 000000000..e390fbc5e --- /dev/null +++ b/src/client/reset-password/reset-password-request.ts @@ -0,0 +1,49 @@ + +import BluebirdPromise = require("bluebird"); + +import Endpoints = require("../../server/endpoints"); +import Constants = require("./constants"); +import jslogger = require("js-logger"); + +export default function(window: Window, $: JQueryStatic) { + function requestPasswordReset(username: string) { + return new BluebirdPromise(function (resolve, reject) { + $.get(Endpoints.RESET_PASSWORD_IDENTITY_START_GET, { + userid: username, + }) + .done(function () { + resolve(); + }) + .fail(function (xhr: JQueryXHR, textStatus: string) { + reject(new Error(textStatus)); + }); + }); + } + + function onFormSubmitted() { + const username = $("#username").val(); + + if (!username) { + $.notify("You must provide your username to reset your password.", "warn"); + return; + } + + requestPasswordReset(username) + .then(function () { + $.notify("An email has been sent. Click on the link to change your password", "success"); + setTimeout(function () { + window.location.replace(Endpoints.FIRST_FACTOR_GET); + }, 1000); + }) + .error(function () { + $.notify("Are you sure this is your username?", "warn"); + }); + return false; + } + + $(document).ready(function () { + jslogger.debug("Reset password request form setup"); + $(Constants.FORM_SELECTOR).on("submit", onFormSubmitted); + }); +} + diff --git a/src/client/secondfactor/TOTPValidator.ts b/src/client/secondfactor/TOTPValidator.ts new file mode 100644 index 000000000..7538f7f1e --- /dev/null +++ b/src/client/secondfactor/TOTPValidator.ts @@ -0,0 +1,22 @@ + +import BluebirdPromise = require("bluebird"); +import Endpoints = require("../../server/endpoints"); + +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 (data: any) { + resolve(data); + }) + .fail(function (xhr: JQueryXHR, textStatus: string) { + reject(new Error(textStatus)); + }); + }); +} \ No newline at end of file diff --git a/src/client/secondfactor/U2FValidator.ts b/src/client/secondfactor/U2FValidator.ts new file mode 100644 index 000000000..fb5da8e17 --- /dev/null +++ b/src/client/secondfactor/U2FValidator.ts @@ -0,0 +1,61 @@ + +import U2fApi = require("u2f-api"); +import U2f = require("u2f"); +import BluebirdPromise = require("bluebird"); +import { SignMessage } from "../../server/lib/routes/secondfactor/u2f/sign_request/SignMessage"; +import Endpoints = require("../../server/endpoints"); + +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 (data) { + resolve(data); + }) + .fail(function (xhr: JQueryXHR, textStatus: string) { + reject(new Error(textStatus)); + }); + }); +} + +function startU2fAuthentication($: JQueryStatic, u2fApi: typeof U2fApi): BluebirdPromise { + return new BluebirdPromise(function (resolve, reject) { + $.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, {}, undefined, "json") + .done(function (signResponse: SignMessage) { + $.notify("Please touch the token", "info"); + + const signRequest: U2fApi.SignRequest = { + appId: signResponse.request.appId, + challenge: signResponse.request.challenge, + keyHandle: signResponse.keyHandle, // linked to the client session cookie + version: "U2F_V2" + }; + + u2fApi.sign([signRequest], 60) + .then(function (signResponse: U2fApi.SignResponse) { + finishU2fAuthentication(signResponse, $) + .then(function (data) { + resolve(data); + }, function (err) { + $.notify("Error when finish U2F transaction", "error"); + reject(err); + }); + }) + .catch(function (err: Error) { + reject(err); + }); + }) + .fail(function (xhr: JQueryXHR, textStatus: string) { + reject(new Error(textStatus)); + }); + }); +} + + +export function validate($: JQueryStatic, u2fApi: typeof U2fApi): BluebirdPromise { + return startU2fAuthentication($, u2fApi); +} diff --git a/src/client/secondfactor/constants.ts b/src/client/secondfactor/constants.ts new file mode 100644 index 000000000..eb8b154b3 --- /dev/null +++ b/src/client/secondfactor/constants.ts @@ -0,0 +1,5 @@ + +export const TOTP_FORM_SELECTOR = ".form-signin.totp"; +export const TOTP_TOKEN_SELECTOR = ".form-signin #token"; + +export const U2F_FORM_SELECTOR = ".form-signin.u2f"; \ No newline at end of file diff --git a/src/client/secondfactor/index.ts b/src/client/secondfactor/index.ts new file mode 100644 index 000000000..1129bc2ae --- /dev/null +++ b/src/client/secondfactor/index.ts @@ -0,0 +1,57 @@ + +import U2fApi = require("u2f-api"); +import jslogger = require("js-logger"); + +import TOTPValidator = require("./TOTPValidator"); +import U2FValidator = require("./U2FValidator"); + +import Endpoints = require("../../server/endpoints"); + +import Constants = require("./constants"); + + +export default function (window: Window, $: JQueryStatic, u2fApi: typeof U2fApi) { + function onAuthenticationSuccess(data: any) { + window.location.href = data.redirection_url; + } + + + function onSecondFactorTotpSuccess(data: any) { + onAuthenticationSuccess(data); + } + + function onSecondFactorTotpFailure(err: Error) { + $.notify("Error while validating TOTP token. Cause: " + err.message, "error"); + } + + function onU2fAuthenticationSuccess(data: any) { + onAuthenticationSuccess(data); + } + + function onU2fAuthenticationFailure() { + $.notify("Problem with U2F authentication. Did you register before authenticating?", "warn"); + } + + + function onTOTPFormSubmitted(): boolean { + const token = $(Constants.TOTP_TOKEN_SELECTOR).val(); + jslogger.debug("TOTP token is %s", token); + + TOTPValidator.validate(token, $) + .then(onSecondFactorTotpSuccess) + .catch(onSecondFactorTotpFailure); + return false; + } + + function onU2FFormSubmitted(): boolean { + jslogger.debug("Start U2F authentication"); + U2FValidator.validate($, U2fApi) + .then(onU2fAuthenticationSuccess, onU2fAuthenticationFailure); + return false; + } + + $(window.document).ready(function () { + $(Constants.TOTP_FORM_SELECTOR).on("submit", onTOTPFormSubmitted); + $(Constants.U2F_FORM_SELECTOR).on("submit", onU2FFormSubmitted); + }); +} \ No newline at end of file diff --git a/src/public_html/js/qrcode.min.js b/src/client/thirdparties/qrcode.min.js similarity index 100% rename from src/public_html/js/qrcode.min.js rename to src/client/thirdparties/qrcode.min.js diff --git a/src/client/totp-register/totp-register.ts b/src/client/totp-register/totp-register.ts new file mode 100644 index 000000000..6a9aa7ee0 --- /dev/null +++ b/src/client/totp-register/totp-register.ts @@ -0,0 +1,11 @@ + +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/src/client/totp-register/ui-selector.ts b/src/client/totp-register/ui-selector.ts new file mode 100644 index 000000000..9d43fabea --- /dev/null +++ b/src/client/totp-register/ui-selector.ts @@ -0,0 +1,2 @@ + +export const QRCODE_ID_SELECTOR = "#qrcode"; \ No newline at end of file diff --git a/src/client/u2f-register/u2f-register.ts b/src/client/u2f-register/u2f-register.ts new file mode 100644 index 000000000..d584ab03b --- /dev/null +++ b/src/client/u2f-register/u2f-register.ts @@ -0,0 +1,53 @@ + +import BluebirdPromise = require("bluebird"); +import U2f = require("u2f"); +import u2fApi = require("u2f-api"); + +import Endpoints = require("../../server/endpoints"); +import jslogger = require("js-logger"); + +export default function(window: Window, $: JQueryStatic) { + + function checkRegistration(regResponse: u2fApi.RegisterResponse, fn: (err: Error) => void) { + const registrationData: U2f.RegistrationData = regResponse; + + jslogger.debug("registrationResponse = %s", JSON.stringify(registrationData)); + + $.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, registrationData, undefined, "json") + .done(function (data) { + document.location.href = data.redirection_url; + }) + .fail(function (xhr, status) { + $.notify("Error when finish U2F transaction" + status); + }); + } + + function requestRegistration(fn: (err: Error) => void) { + $.get(Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, {}, undefined, "json") + .done(function (registrationRequest: U2f.Request) { + jslogger.debug("registrationRequest = %s", JSON.stringify(registrationRequest)); + + const registerRequest: u2fApi.RegisterRequest = registrationRequest; + u2fApi.register([registerRequest], [], 120) + .then(function (res: u2fApi.RegisterResponse) { + checkRegistration(res, fn); + }) + .catch(function (err: Error) { + fn(err); + }); + }); + } + + function onRegisterFailure(err: Error) { + $.notify("Problem authenticating with U2F.", "error"); + } + + $(document).ready(function () { + requestRegistration(function (err: Error) { + if (err) { + onRegisterFailure(err); + return; + } + }); + }); +} diff --git a/src/lib/IdentityValidator.ts b/src/lib/IdentityValidator.ts deleted file mode 100644 index 94f19458e..000000000 --- a/src/lib/IdentityValidator.ts +++ /dev/null @@ -1,156 +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 UserDataStore from "./UserDataStore"; -import { ILogger } from "../types/ILogger"; -import express = require("express"); - -import Identity = require("../types/Identity"); -import { IdentityValidationRequestContent } from "./UserDataStore"; - -const filePath = __dirname + "/../resources/email-template.ejs"; -const email_template = fs.readFileSync(filePath, "utf8"); - - -// 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; - templateName(): string; - preValidation(req: express.Request): BluebirdPromise; - mailSubject(): string; -} - -export class IdentityValidator { - private userDataStore: UserDataStore; - private logger: ILogger; - - constructor(userDataStore: UserDataStore, logger: ILogger) { - this.userDataStore = userDataStore; - this.logger = logger; - } - - - static setup(app: express.Application, endpoint: string, handler: IdentityValidable, userDataStore: UserDataStore, logger: ILogger) { - const identityValidator = new IdentityValidator(userDataStore, logger); - app.get(endpoint, identityValidator.identity_check_get(endpoint, handler)); - app.post(endpoint, identityValidator.identity_check_post(endpoint, handler)); - } - - - private issue_token(userid: string, content: Object): BluebirdPromise { - const five_minutes = 4 * 60 * 1000; - const token = randomstring.generate({ length: 64 }); - const that = this; - - this.logger.debug("identity_check: issue identity token %s for 5 minutes", token); - return this.userDataStore.issue_identity_check_token(userid, token, content, five_minutes) - .then(function () { - return BluebirdPromise.resolve(token); - }); - } - - private consume_token(token: string): BluebirdPromise { - this.logger.debug("identity_check: consume token %s", token); - return this.userDataStore.consume_identity_check_token(token); - } - - private identity_check_get(endpoint: string, handler: IdentityValidable): express.RequestHandler { - const that = this; - return function (req: express.Request, res: express.Response) { - const logger = req.app.get("logger"); - const identity_token = objectPath.get(req, "query.identity_token"); - logger.info("GET identity_check: identity token provided is %s", identity_token); - - if (!identity_token) { - res.status(403); - res.send(); - return; - } - - that.consume_token(identity_token) - .then(function (content: IdentityValidationRequestContent) { - objectPath.set(req, "session.auth_session.identity_check", {}); - req.session.auth_session.identity_check.challenge = handler.challenge(); - req.session.auth_session.identity_check.userid = content.userid; - res.render(handler.templateName()); - }, function (err: Error) { - logger.error("GET identity_check: Error while consuming token %s", err); - throw new exceptions.AccessDeniedError("Access denied"); - }) - .catch(exceptions.AccessDeniedError, function (err: Error) { - logger.error("GET identity_check: Access Denied %s", err); - res.status(403); - res.send(); - }) - .catch(function (err: Error) { - logger.error("GET identity_check: Internal error %s", err); - res.status(500); - res.send(); - }); - }; - } - - - private identity_check_post(endpoint: string, handler: IdentityValidable): express.RequestHandler { - const that = this; - return function (req: express.Request, res: express.Response) { - const logger = req.app.get("logger"); - const notifier = req.app.get("notifier"); - let identity: Identity.Identity; - - handler.preValidation(req) - .then(function (id: Identity.Identity) { - identity = id; - const email_address = objectPath.get(identity, "email"); - const userid = objectPath.get(identity, "userid"); - - if (!(email_address && userid)) { - throw new exceptions.IdentityError("Missing user id or email address"); - } - - return that.issue_token(userid, undefined); - }, function (err: Error) { - throw new exceptions.AccessDeniedError(err.message); - }) - .then(function (token: string) { - const redirect_url = objectPath.get(req, "body.redirect"); - const original_uri = objectPath.get(req, "headers.x-original-uri", ""); - const original_url = util.format("https://%s%s", req.headers.host, original_uri); - let link_url = util.format("%s?identity_token=%s", original_url, token); - if (redirect_url) { - link_url = util.format("%s&redirect=%s", link_url, redirect_url); - } - - logger.info("POST identity_check: notify to %s", identity.userid); - return notifier.notify(identity, handler.mailSubject(), link_url); - }) - .then(function () { - res.status(204); - res.send(); - }) - .catch(exceptions.IdentityError, function (err: Error) { - logger.error("POST identity_check: %s", err); - res.status(400); - res.send(); - }) - .catch(exceptions.AccessDeniedError, function (err: Error) { - logger.error("POST identity_check: %s", err); - res.status(403); - res.send(); - }) - .catch(function (err: Error) { - logger.error("POST identity_check: Error %s", err); - res.status(500); - res.send(); - }); - }; - } -} diff --git a/src/lib/RestApi.ts b/src/lib/RestApi.ts deleted file mode 100644 index 558321b29..000000000 --- a/src/lib/RestApi.ts +++ /dev/null @@ -1,282 +0,0 @@ - -import express = require("express"); -import routes = require("./routes"); -import IdentityValidator = require("./IdentityValidator"); -import UserDataStore from "./UserDataStore"; -import { ILogger } from "../types/ILogger"; - -export default class RestApi { - static setup(app: express.Application, userDataStore: UserDataStore, logger: ILogger): void { - /** - * @apiDefine UserSession - * @apiHeader {String} Cookie Cookie containing "connect.sid", the user - * session token. - */ - - /** - * @apiDefine InternalError - * @apiError (Error 500) {String} error Internal error message. - */ - - /** - * @apiDefine IdentityValidationPost - * - * @apiSuccess (Success 204) status Identity validation has been initiated. - * @apiError (Error 403) AccessDenied Access is denied. - * @apiError (Error 400) InvalidIdentity User identity is invalid. - * @apiError (Error 500) {String} error Internal error message. - * - * @apiDescription This request issue an identity validation token for the user - * bound to the session. It sends a challenge to the email address set in the user - * LDAP entry. The user must visit the sent URL to complete the validation and - * continue the registration process. - */ - - /** - * @apiDefine IdentityValidationGet - * @apiParam {String} identity_token The one-time identity validation token provided in the email. - * @apiSuccess (Success 200) {String} content The content of the page. - * @apiError (Error 403) AccessDenied Access is denied. - * @apiError (Error 500) {String} error Internal error message. - */ - - /** - * @api {get} /login Serve login page - * @apiName Login - * @apiGroup Pages - * @apiVersion 1.0.0 - * - * @apiParam {String} redirect Redirect to this URL when user is authenticated. - * @apiSuccess (Success 200) {String} Content The content of the login page. - * - * @apiDescription Create a user session and serve the login page along with - * a cookie. - */ - app.get("/login", routes.login); - - /** - * @api {get} /logout Server logout page - * @apiName Logout - * @apiGroup Pages - * @apiVersion 1.0.0 - * - * @apiParam {String} redirect Redirect to this URL when user is deauthenticated. - * @apiSuccess (Success 301) redirect Redirect to the URL. - * - * @apiDescription Deauthenticate the user and redirect him. - */ - app.get("/logout", routes.logout); - - /** - * @api {post} /totp-register Request TOTP registration - * @apiName RequestTOTPRegistration - * @apiGroup Registration - * @apiVersion 1.0.0 - * @apiUse UserSession - * @apiUse IdentityValidationPost - */ - /** - * @api {get} /totp-register Serve TOTP registration page - * @apiName ServeTOTPRegistrationPage - * @apiGroup Registration - * @apiVersion 1.0.0 - * @apiUse UserSession - * @apiUse IdentityValidationGet - * - * - * @apiDescription Serves the TOTP registration page that displays the secret. - * The secret is a QRCode and a base32 secret. - */ - IdentityValidator.IdentityValidator.setup(app, "/totp-register", routes.totp_register.icheck_interface, userDataStore, logger); - - - /** - * @api {post} /u2f-register Request U2F registration - * @apiName RequestU2FRegistration - * @apiGroup Registration - * @apiVersion 1.0.0 - * @apiUse UserSession - * @apiUse IdentityValidationPost - */ - /** - * @api {get} /u2f-register Serve U2F registration page - * @apiName ServeU2FRegistrationPage - * @apiGroup Pages - * @apiVersion 1.0.0 - * @apiUse UserSession - * @apiUse IdentityValidationGet - * - * @apiDescription Serves the U2F registration page that asks the user to - * touch the token of the U2F device. - */ - IdentityValidator.IdentityValidator.setup(app, "/u2f-register", routes.u2f_register.icheck_interface, userDataStore, logger); - - /** - * @api {post} /reset-password Request for password reset - * @apiName RequestPasswordReset - * @apiGroup Registration - * @apiVersion 1.0.0 - * @apiUse UserSession - * @apiUse IdentityValidationPost - */ - /** - * @api {get} /reset-password Serve password reset form. - * @apiName ServePasswordResetForm - * @apiGroup Pages - * @apiVersion 1.0.0 - * @apiUse UserSession - * @apiUse IdentityValidationGet - * - * @apiDescription Serves password reset form that allow the user to provide - * the new password. - */ - IdentityValidator.IdentityValidator.setup(app, "/reset-password", routes.reset_password.icheck_interface, userDataStore, logger); - - app.get("/reset-password-form", function (req, res) { res.render("reset-password-form"); }); - - /** - * @api {post} /new-password Set LDAP password - * @apiName SetLDAPPassword - * @apiGroup Registration - * @apiVersion 1.0.0 - * @apiUse UserSession - * - * @apiParam {String} password New password - * - * @apiDescription Set a new password for the user. - */ - app.post("/new-password", routes.reset_password.post); - - /** - * @api {post} /new-totp-secret Generate TOTP secret - * @apiName GenerateTOTPSecret - * @apiGroup Registration - * @apiVersion 1.0.0 - * @apiUse UserSession - * - * @apiSuccess (Success 200) {String} base32 The base32 representation of the secret. - * @apiSuccess (Success 200) {String} ascii The ASCII representation of the secret. - * @apiSuccess (Success 200) {String} qrcode The QRCode of the secret in URI format. - * - * @apiError (Error 403) {String} error No user provided in the session or - * unexpected identity validation challenge in the session. - * @apiError (Error 500) {String} error Internal error message - * - * @apiDescription Generate a new TOTP secret and returns it. - */ - app.post("/new-totp-secret", routes.totp_register.post); - - /** - * @api {get} /verify Verify user authentication - * @apiName VerifyAuthentication - * @apiGroup Verification - * @apiVersion 1.0.0 - * @apiUse UserSession - * - * @apiSuccess (Success 204) status The user is authenticated. - * @apiError (Error 401) status The user is not authenticated. - * - * @apiDescription Verify that the user is authenticated, i.e., the two - * factors have been validated - */ - app.get("/verify", routes.verify); - - /** - * @api {post} /1stfactor LDAP authentication - * @apiName ValidateFirstFactor - * @apiGroup Authentication - * @apiVersion 1.0.0 - * @apiUse UserSession - * @apiUse InternalError - * - * @apiParam {String} username User username. - * @apiParam {String} password User password. - * - * @apiSuccess (Success 204) status 1st factor is validated. - * @apiError (Error 401) {none} error 1st factor is not validated. - * @apiError (Error 403) {none} error Access has been restricted after too - * many authentication attempts - * - * @apiDescription Verify credentials against the LDAP. - */ - app.post("/1stfactor", routes.first_factor); - - /** - * @api {post} /2ndfactor/totp TOTP authentication - * @apiName ValidateTOTPSecondFactor - * @apiGroup Authentication - * @apiVersion 1.0.0 - * @apiUse UserSession - * @apiUse InternalError - * - * @apiParam {String} token TOTP token. - * - * @apiSuccess (Success 204) status TOTP token is valid. - * @apiError (Error 401) {none} error TOTP token is invalid. - * - * @apiDescription Verify TOTP token. The user is authenticated upon success. - */ - app.post("/2ndfactor/totp", routes.second_factor.totp); - - /** - * @api {get} /2ndfactor/u2f/sign_request U2F Start authentication - * @apiName StartU2FAuthentication - * @apiGroup Authentication - * @apiVersion 1.0.0 - * @apiUse UserSession - * @apiUse InternalError - * - * @apiSuccess (Success 200) authentication_request The U2F authentication request. - * @apiError (Error 401) {none} error There is no key registered for user in session. - * - * @apiDescription Initiate an authentication request using a U2F device. - */ - app.get("/2ndfactor/u2f/sign_request", routes.second_factor.u2f.sign_request); - - /** - * @api {post} /2ndfactor/u2f/sign U2F Complete authentication - * @apiName CompleteU2FAuthentication - * @apiGroup Authentication - * @apiVersion 1.0.0 - * @apiUse UserSession - * @apiUse InternalError - * - * @apiSuccess (Success 204) status The U2F authentication succeeded. - * @apiError (Error 403) {none} error No authentication request has been provided. - * - * @apiDescription Complete authentication request of the U2F device. - */ - app.post("/2ndfactor/u2f/sign", routes.second_factor.u2f.sign); - - /** - * @api {get} /2ndfactor/u2f/register_request U2F Start device registration - * @apiName StartU2FRegistration - * @apiGroup Registration - * @apiVersion 1.0.0 - * @apiUse UserSession - * @apiUse InternalError - * - * @apiSuccess (Success 200) authentication_request The U2F registration request. - * @apiError (Error 403) {none} error Unexpected identity validation challenge. - * - * @apiDescription Initiate a U2F device registration request. - */ - app.get("/2ndfactor/u2f/register_request", routes.second_factor.u2f.register_request); - - /** - * @api {post} /2ndfactor/u2f/register U2F Complete device registration - * @apiName CompleteU2FRegistration - * @apiGroup Registration - * @apiVersion 1.0.0 - * @apiUse UserSession - * @apiUse InternalError - * - * @apiSuccess (Success 204) status The U2F registration succeeded. - * @apiError (Error 403) {none} error Unexpected identity validation challenge. - * @apiError (Error 403) {none} error No registration request has been provided. - * - * @apiDescription Complete U2F registration request. - */ - app.post("/2ndfactor/u2f/register", routes.second_factor.u2f.register); - } -} diff --git a/src/lib/Server.ts b/src/lib/Server.ts deleted file mode 100644 index da54cd387..000000000 --- a/src/lib/Server.ts +++ /dev/null @@ -1,94 +0,0 @@ - -import { UserConfiguration } from "./Configuration"; -import { GlobalDependencies } from "../types/Dependencies"; -import AuthenticationRegulator from "./AuthenticationRegulator"; -import UserDataStore from "./UserDataStore"; -import ConfigurationAdapter from "./ConfigurationAdapter"; -import { NotifierFactory } from "./notifiers/NotifierFactory"; -import TOTPValidator from "./TOTPValidator"; -import TOTPGenerator from "./TOTPGenerator"; -import RestApi from "./RestApi"; -import { LdapClient } from "./LdapClient"; -import BluebirdPromise = require("bluebird"); -import { IdentityValidator } from "./IdentityValidator"; - -import * as Express from "express"; -import * as BodyParser from "body-parser"; -import * as Path from "path"; -import * as http from "http"; - -import AccessController from "./access_control/AccessController"; - -export default class Server { - private httpServer: http.Server; - - start(yaml_configuration: UserConfiguration, deps: GlobalDependencies): BluebirdPromise { - const config = ConfigurationAdapter.adapt(yaml_configuration); - - const view_directory = Path.resolve(__dirname, "../views"); - const public_html_directory = Path.resolve(__dirname, "../public_html"); - const datastore_options = { - directory: config.store_directory, - inMemory: config.store_in_memory - }; - - const app = Express(); - app.use(Express.static(public_html_directory)); - app.use(BodyParser.urlencoded({ extended: false })); - app.use(BodyParser.json()); - app.set("trust proxy", 1); // trust first proxy - - app.use(deps.session({ - secret: config.session.secret, - resave: false, - saveUninitialized: true, - cookie: { - secure: false, - maxAge: config.session.expiration, - domain: config.session.domain - }, - })); - - app.set("views", view_directory); - app.set("view engine", "ejs"); - - // by default the level of logs is info - deps.winston.level = config.logs_level || "info"; - - const five_minutes = 5 * 60; - const userDataStore = new UserDataStore(datastore_options, deps.nedb); - const regulator = new AuthenticationRegulator(userDataStore, five_minutes); - const notifier = NotifierFactory.build(config.notifier, deps.nodemailer); - const ldap = new LdapClient(config.ldap, deps.ldapjs, deps.winston); - const accessController = new AccessController(config.access_control, deps.winston); - const totpValidator = new TOTPValidator(deps.speakeasy); - const totpGenerator = new TOTPGenerator(deps.speakeasy); - const identityValidator = new IdentityValidator(userDataStore, deps.winston); - - app.set("logger", deps.winston); - app.set("ldap", ldap); - app.set("totp validator", totpValidator); - app.set("totp generator", totpGenerator); - app.set("u2f", deps.u2f); - app.set("user data store", userDataStore); - app.set("notifier", notifier); - app.set("authentication regulator", regulator); - app.set("config", config); - app.set("access controller", accessController); - app.set("identity validator", identityValidator); - - RestApi.setup(app, userDataStore, deps.winston); - - return new BluebirdPromise((resolve, reject) => { - this.httpServer = app.listen(config.port, function (err: string) { - console.log("Listening on %d...", config.port); - resolve(); - }); - }); - } - - stop() { - this.httpServer.close(); - } -} - diff --git a/src/lib/routes.ts b/src/lib/routes.ts deleted file mode 100644 index 4c2d680d1..000000000 --- a/src/lib/routes.ts +++ /dev/null @@ -1,41 +0,0 @@ - -import FirstFactor = require("./routes/FirstFactor"); -import SecondFactorRoutes = require("./routes/SecondFactorRoutes"); -import PasswordReset = require("./routes/PasswordReset"); -import AuthenticationValidator = require("./routes/AuthenticationValidator"); -import U2FRegistration = require("./routes/U2FRegistration"); -import TOTPRegistration = require("./routes/TOTPRegistration"); -import objectPath = require("object-path"); - -import express = require("express"); - -export = { - login: serveLogin, - logout: serveLogout, - verify: AuthenticationValidator, - first_factor: FirstFactor, - second_factor: SecondFactorRoutes, - reset_password: PasswordReset, - u2f_register: U2FRegistration, - totp_register: TOTPRegistration, -}; - -function serveLogin(req: express.Request, res: express.Response) { - if (!(objectPath.has(req, "session.auth_session"))) { - req.session.auth_session = {}; - req.session.auth_session.first_factor = false; - req.session.auth_session.second_factor = false; - } - res.render("login"); -} - -function serveLogout(req: express.Request, res: express.Response) { - const redirect_param = req.query.redirect; - const redirect_url = redirect_param || "/"; - req.session.auth_session = { - first_factor: false, - second_factor: false - }; - res.redirect(redirect_url); -} - diff --git a/src/lib/routes/AuthenticationValidator.ts b/src/lib/routes/AuthenticationValidator.ts deleted file mode 100644 index d5ae1178d..000000000 --- a/src/lib/routes/AuthenticationValidator.ts +++ /dev/null @@ -1,53 +0,0 @@ - -import objectPath = require("object-path"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import AccessController from "../access_control/AccessController"; -import exceptions = require("../Exceptions"); - -function verify_filter(req: express.Request, res: express.Response) { - const logger = req.app.get("logger"); - const accessController: AccessController = req.app.get("access controller"); - - if (!objectPath.has(req, "session.auth_session")) - return BluebirdPromise.reject("No auth_session variable"); - - if (!objectPath.has(req, "session.auth_session.first_factor")) - return BluebirdPromise.reject("No first factor variable"); - - if (!objectPath.has(req, "session.auth_session.second_factor")) - return BluebirdPromise.reject("No second factor variable"); - - if (!objectPath.has(req, "session.auth_session.userid")) - return BluebirdPromise.reject("No userid variable"); - - const username = objectPath.get(req, "session.auth_session.userid"); - const groups = objectPath.get(req, "session.auth_session.groups"); - - const host = objectPath.get(req, "headers.host"); - const domain = host.split(":")[0]; - - const isAllowed = accessController.isDomainAllowedForUser(domain, username, groups); - if (!isAllowed) return BluebirdPromise.reject( - new exceptions.DomainAccessDenied("User '" + username + "' does not have access to " + domain)); - - if (!req.session.auth_session.first_factor || - !req.session.auth_session.second_factor) - return BluebirdPromise.reject(new exceptions.AccessDeniedError("First or second factor not validated")); - - return BluebirdPromise.resolve(); -} - -export = function (req: express.Request, res: express.Response) { - verify_filter(req, res) - .then(function () { - res.status(204); - res.send(); - }) - .catch(function (err) { - req.app.get("logger").error(err); - res.status(401); - res.send(); - }); -}; - diff --git a/src/lib/routes/DenyNotLogged.ts b/src/lib/routes/DenyNotLogged.ts deleted file mode 100644 index 2c2b71d93..000000000 --- a/src/lib/routes/DenyNotLogged.ts +++ /dev/null @@ -1,19 +0,0 @@ - -import objectPath = require("object-path"); -import express = require("express"); - -type ExpressRequest = (req: express.Request, res: express.Response, next?: express.NextFunction) => void; - -export = function(callback: ExpressRequest): ExpressRequest { - return function (req: express.Request, res: express.Response, next: express.NextFunction) { - const auth_session = req.session.auth_session; - const first_factor = objectPath.has(req, "session.auth_session.first_factor") - && req.session.auth_session.first_factor; - if (!first_factor) { - res.status(403); - res.send(); - return; - } - callback(req, res, next); - }; -}; diff --git a/src/lib/routes/FirstFactor.ts b/src/lib/routes/FirstFactor.ts deleted file mode 100644 index 7d33afc99..000000000 --- a/src/lib/routes/FirstFactor.ts +++ /dev/null @@ -1,82 +0,0 @@ - -import exceptions = require("../Exceptions"); -import objectPath = require("object-path"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import AccessController from "../access_control/AccessController"; -import AuthenticationRegulator from "../AuthenticationRegulator"; -import { LdapClient } from "../LdapClient"; - -export = function (req: express.Request, res: express.Response) { - const username: string = req.body.username; - const password: string = req.body.password; - if (!username || !password) { - res.status(401); - res.send(); - return; - } - - const logger = req.app.get("logger"); - const ldap: LdapClient = req.app.get("ldap"); - const config = req.app.get("config"); - const regulator: AuthenticationRegulator = req.app.get("authentication regulator"); - const accessController: AccessController = req.app.get("access controller"); - - logger.info("1st factor: Starting authentication of user \"%s\"", username); - logger.debug("1st factor: Start bind operation against LDAP"); - logger.debug("1st factor: username=%s", username); - - regulator.regulate(username) - .then(function () { - return ldap.bind(username, password); - }) - .then(function () { - objectPath.set(req, "session.auth_session.userid", username); - objectPath.set(req, "session.auth_session.first_factor", true); - logger.info("1st factor: LDAP binding successful"); - logger.debug("1st factor: Retrieve email from LDAP"); - return BluebirdPromise.join(ldap.get_emails(username), ldap.get_groups(username)); - }) - .then(function (data: [string[], string[]]) { - const emails: string[] = data[0]; - const groups: string[] = data[1]; - - if (!emails && emails.length <= 0) throw new Error("No email found"); - logger.debug("1st factor: Retrieved email are %s", emails); - objectPath.set(req, "session.auth_session.email", emails[0]); - objectPath.set(req, "session.auth_session.groups", groups); - - regulator.mark(username, true); - res.status(204); - res.send(); - }) - .catch(exceptions.LdapSeachError, function (err: Error) { - logger.error("1st factor: Unable to retrieve email from LDAP", err); - res.status(500); - res.send(); - }) - .catch(exceptions.LdapBindError, function (err: Error) { - logger.error("1st factor: LDAP binding failed"); - logger.debug("1st factor: LDAP binding failed due to ", err); - regulator.mark(username, false); - res.status(401); - res.send("Bad credentials"); - }) - .catch(exceptions.AuthenticationRegulationError, function (err: Error) { - logger.error("1st factor: the regulator rejected the authentication of user %s", username); - logger.debug("1st factor: authentication rejected due to %s", err); - res.status(403); - res.send("Access has been restricted for a few minutes..."); - }) - .catch(exceptions.DomainAccessDenied, (err: Error) => { - logger.error("1st factor: ", err); - res.status(401); - res.send("Access denied..."); - }) - .catch(function (err: Error) { - console.log(err.stack); - logger.error("1st factor: Unhandled error %s", err); - res.status(500); - res.send("Internal error"); - }); -}; diff --git a/src/lib/routes/PasswordReset.ts b/src/lib/routes/PasswordReset.ts deleted file mode 100644 index 25b8e1072..000000000 --- a/src/lib/routes/PasswordReset.ts +++ /dev/null @@ -1,81 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import objectPath = require("object-path"); -import exceptions = require("../Exceptions"); -import express = require("express"); -import { Identity } from "../../types/Identity"; -import { IdentityValidable } from "../IdentityValidator"; - -const CHALLENGE = "reset-password"; - -class PasswordResetHandler implements IdentityValidable { - challenge(): string { - return CHALLENGE; - } - - templateName(): string { - return "reset-password"; - } - - preValidation(req: express.Request): BluebirdPromise { - const userid = objectPath.get(req, "body.userid"); - if (!userid) { - return BluebirdPromise.reject(new exceptions.AccessDeniedError("No user id provided")); - } - - const ldap = req.app.get("ldap"); - return ldap.get_emails(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); - }); - } - - mailSubject(): string { - return "Reset your password"; - } -} - -function protect(fn: express.RequestHandler) { - return function (req: express.Request, res: express.Response) { - const challenge = objectPath.get(req, "session.auth_session.identity_check.challenge"); - if (challenge != CHALLENGE) { - res.status(403); - res.send(); - return; - } - fn(req, res, undefined); - }; -} - -function post(req: express.Request, res: express.Response) { - const logger = req.app.get("logger"); - const ldap = req.app.get("ldap"); - const new_password = objectPath.get(req, "body.password"); - const userid = objectPath.get(req, "session.auth_session.identity_check.userid"); - - logger.info("POST reset-password: User %s wants to reset his/her password", userid); - - ldap.update_password(userid, new_password) - .then(function () { - logger.info("POST reset-password: Password reset for user %s", userid); - objectPath.set(req, "session.auth_session", undefined); - res.status(204); - res.send(); - }) - .catch(function (err: Error) { - logger.error("POST reset-password: Error while resetting the password of user %s. %s", userid, err); - res.status(500); - res.send(); - }); -} - -export = { - icheck_interface: new PasswordResetHandler(), - post: protect(post) -}; diff --git a/src/lib/routes/SecondFactorRoutes.ts b/src/lib/routes/SecondFactorRoutes.ts deleted file mode 100644 index f8698c2f2..000000000 --- a/src/lib/routes/SecondFactorRoutes.ts +++ /dev/null @@ -1,28 +0,0 @@ - -import DenyNotLogged = require("./DenyNotLogged"); -import U2FRoutes = require("./U2FRoutes"); -import TOTPAuthenticator = require("./TOTPAuthenticator"); - -import express = require("express"); - -interface SecondFactorRoutes { - totp: express.RequestHandler; - u2f: { - register_request: express.RequestHandler; - register: express.RequestHandler; - sign_request: express.RequestHandler; - sign: express.RequestHandler; - }; -} - -export = { - totp: DenyNotLogged(TOTPAuthenticator), - u2f: { - register_request: U2FRoutes.register_request, - register: U2FRoutes.register, - - sign_request: DenyNotLogged(U2FRoutes.sign_request), - sign: DenyNotLogged(U2FRoutes.sign), - } -} as SecondFactorRoutes; - diff --git a/src/lib/routes/TOTPAuthenticator.ts b/src/lib/routes/TOTPAuthenticator.ts deleted file mode 100644 index 7f63f2ff8..000000000 --- a/src/lib/routes/TOTPAuthenticator.ts +++ /dev/null @@ -1,49 +0,0 @@ - -import exceptions = require("../Exceptions"); -import objectPath = require("object-path"); -import express = require("express"); -import { TOTPSecretDocument } from "../UserDataStore"; -import BluebirdPromise = require("bluebird"); - -const UNAUTHORIZED_MESSAGE = "Unauthorized access"; - -export = function(req: express.Request, res: express.Response) { - const logger = req.app.get("logger"); - const userid = objectPath.get(req, "session.auth_session.userid"); - logger.info("POST 2ndfactor totp: Initiate TOTP validation for user %s", userid); - - if (!userid) { - logger.error("POST 2ndfactor totp: No user id in the session"); - res.status(403); - res.send(); - return; - } - - const token = req.body.token; - const totpValidator = req.app.get("totp validator"); - const userDataStore = req.app.get("user data store"); - - logger.debug("POST 2ndfactor totp: Fetching secret for user %s", userid); - userDataStore.get_totp_secret(userid) - .then(function (doc: TOTPSecretDocument) { - logger.debug("POST 2ndfactor totp: TOTP secret is %s", JSON.stringify(doc)); - return totpValidator.validate(token, doc.secret.base32); - }) - .then(function () { - logger.debug("POST 2ndfactor totp: TOTP validation succeeded"); - objectPath.set(req, "session.auth_session.second_factor", true); - res.status(204); - res.send(); - }) - .catch(exceptions.InvalidTOTPError, function (err: Error) { - logger.error("POST 2ndfactor totp: Invalid TOTP token %s", err.message); - res.status(401); - res.send("Invalid TOTP token"); - }) - .catch(function (err: Error) { - console.log(err.stack); - logger.error("POST 2ndfactor totp: Internal error %s", err.message); - res.status(500); - res.send("Internal error"); - }); -}; diff --git a/src/lib/routes/TOTPRegistration.ts b/src/lib/routes/TOTPRegistration.ts deleted file mode 100644 index 1be58181f..000000000 --- a/src/lib/routes/TOTPRegistration.ts +++ /dev/null @@ -1,86 +0,0 @@ -import objectPath = require("object-path"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import exceptions = require("../Exceptions"); -import { Identity } from "../../types/Identity"; -import { IdentityValidable } from "../IdentityValidator"; - -const CHALLENGE = "totp-register"; -const TEMPLATE_NAME = "totp-register"; - - -class TOTPRegistrationHandler implements IdentityValidable { - challenge(): string { - return CHALLENGE; - } - - templateName(): string { - return TEMPLATE_NAME; - } - - preValidation(req: express.Request): BluebirdPromise { - const first_factor_passed = objectPath.get(req, "session.auth_session.first_factor"); - if (!first_factor_passed) { - return BluebirdPromise.reject("Authentication required before registering TOTP secret key"); - } - - const userid = objectPath.get(req, "session.auth_session.userid"); - const email = objectPath.get(req, "session.auth_session.email"); - - if (!(userid && email)) { - return BluebirdPromise.reject("User ID or email is missing"); - } - - const identity = { - email: email, - userid: userid - }; - return BluebirdPromise.resolve(identity); - } - - mailSubject(): string { - return "Register your TOTP secret key"; - } -} - -// Generate a secret and send it to the user -function post(req: express.Request, res: express.Response) { - const logger = req.app.get("logger"); - const userid = objectPath.get(req, "session.auth_session.identity_check.userid"); - const challenge = objectPath.get(req, "session.auth_session.identity_check.challenge"); - - if (challenge != CHALLENGE || !userid) { - res.status(403); - res.send(); - return; - } - - const user_data_store = req.app.get("user data store"); - const totpGenerator = req.app.get("totp generator"); - const secret = totpGenerator.generate(); - - logger.debug("POST new-totp-secret: save the TOTP secret in DB"); - user_data_store.set_totp_secret(userid, secret) - .then(function () { - const doc = { - otpauth_url: secret.otpauth_url, - base32: secret.base32, - ascii: secret.ascii - }; - objectPath.set(req, "session", undefined); - - res.status(200); - res.json(doc); - }) - .catch(function (err: Error) { - logger.error("POST new-totp-secret: Internal error %s", err); - res.status(500); - res.send(); - }); -} - - -export = { - icheck_interface: new TOTPRegistrationHandler(), - post: post, -}; diff --git a/src/lib/routes/U2FAuthenticationProcess.ts b/src/lib/routes/U2FAuthenticationProcess.ts deleted file mode 100644 index 84c8690b1..000000000 --- a/src/lib/routes/U2FAuthenticationProcess.ts +++ /dev/null @@ -1,84 +0,0 @@ - -import u2f_register_handler = require("./U2FRegistration"); -import objectPath = require("object-path"); -import u2f_common = require("./u2f_common"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import authdog = require("../../types/authdog"); -import UserDataStore, { U2FMetaDocument } from "../UserDataStore"; - - -function retrieve_u2f_meta(req: express.Request, userDataStore: UserDataStore) { - const userid = req.session.auth_session.userid; - const appid = u2f_common.extract_app_id(req); - return userDataStore.get_u2f_meta(userid, appid); -} - - -function sign_request(req: express.Request, res: express.Response) { - const logger = req.app.get("logger"); - const userDataStore = req.app.get("user data store"); - - retrieve_u2f_meta(req, userDataStore) - .then(function (doc: U2FMetaDocument) { - if (!doc) { - u2f_common.reply_with_missing_registration(res); - return; - } - - const u2f = req.app.get("u2f"); - const meta = doc.meta; - const appid = u2f_common.extract_app_id(req); - logger.info("U2F sign_request: Start authentication to app %s", appid); - return u2f.startAuthentication(appid, [meta]); - }) - .then(function (authRequest: authdog.AuthenticationRequest) { - logger.info("U2F sign_request: Store authentication request and reply"); - req.session.auth_session.sign_request = authRequest; - res.status(200); - res.json(authRequest); - }) - .catch(function (err: Error) { - logger.info("U2F sign_request: %s", err); - res.status(500); - res.send(); - }); -} - - -function sign(req: express.Request, res: express.Response) { - if (!objectPath.has(req, "session.auth_session.sign_request")) { - u2f_common.reply_with_unauthorized(res); - return; - } - - const logger = req.app.get("logger"); - const userDataStore = req.app.get("user data store"); - - retrieve_u2f_meta(req, userDataStore) - .then(function (doc: U2FMetaDocument) { - const appid = u2f_common.extract_app_id(req); - const u2f = req.app.get("u2f"); - const authRequest = req.session.auth_session.sign_request; - const meta = doc.meta; - logger.info("U2F sign: Finish authentication"); - return u2f.finishAuthentication(authRequest, req.body, [meta]); - }) - .then(function (authenticationStatus: authdog.Authentication) { - logger.info("U2F sign: Authentication successful"); - req.session.auth_session.second_factor = true; - res.status(204); - res.send(); - }) - .catch(function (err: Error) { - logger.error("U2F sign: %s", err); - res.status(500); - res.send(); - }); -} - - -export = { - sign_request: sign_request, - sign: sign -}; diff --git a/src/lib/routes/U2FRegistration.ts b/src/lib/routes/U2FRegistration.ts deleted file mode 100644 index d8126c460..000000000 --- a/src/lib/routes/U2FRegistration.ts +++ /dev/null @@ -1,51 +0,0 @@ - -import objectPath = require("object-path"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); - -import { IdentityValidable } from "../IdentityValidator"; -import { Identity } from "../../types/Identity"; - -const CHALLENGE = "u2f-register"; -const TEMPLATE_NAME = "u2f-register"; -const MAIL_SUBJECT = "Register your U2F device"; - - -class U2FRegistrationHandler implements IdentityValidable { - challenge(): string { - return CHALLENGE; - } - - templateName(): string { - return TEMPLATE_NAME; - } - - preValidation(req: express.Request): BluebirdPromise { - const first_factor_passed = objectPath.get(req, "session.auth_session.first_factor"); - if (!first_factor_passed) { - return BluebirdPromise.reject("Authentication required before issuing a u2f registration request"); - } - - const userid = objectPath.get(req, "session.auth_session.userid"); - const email = objectPath.get(req, "session.auth_session.email"); - - if (!(userid && email)) { - return BluebirdPromise.reject("User ID or email is missing"); - } - - const identity = { - email: email, - userid: userid - }; - return BluebirdPromise.resolve(identity); - } - - mailSubject(): string { - return MAIL_SUBJECT; - } -} - -export = { - icheck_interface: new U2FRegistrationHandler(), -}; - diff --git a/src/lib/routes/U2FRegistrationProcess.ts b/src/lib/routes/U2FRegistrationProcess.ts deleted file mode 100644 index 1737e2569..000000000 --- a/src/lib/routes/U2FRegistrationProcess.ts +++ /dev/null @@ -1,89 +0,0 @@ - -import u2f_register_handler = require("./U2FRegistration"); -import objectPath = require("object-path"); -import u2f_common = require("./u2f_common"); -import BluebirdPromise = require("bluebird"); -import express = require("express"); -import authdog = require("../../types/authdog"); - -function register_request(req: express.Request, res: express.Response) { - const logger = req.app.get("logger"); - const challenge = objectPath.get(req, "session.auth_session.identity_check.challenge"); - if (challenge != "u2f-register") { - res.status(403); - res.send(); - return; - } - - const u2f = req.app.get("u2f"); - const appid = u2f_common.extract_app_id(req); - - logger.debug("U2F register_request: headers=%s", JSON.stringify(req.headers)); - logger.info("U2F register_request: Starting registration of app %s", appid); - u2f.startRegistration(appid, []) - .then(function (registrationRequest: authdog.AuthenticationRequest) { - logger.info("U2F register_request: Sending back registration request"); - req.session.auth_session.register_request = registrationRequest; - res.status(200); - res.json(registrationRequest); - }) - .catch(function (err: Error) { - logger.error("U2F register_request: %s", err); - res.status(500); - res.send("Unable to start registration request"); - }); -} - -function register(req: express.Request, res: express.Response) { - const registrationRequest = objectPath.get(req, "session.auth_session.register_request"); - const challenge = objectPath.get(req, "session.auth_session.identity_check.challenge"); - - if (!registrationRequest) { - res.status(403); - res.send(); - return; - } - - if (!(registrationRequest && challenge == "u2f-register")) { - res.status(403); - res.send(); - return; - } - - - const user_data_storage = req.app.get("user data store"); - const u2f = req.app.get("u2f"); - const userid = req.session.auth_session.userid; - const appid = u2f_common.extract_app_id(req); - const logger = req.app.get("logger"); - - logger.info("U2F register: Finishing registration"); - logger.debug("U2F register: register_request=%s", JSON.stringify(registrationRequest)); - logger.debug("U2F register: body=%s", JSON.stringify(req.body)); - - u2f.finishRegistration(registrationRequest, req.body) - .then(function (registrationStatus: authdog.Registration) { - logger.info("U2F register: Store registration and reply"); - const meta = { - keyHandle: registrationStatus.keyHandle, - publicKey: registrationStatus.publicKey, - certificate: registrationStatus.certificate - }; - return user_data_storage.set_u2f_meta(userid, appid, meta); - }) - .then(function () { - objectPath.set(req, "session.auth_session.identity_check", undefined); - res.status(204); - res.send(); - }) - .catch(function (err: Error) { - logger.error("U2F register: %s", err); - res.status(500); - res.send("Unable to register"); - }); -} - -export = { - register_request: register_request, - register: register -}; diff --git a/src/lib/routes/U2FRoutes.ts b/src/lib/routes/U2FRoutes.ts deleted file mode 100644 index 50c150eee..000000000 --- a/src/lib/routes/U2FRoutes.ts +++ /dev/null @@ -1,19 +0,0 @@ - -import U2FRegistrationProcess = require("./U2FRegistrationProcess"); -import U2FAuthenticationProcess = require("./U2FAuthenticationProcess"); - -import express = require("express"); - -interface U2FRoutes { - register_request: express.RequestHandler; - register: express.RequestHandler; - sign_request: express.RequestHandler; - sign: express.RequestHandler; -} - -export = { - register_request: U2FRegistrationProcess.register_request, - register: U2FRegistrationProcess.register, - sign_request: U2FAuthenticationProcess.sign_request, - sign: U2FAuthenticationProcess.sign, -} as U2FRoutes; diff --git a/src/lib/routes/u2f_common.ts b/src/lib/routes/u2f_common.ts deleted file mode 100644 index cb13bd015..000000000 --- a/src/lib/routes/u2f_common.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import util = require("util"); -import express = require("express"); - -function extract_app_id(req: express.Request) { - return util.format("https://%s", req.headers.host); -} - -function extract_original_url(req: express.Request) { - return util.format("https://%s%s", req.headers.host, req.headers["x-original-uri"]); -} - -function extract_referrer(req: express.Request) { - return req.headers.referrer; -} - -function reply_with_internal_error(res: express.Response, msg: string) { - res.status(500); - res.send(msg); -} - -function reply_with_missing_registration(res: express.Response) { - res.status(401); - res.send("Please register before authenticate"); -} - -function reply_with_unauthorized(res: express.Response) { - res.status(401); - res.send(); -} - -export = { - extract_app_id: extract_app_id, - extract_original_url: extract_original_url, - extract_referrer: extract_referrer, - reply_with_internal_error: reply_with_internal_error, - reply_with_missing_registration: reply_with_missing_registration, - reply_with_unauthorized: reply_with_unauthorized -}; \ No newline at end of file diff --git a/src/public_html/css/login.css b/src/public_html/css/login.css deleted file mode 100644 index 85143d5e8..000000000 --- a/src/public_html/css/login.css +++ /dev/null @@ -1,126 +0,0 @@ -@import url(https://fonts.googleapis.com/css?family=Open+Sans); -.btn { display: inline-block; *display: inline; *zoom: 1; padding: 4px 10px 4px; margin-bottom: 0; font-size: 13px; line-height: 18px; color: #333333; text-align: center;text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); vertical-align: middle; background-color: #f5f5f5; background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); background-image: linear-gradient(top, #ffffff, #e6e6e6); background-repeat: repeat-x; filter: progid:dximagetransform.microsoft.gradient(startColorstr=#ffffff, endColorstr=#e6e6e6, GradientType=0); border-color: #e6e6e6 #e6e6e6 #e6e6e6; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); border: 1px solid #e6e6e6; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); cursor: pointer; *margin-left: .3em; } -.btn:hover, .btn:active, .btn.active, .btn.disabled, .btn[disabled] { background-color: #e6e6e6; } -.btn-large { padding: 9px 14px; font-size: 15px; line-height: normal; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } -.btn:hover { color: #333333; text-decoration: none; background-color: #e6e6e6; background-position: 0 -15px; -webkit-transition: background-position 0.1s linear; -moz-transition: background-position 0.1s linear; -ms-transition: background-position 0.1s linear; -o-transition: background-position 0.1s linear; transition: background-position 0.1s linear; } -.btn-primary, .btn-primary:hover { text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); color: #ffffff; } -.btn-primary.active { color: rgba(255, 255, 255, 0.75); } -.btn-primary { background-color: #4a77d4; background-image: -moz-linear-gradient(top, #6eb6de, #4a77d4); background-image: -ms-linear-gradient(top, #6eb6de, #4a77d4); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#6eb6de), to(#4a77d4)); background-image: -webkit-linear-gradient(top, #6eb6de, #4a77d4); background-image: -o-linear-gradient(top, #6eb6de, #4a77d4); background-image: linear-gradient(top, #6eb6de, #4a77d4); background-repeat: repeat-x; filter: progid:dximagetransform.microsoft.gradient(startColorstr=#6eb6de, endColorstr=#4a77d4, GradientType=0); border: 1px solid #3762bc; text-shadow: 1px 1px 1px rgba(0,0,0,0.4); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.5); } -.btn-primary:hover, .btn-primary:active, .btn-primary.active, .btn-primary.disabled, .btn-primary[disabled] { filter: none; background-color: #4a77d4; } -.btn-block { width: 100%; display:block; } - -* { -webkit-box-sizing:border-box; -moz-box-sizing:border-box; -ms-box-sizing:border-box; -o-box-sizing:border-box; box-sizing:border-box; } - -html { width: 100%; height:100%; overflow:hidden; } - -body { - width: 100%; - height:100%; - font-family: 'Open Sans', sans-serif; - background: #092756; - background: -moz-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%),-moz-linear-gradient(top, rgba(57,173,219,.25) 0%, rgba(42,60,87,.4) 100%), -moz-linear-gradient(-45deg, #670d10 0%, #092756 100%); - background: -webkit-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -webkit-linear-gradient(top, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -webkit-linear-gradient(-45deg, #670d10 0%,#092756 100%); - background: -o-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -o-linear-gradient(top, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -o-linear-gradient(-45deg, #670d10 0%,#092756 100%); - background: -ms-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -ms-linear-gradient(top, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -ms-linear-gradient(-45deg, #670d10 0%,#092756 100%); - background: -webkit-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), linear-gradient(to bottom, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), linear-gradient(135deg, #670d10 0%,#092756 100%); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3E1D6D', endColorstr='#092756',GradientType=1 ); -} - -.vr { - margin-left: 10px; - margin-right: 10px; -} - -.login { - position: absolute; - top: 50%; - left: 50%; - margin: -150px 0 0 -150px; - width:300px; - height:300px; -} - -.totp { - position: absolute; - top: 50%; - left: 50%; - margin: -150px 0 0 -150px; - width:400px; - height:300px; -} - -h1 { color: #fff; text-shadow: 0 0 10px rgba(0,0,0,0.3); letter-spacing:1px; text-align:center; } - -h2 { color: #fff; text-shadow: 0 0 10px rgba(0,0,0,0.3); letter-spacing:1px; text-align:center; font-size: 1em; } - -p { color: #fff; text-shadow: 0 0 10px rgba(0,0,0,0.3); letter-spacing:1px; text-align:center; } - -a { color: #fff; text-align: center; } - -#qrcode img { - margin: auto; - text-align: center; - padding: 10px; - background: white; -} - -#secret { font-size: 0.7em; } - -input { - width: 100%; - margin-bottom: 10px; - background: rgba(0,0,0,0.3); - border: none; - outline: none; - padding: 10px; - font-size: 13px; - color: #fff; - text-shadow: 1px 1px 1px rgba(0,0,0,0.3); - border: 1px solid rgba(0,0,0,0.3); - border-radius: 4px; - box-shadow: inset 0 -5px 45px rgba(100,100,100,0.2), 0 1px 1px rgba(255,255,255,0.2); - -webkit-transition: box-shadow .5s ease; - -moz-transition: box-shadow .5s ease; - -o-transition: box-shadow .5s ease; - -ms-transition: box-shadow .5s ease; - transition: box-shadow .5s ease; -} -input:focus { box-shadow: inset 0 -5px 45px rgba(100,100,100,0.4), 0 1px 1px rgba(255,255,255,0.2); } - -#information { - border: 1px solid black; - padding: 10px 20px; - margin-top: 25px; - font-size: 0.8em; - border-radius: 4px; -} - -#information.failure { - background-color: rgb(255, 124, 124); -} - -#information.success { - background-color: rgb(43, 188, 99); -} - -#second-factor { - width: 400px; -} - -#second-factor .login { - display: inline-block; -} - -#second-factor #totp { - width: 180px; - float: left; -} - -#second-factor #u2f { - width: 180px; - float: right; -} - -button { - margin-top: 5px; -} diff --git a/src/public_html/js/jquery.min.js b/src/public_html/js/jquery.min.js deleted file mode 100644 index 4c5be4c0f..000000000 --- a/src/public_html/js/jquery.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! jQuery v3.1.1 | (c) jQuery Foundation | jquery.org/license */ -!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R), -a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};la.optgroup=la.option,la.tbody=la.tfoot=la.colgroup=la.caption=la.thead,la.th=la.td;function ma(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function na(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=ma(l.appendChild(f),"script"),j&&na(g),c){k=0;while(f=g[k++])ka.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var qa=d.documentElement,ra=/^key/,sa=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ta=/^([^.]*)(?:\.(.+)|)/;function ua(){return!0}function va(){return!1}function wa(){try{return d.activeElement}catch(a){}}function xa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)xa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=va;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(qa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,za=/\s*$/g;function Da(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Ea(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Fa(a){var b=Ba.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ga(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Aa.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ia(f,b,c,d)});if(m&&(e=pa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(ma(e,"script"),Ea),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=ma(h),f=ma(a),d=0,e=f.length;d0&&na(g,!i&&ma(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ja(this,a,!0)},remove:function(a){return Ja(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.appendChild(a)}})},prepend:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(ma(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!za.test(a)&&!la[(ja.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function Ya(a,b,c,d,e){return new Ya.prototype.init(a,b,c,d,e)}r.Tween=Ya,Ya.prototype={constructor:Ya,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Ya.propHooks[this.prop];return a&&a.get?a.get(this):Ya.propHooks._default.get(this)},run:function(a){var b,c=Ya.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ya.propHooks._default.set(this),this}},Ya.prototype.init.prototype=Ya.prototype,Ya.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Ya.propHooks.scrollTop=Ya.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Ya.prototype.init,r.fx.step={};var Za,$a,_a=/^(?:toggle|show|hide)$/,ab=/queueHooks$/;function bb(){$a&&(a.requestAnimationFrame(bb),r.fx.tick())}function cb(){return a.setTimeout(function(){Za=void 0}),Za=r.now()}function db(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ba[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function eb(a,b,c){for(var d,e=(hb.tweeners[b]||[]).concat(hb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?ib:void 0)), -void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),ib={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=jb[b]||r.find.attr;jb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=jb[g],jb[g]=e,e=null!=c(a,b,d)?g:null,jb[g]=f),e}});var kb=/^(?:input|select|textarea|button)$/i,lb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):kb.test(a.nodeName)||lb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function mb(a){var b=a.match(K)||[];return b.join(" ")}function nb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,nb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,nb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,nb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=nb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(nb(c))+" ").indexOf(b)>-1)return!0;return!1}});var ob=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(ob,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:mb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ia.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,"$1"),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r(" \ No newline at end of file diff --git a/src/server/views/layout/layout.pug b/src/server/views/layout/layout.pug new file mode 100644 index 000000000..a085cb227 --- /dev/null +++ b/src/server/views/layout/layout.pug @@ -0,0 +1,27 @@ +block variables + +html + head + title Authelia - 2FA + meta(name="viewport", content="width=device-width, initial-scale=1.0")/ + link(rel="icon", href="/img/icon.png" type="image/gif" sizes="32x32")/ + link(rel="stylesheet", type="text/css", href="/css/authelia.min.css")/ + if redirection_url + + body +
    +
    +
    + +
    +
    +
    + script(src="/js/authelia.min.js") + block entrypoint \ No newline at end of file diff --git a/src/server/views/need-identity-validation.pug b/src/server/views/need-identity-validation.pug new file mode 100644 index 000000000..c6690b0b2 --- /dev/null +++ b/src/server/views/need-identity-validation.pug @@ -0,0 +1,8 @@ +extends layout/layout.pug + +block form-header +

    Registration

    + + +block content +

    A confirmation email has been sent to your mailbox. Please open it and click on the link within 15 minutes to confirm the registration.

    diff --git a/src/server/views/password-reset-form.pug b/src/server/views/password-reset-form.pug new file mode 100644 index 000000000..e90c5e3fb --- /dev/null +++ b/src/server/views/password-reset-form.pug @@ -0,0 +1,22 @@ +extends layout/layout.pug + +block variables + - page_classname = "password-reset-form"; + +block form-header +

    Reset password

    + +

    Set your new password and confirm it.

    + +block content + + +block entrypoint + diff --git a/src/server/views/password-reset-request.pug b/src/server/views/password-reset-request.pug new file mode 100644 index 000000000..714ff0eac --- /dev/null +++ b/src/server/views/password-reset-request.pug @@ -0,0 +1,22 @@ +extends layout/layout.pug + +block variables + - page_classname = "password-reset-request"; + +block form-header +

    Reset password

    + +

    After giving your username, you will receive an email to change your password.

    + +block content + + +block entrypoint + + diff --git a/src/server/views/secondfactor.pug b/src/server/views/secondfactor.pug new file mode 100644 index 000000000..1f824c2cf --- /dev/null +++ b/src/server/views/secondfactor.pug @@ -0,0 +1,26 @@ +extends layout/layout.pug + +block form-header +

    Sign in

    + + +block content + +
    + + +block entrypoint + \ No newline at end of file diff --git a/src/server/views/totp-register.pug b/src/server/views/totp-register.pug new file mode 100644 index 000000000..f4c4237ed --- /dev/null +++ b/src/server/views/totp-register.pug @@ -0,0 +1,19 @@ +extends layout/layout.pug + +block variables + - page_classname = "totp-register"; + +block form-header +

    TOTP Secret

    +

    Insert your secret in Google Authenticator

    + +block content + p(id="secret") #{ base32_secret } + div(id="qrcode") #{ otpauth_url } +

    Login

    + +block entrypoint + + diff --git a/src/server/views/u2f-register.pug b/src/server/views/u2f-register.pug new file mode 100644 index 000000000..af24eae90 --- /dev/null +++ b/src/server/views/u2f-register.pug @@ -0,0 +1,14 @@ +extends layout/layout.pug + +block variables + - page_classname = "u2f-register"; + +block form-header +

    U2F Registration

    +

    Touch the token to register your U2F device.

    + +block content + pendrive + +block entrypoint + diff --git a/src/lib/Configuration.ts b/src/types/Configuration.ts similarity index 100% rename from src/lib/Configuration.ts rename to src/types/Configuration.ts diff --git a/src/types/Dependencies.ts b/src/types/Dependencies.ts index 3047938fb..261cd2ff5 100644 --- a/src/types/Dependencies.ts +++ b/src/types/Dependencies.ts @@ -4,6 +4,7 @@ import nodemailer = require("nodemailer"); import session = require("express-session"); import nedb = require("nedb"); import ldapjs = require("ldapjs"); +import u2f = require("u2f"); export type Nodemailer = typeof nodemailer; export type Speakeasy = typeof speakeasy; @@ -11,9 +12,10 @@ 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 interface GlobalDependencies { - u2f: object; + u2f: U2f; nodemailer: Nodemailer; ldapjs: Ldapjs; session: Session; diff --git a/src/types/ILogger.ts b/src/types/ILogger.ts deleted file mode 100644 index 96f03fe64..000000000 --- a/src/types/ILogger.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import * as winston from "winston"; - -export interface ILogger { - debug: winston.LeveledLogMethod; -} - diff --git a/src/types/TOTPSecret.ts b/src/types/TOTPSecret.ts index e4a6b7d7b..33ce602c0 100644 --- a/src/types/TOTPSecret.ts +++ b/src/types/TOTPSecret.ts @@ -2,5 +2,5 @@ export interface TOTPSecret { base32: string; ascii: string; - otpauth_url: string; + otpauth_url?: string; } \ No newline at end of file diff --git a/src/types/authdog.d.ts b/src/types/authdog.d.ts deleted file mode 100644 index 4405f6f1d..000000000 --- a/src/types/authdog.d.ts +++ /dev/null @@ -1,69 +0,0 @@ - -import BluebirdPromise = require("bluebird"); - -declare module "authdog" { - interface RegisterRequest { - challenge: string; - } - - interface RegisteredKey { - version: number; - keyHandle: string; - } - - type RegisteredKeys = Array; - type RegisterRequests = Array; - type AppId = string; - - interface RegistrationRequest { - appId: AppId; - type: string; - registerRequests: RegisterRequests; - registeredKeys: RegisteredKeys; - } - - interface Registration { - publicKey: string; - keyHandle: string; - certificate: string; - } - - interface ClientData { - challenge: string; - } - - interface RegistrationResponse { - clientData: ClientData; - registrationData: string; - } - - interface Options { - timeoutSeconds: number; - requestId: string; - } - - interface AuthenticationRequest { - appId: AppId; - type: string; - challenge: string; - registeredKeys: RegisteredKeys; - timeoutSeconds: number; - requestId: string; - } - - interface AuthenticationResponse { - keyHandle: string; - clientData: ClientData; - signatureData: string; - } - - interface Authentication { - userPresence: Uint8Array, - counter: Uint32Array - } - - export function startRegistration(appId: AppId, registeredKeys: RegisteredKeys, options?: Options): BluebirdPromise; - export function finishRegistration(registrationRequest: RegistrationRequest, registrationResponse: RegistrationResponse): BluebirdPromise; - export function startAuthentication(appId: AppId, registeredKeys: RegisteredKeys, options: Options): BluebirdPromise; - export function finishAuthentication(challenge: string, deviceResponse: AuthenticationResponse, registeredKeys: RegisteredKeys): BluebirdPromise; -} \ No newline at end of file diff --git a/src/types/jquery-notify.d.ts b/src/types/jquery-notify.d.ts new file mode 100644 index 000000000..60d08cc11 --- /dev/null +++ b/src/types/jquery-notify.d.ts @@ -0,0 +1,4 @@ + +interface JQueryStatic { + notify: any; +} diff --git a/src/types/request-async.d.ts b/src/types/request-async.d.ts index 164d69196..964a7b24e 100644 --- a/src/types/request-async.d.ts +++ b/src/types/request-async.d.ts @@ -1,8 +1,9 @@ import * as BluebirdPromise from "bluebird"; -import * as request from "request"; declare module "request" { - export interface RequestAsync extends RequestAPI { + export interface RequestAPI { getAsync(uri: string, options?: RequiredUriUrl): BluebirdPromise; getAsync(uri: string): BluebirdPromise; getAsync(options: RequiredUriUrl & CoreOptions): BluebirdPromise; diff --git a/src/types/u2f-api.d.ts b/src/types/u2f-api.d.ts new file mode 100644 index 000000000..87a0e4b8b --- /dev/null +++ b/src/types/u2f-api.d.ts @@ -0,0 +1,63 @@ + + +declare module "u2f-api" { + type MessageTypes = "u2f_register_request" | "u2f_sign_request" | "u2f_register_response" | "u2f_sign_response"; + + export interface Request { + type: MessageTypes, + signRequests: SignRequest[], + registerRequests?: RegisterRequest[], + timeoutSeconds?: number, + requestId?: number + } + + type ResponseData = Error | RegisterResponse | SignResponse; + + + export interface Response { + type: MessageTypes; + responseData: ResponseData; + requestId?: number; + } + + export enum ErrorCodes { + 'OK' = 0, + 'OTHER_ERROR' = 1, + 'BAD_REQUEST' = 2, + 'CONFIGURATION_UNSUPPORTED' = 3, + 'DEVICE_INELIGIBLE' = 4, + 'TIMEOUT' = 5 + } + + export interface Error { + errorCode: ErrorCodes; + errorMessage?: string; + } + + export interface RegisterResponse { + registrationData: string; + clientData: string; + } + + export interface RegisterRequest { + version: string; + challenge: string; + appId: string; + } + + export interface SignResponse { + keyHandle: string; + signatureData: string; + clientData: string; + } + + export interface SignRequest { + version: string; + challenge: string; + keyHandle: string; + appId: string; + } + + export function sign(signRequests: SignRequest[], timeout: number): Promise; + export function register(registerRequests: RegisterRequest[], signRequests: SignRequest[], timeout: number): Promise; +} \ No newline at end of file diff --git a/src/types/u2f.d.ts b/src/types/u2f.d.ts new file mode 100644 index 000000000..b308fbc42 --- /dev/null +++ b/src/types/u2f.d.ts @@ -0,0 +1,45 @@ + + +declare module "u2f" { + export interface Request { + version: "U2F_V2"; + appId: string; + challenge: string; + keyHandle?: string; + } + + export interface RegistrationData { + clientData: string; + registrationData: string; + errorCode?: number; + } + + export interface RegistrationResult { + successful: boolean; + publicKey: string; + keyHandle: string; + certificate: string; + } + + + export interface SignatureData { + clientData: string; + signatureData: string; + errorCode?: number; + } + + export interface SignatureResult { + successful: boolean; + userPresent: boolean; + counter: number; + } + + export interface Error { + errorCode: number; + errorMessage: string; + } + + export function request(appId: string, keyHandle?: string): Request; + export function checkRegistration(request: Request, registerData: RegistrationData): RegistrationResult | Error; + export function checkSignature(request: Request, signData: SignatureData, publicKey: string): SignatureResult | Error; +} \ No newline at end of file diff --git a/src/views/head.ejs b/src/views/head.ejs deleted file mode 100644 index 618957e4e..000000000 --- a/src/views/head.ejs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/views/login.ejs b/src/views/login.ejs deleted file mode 100644 index cacd1517e..000000000 --- a/src/views/login.ejs +++ /dev/null @@ -1,35 +0,0 @@ - - - Login - <% include head %> - - - - - - - <% include scripts %> - - - diff --git a/src/views/reset-password-form.ejs b/src/views/reset-password-form.ejs deleted file mode 100644 index 7a4f44d01..000000000 --- a/src/views/reset-password-form.ejs +++ /dev/null @@ -1,18 +0,0 @@ - - - Reset Password - <% include head %> - - - - - <% include scripts %> - - diff --git a/src/views/reset-password.ejs b/src/views/reset-password.ejs deleted file mode 100644 index 603417549..000000000 --- a/src/views/reset-password.ejs +++ /dev/null @@ -1,19 +0,0 @@ - - - Reset Password - <% include head %> - - - - - <% include scripts %> - - diff --git a/src/views/scripts.ejs b/src/views/scripts.ejs deleted file mode 100644 index 49ad79ac8..000000000 --- a/src/views/scripts.ejs +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/views/totp-register.ejs b/src/views/totp-register.ejs deleted file mode 100644 index 0e94f70e6..000000000 --- a/src/views/totp-register.ejs +++ /dev/null @@ -1,19 +0,0 @@ - - - TOTP Registration - <% include head %> - - -
    -

    TOTP Secret

    -

    Insert your secret in Google Authenticator

    -

    -
    -

    Login

    -
    - - - <% include scripts %> - - - diff --git a/src/views/u2f-register.ejs b/src/views/u2f-register.ejs deleted file mode 100644 index d7b743eae..000000000 --- a/src/views/u2f-register.ejs +++ /dev/null @@ -1,16 +0,0 @@ - - - FIDO U2F Registration - <% include head %> - - - - - - <% include scripts %> - - - diff --git a/test/client/firstfactor/FirstFactorValidator.test.ts b/test/client/firstfactor/FirstFactorValidator.test.ts new file mode 100644 index 000000000..717f10609 --- /dev/null +++ b/test/client/firstfactor/FirstFactorValidator.test.ts @@ -0,0 +1,48 @@ + +import FirstFactorValidator = require("../../../src/client/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(); + postPromise.done.returns(postPromise); + + const jqueryMock = JQueryMock.JQueryMock(); + jqueryMock.post.returns(postPromise); + + return FirstFactorValidator.validate("username", "password", jqueryMock as any); + }); + + function should_fail_first_factor_validation(statusCode: number, errorMessage: string) { + const xhr = { + status: statusCode + }; + const postPromise = JQueryMock.JQueryDeferredMock(); + postPromise.fail.yields(xhr, errorMessage); + postPromise.done.returns(postPromise); + + const jqueryMock = JQueryMock.JQueryMock(); + jqueryMock.post.returns(postPromise); + + return FirstFactorValidator.validate("username", "password", jqueryMock 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 500", () => { + return should_fail_first_factor_validation(500, "Internal error"); + }); + + it("should fail with error 401", () => { + return should_fail_first_factor_validation(401, "Authetication failed. Please check your credentials"); + }); + }); +}); \ No newline at end of file diff --git a/test/client/firstfactor/login.test.ts b/test/client/firstfactor/login.test.ts new file mode 100644 index 000000000..50e7307f1 --- /dev/null +++ b/test/client/firstfactor/login.test.ts @@ -0,0 +1,87 @@ + +import Endpoints = require("../../../src/server/endpoints"); +import BluebirdPromise = require("bluebird"); + +import UISelectors = require("../../../src/client/firstfactor/UISelectors"); +import firstfactor from "../../../src/client/firstfactor/index"; +import JQueryMock = require("../mocks/jquery"); +import Assert = require("assert"); +import sinon = require("sinon"); +import jslogger = require("js-logger"); + +describe("test first factor page", () => { + it("should validate first factor", () => { + const jQuery = JQueryMock.JQueryMock(); + const window = { + location: { + search: "?redirect=https://example.com", + href: "" + }, + document: {}, + }; + + const thenSpy = sinon.spy(); + const FirstFactorValidator: any = { + validate: sinon.stub().returns({ then: thenSpy }) + }; + + firstfactor(window as Window, jQuery as any, FirstFactorValidator, jslogger); + const readyCallback = jQuery.getCall(0).returnValue.ready.getCall(0).args[0]; + readyCallback(); + + const onSubmitCallback = jQuery.getCall(1).returnValue.on.getCall(0).args[1]; + jQuery.onCall(2).returns({ val: sinon.stub() }); + jQuery.onCall(3).returns({ val: sinon.stub() }); + jQuery.onCall(4).returns({ val: sinon.stub() }); + jQuery.onCall(5).returns({ val: sinon.stub() }); + + onSubmitCallback(); + + const successCallback = thenSpy.getCall(0).args[0]; + successCallback(); + + Assert.equal(window.location.href, Endpoints.SECOND_FACTOR_GET); + }); + + describe("fail to validate first factor", () => { + let jQuery: JQueryMock.JQueryMock; + beforeEach(function () { + jQuery = JQueryMock.JQueryMock(); + const window = { + location: { + search: "?redirect=https://example.com", + href: "" + }, + document: {}, + }; + + const thenSpy = sinon.spy(); + const FirstFactorValidator: any = { + validate: sinon.stub().returns({ then: thenSpy }) + }; + + firstfactor(window as Window, jQuery as any, FirstFactorValidator, jslogger); + const readyCallback = jQuery.getCall(0).returnValue.ready.getCall(0).args[0]; + readyCallback(); + + const onSubmitCallback = jQuery.getCall(1).returnValue.on.getCall(0).args[1]; + jQuery.onCall(2).returns({ val: sinon.stub() }); + jQuery.onCall(3).returns({ val: sinon.stub() }); + jQuery.onCall(4).returns({ val: sinon.stub() }); + jQuery.onCall(5).returns({ val: sinon.stub() }); + + onSubmitCallback(); + + const failureCallback = thenSpy.getCall(0).args[1]; + failureCallback(new Error("Error when validating first factor")); + }); + + it("should notify the user there is a failure", function () { + Assert(jQuery.notify.calledOnce); + }); + + it("should reset the password field", function () { + Assert.equal(jQuery.getCall(4).returnValue.val.getCall(0).args[0], ""); + }); + }); +}); \ No newline at end of file diff --git a/test/client/mocks/jquery.ts b/test/client/mocks/jquery.ts new file mode 100644 index 000000000..905840ac6 --- /dev/null +++ b/test/client/mocks/jquery.ts @@ -0,0 +1,39 @@ + +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 JQueryDeferredMock { + done: sinon.SinonStub; + fail: sinon.SinonStub; +} + +export function JQueryMock(): JQueryMock { + const jquery = sinon.stub() as any; + const jqueryInstance = { + ready: sinon.stub(), + show: sinon.stub(), + hide: 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; +} + +export function JQueryDeferredMock(): JQueryDeferredMock { + return { + done: sinon.stub(), + fail: sinon.stub() + }; +} diff --git a/test/client/mocks/u2f-api.ts b/test/client/mocks/u2f-api.ts new file mode 100644 index 000000000..d123f6a95 --- /dev/null +++ b/test/client/mocks/u2f-api.ts @@ -0,0 +1,14 @@ + +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/test/client/secondfactor/TOTPValidator.test.ts b/test/client/secondfactor/TOTPValidator.test.ts new file mode 100644 index 000000000..dd10db8b5 --- /dev/null +++ b/test/client/secondfactor/TOTPValidator.test.ts @@ -0,0 +1,37 @@ + +import TOTPValidator = require("../../../src/client/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(); + postPromise.done.returns(postPromise); + + const jqueryMock = JQueryMock.JQueryMock(); + jqueryMock.ajax.returns(postPromise); + + return TOTPValidator.validate("totp_token", jqueryMock 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.ajax.returns(postPromise); + + return TOTPValidator.validate("totp_token", jqueryMock 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/test/client/secondfactor/U2FValidator.test.ts b/test/client/secondfactor/U2FValidator.test.ts new file mode 100644 index 000000000..feb08297a --- /dev/null +++ b/test/client/secondfactor/U2FValidator.test.ts @@ -0,0 +1,110 @@ + +import U2FValidator = require("../../../src/client/secondfactor/U2FValidator"); +import JQueryMock = require("../mocks/jquery"); +import U2FApiMock = require("../mocks/u2f-api"); +import { SignMessage } from "../../../src/server/lib/routes/secondfactor/u2f/sign_request/SignMessage"; +import BluebirdPromise = require("bluebird"); +import Assert = require("assert"); + +describe("test U2F validation", function () { + it("should validate the U2F device", () => { + const signatureRequest: SignMessage = { + keyHandle: "keyhandle", + request: { + version: "U2F_V2", + appId: "https://example.com", + challenge: "challenge" + } + }; + const u2fClient = U2FApiMock.U2FApiMock(); + u2fClient.sign.returns(BluebirdPromise.resolve()); + + const getPromise = JQueryMock.JQueryDeferredMock(); + getPromise.done.yields(signatureRequest); + getPromise.done.returns(getPromise); + + const postPromise = JQueryMock.JQueryDeferredMock(); + postPromise.done.yields(); + postPromise.done.returns(postPromise); + + const jqueryMock = JQueryMock.JQueryMock(); + jqueryMock.get.returns(getPromise); + jqueryMock.ajax.returns(postPromise); + + return U2FValidator.validate(jqueryMock as any, u2fClient as any); + }); + + it("should fail during initial authentication request", () => { + const u2fClient = U2FApiMock.U2FApiMock(); + + const getPromise = JQueryMock.JQueryDeferredMock(); + getPromise.done.returns(getPromise); + getPromise.fail.yields(undefined, "Error while issuing authentication request"); + + const jqueryMock = JQueryMock.JQueryMock(); + jqueryMock.get.returns(getPromise); + + return U2FValidator.validate(jqueryMock as any, u2fClient as any) + .catch(function(err: Error) { + Assert.equal("Error while issuing authentication request", err.message); + return BluebirdPromise.resolve(); + }); + }); + + it("should fail during device signature", () => { + const signatureRequest: SignMessage = { + keyHandle: "keyhandle", + request: { + version: "U2F_V2", + appId: "https://example.com", + challenge: "challenge" + } + }; + const u2fClient = U2FApiMock.U2FApiMock(); + u2fClient.sign.returns(BluebirdPromise.reject(new Error("Device unable to sign"))); + + const getPromise = JQueryMock.JQueryDeferredMock(); + getPromise.done.yields(signatureRequest); + getPromise.done.returns(getPromise); + + const jqueryMock = JQueryMock.JQueryMock(); + jqueryMock.get.returns(getPromise); + + return U2FValidator.validate(jqueryMock as any, u2fClient as any) + .catch(function(err: Error) { + Assert.equal("Device unable to sign", err.message); + return BluebirdPromise.resolve(); + }); + }); + + it("should fail at the end of the authentication request", () => { + const signatureRequest: SignMessage = { + keyHandle: "keyhandle", + request: { + version: "U2F_V2", + appId: "https://example.com", + challenge: "challenge" + } + }; + const u2fClient = U2FApiMock.U2FApiMock(); + u2fClient.sign.returns(BluebirdPromise.resolve()); + + const getPromise = JQueryMock.JQueryDeferredMock(); + getPromise.done.yields(signatureRequest); + getPromise.done.returns(getPromise); + + const postPromise = JQueryMock.JQueryDeferredMock(); + postPromise.fail.yields(undefined, "Error while finishing authentication"); + postPromise.done.returns(postPromise); + + const jqueryMock = JQueryMock.JQueryMock(); + jqueryMock.get.returns(getPromise); + jqueryMock.ajax.returns(postPromise); + + return U2FValidator.validate(jqueryMock as any, u2fClient as any) + .catch(function(err: Error) { + Assert.equal("Error while finishing authentication", err.message); + return BluebirdPromise.resolve(); + }); + }); +}); \ No newline at end of file diff --git a/test/client/totp-register/totp-register.test.ts b/test/client/totp-register/totp-register.test.ts new file mode 100644 index 000000000..0a445cc44 --- /dev/null +++ b/test/client/totp-register/totp-register.test.ts @@ -0,0 +1,31 @@ + +import sinon = require("sinon"); +import assert = require("assert"); + +import UISelector = require("../../../src/client/totp-register/ui-selector"); +import TOTPRegister = require("../../../src/client/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/test/integration/test_server.js b/test/integration/test_server.js deleted file mode 100644 index d91342876..000000000 --- a/test/integration/test_server.js +++ /dev/null @@ -1,156 +0,0 @@ - -var request_ = require('request'); -var assert = require('assert'); -var speakeasy = require('speakeasy'); -var j = request_.jar(); -var Promise = require('bluebird'); -var request = Promise.promisifyAll(request_.defaults({jar: j})); -var util = require('util'); -var sinon = require('sinon'); - -process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; - -var AUTHELIA_HOST = 'nginx'; -var DOMAIN = 'test.local'; -var PORT = 8080; - -var HOME_URL = util.format('https://%s.%s:%d', 'home', DOMAIN, PORT); -var SECRET_URL = util.format('https://%s.%s:%d', 'secret', DOMAIN, PORT); -var SECRET1_URL = util.format('https://%s.%s:%d', 'secret1', DOMAIN, PORT); -var SECRET2_URL = util.format('https://%s.%s:%d', 'secret2', DOMAIN, PORT); -var MX1_URL = util.format('https://%s.%s:%d', 'mx1.mail', DOMAIN, PORT); -var MX2_URL = util.format('https://%s.%s:%d', 'mx2.mail', DOMAIN, PORT); -var BASE_AUTH_URL = util.format('https://%s.%s:%d', 'auth', DOMAIN, PORT); - -describe('test the server', function() { - var home_page; - var login_page; - - before(function() { - var home_page_promise = getHomePage() - .then(function(data) { - home_page = data.body; - }); - var login_page_promise = getLoginPage() - .then(function(data) { - login_page = data.body; - }); - return Promise.all([home_page_promise, - login_page_promise]); - }); - - function str_contains(str, pattern) { - return str.indexOf(pattern) != -1; - } - - function home_page_contains(pattern) { - return str_contains(home_page, pattern); - } - - it('should serve a correct home page', function() { - assert(home_page_contains(BASE_AUTH_URL + '/logout?redirect=' + HOME_URL + '/')); - assert(home_page_contains(HOME_URL + '/secret.html')); - assert(home_page_contains(SECRET_URL + '/secret.html')); - assert(home_page_contains(SECRET1_URL + '/secret.html')); - assert(home_page_contains(SECRET2_URL + '/secret.html')); - assert(home_page_contains(MX1_URL + '/secret.html')); - assert(home_page_contains(MX2_URL + '/secret.html')); - }); - - it('should serve the login page', function(done) { - getPromised(BASE_AUTH_URL + '/login?redirect=/') - .then(function(data) { - assert.equal(data.statusCode, 200); - done(); - }); - }); - - it('should serve the homepage', function(done) { - getPromised(HOME_URL + '/') - .then(function(data) { - assert.equal(data.statusCode, 200); - done(); - }); - }); - - it('should redirect when logout', function(done) { - getPromised(BASE_AUTH_URL + '/logout?redirect=' + HOME_URL) - .then(function(data) { - assert.equal(data.statusCode, 200); - assert.equal(data.body, home_page); - done(); - }); - }); - - it('should be redirected to the login page when accessing secret while not authenticated', function(done) { - var url = HOME_URL + '/secret.html'; - // console.log(url); - getPromised(url) - .then(function(data) { - assert.equal(data.statusCode, 200); - assert.equal(data.body, login_page); - done(); - }); - }); - - it.skip('should fail the first factor', function(done) { - postPromised(BASE_AUTH_URL + '/1stfactor', { - form: { - username: 'admin', - password: 'password', - } - }) - .then(function(data) { - assert.equal(data.body, 'Bad credentials'); - done(); - }); - }); - - function login_as(username, password) { - return postPromised(BASE_AUTH_URL + '/1stfactor', { - form: { - username: 'john', - password: 'password', - } - }) - .then(function(data) { - assert.equal(data.statusCode, 204); - return Promise.resolve(); - }); - } - - it('should succeed the first factor', function() { - return login_as('john', 'password'); - }); - - describe('test ldap connection', function() { - it('should not fail after inactivity', function() { - var clock = sinon.useFakeTimers(); - return login_as('john', 'password') - .then(function() { - clock.tick(3600000 * 24); // 24 hour - return login_as('john', 'password'); - }) - .then(function() { - clock.restore(); - return Promise.resolve(); - }); - }); - }); -}); - -function getPromised(url) { - return request.getAsync(url); -} - -function postPromised(url, body) { - return request.postAsync(url, body); -} - -function getHomePage() { - return getPromised(HOME_URL + '/'); -} - -function getLoginPage() { - return getPromised(BASE_AUTH_URL + '/login'); -} diff --git a/test/integration/test_server.ts b/test/integration/test_server.ts new file mode 100644 index 000000000..5a0541ed5 --- /dev/null +++ b/test/integration/test_server.ts @@ -0,0 +1,157 @@ + +import request_ = require("request"); +import assert = require("assert"); +import speakeasy = require("speakeasy"); +import BluebirdPromise = require("bluebird"); +import util = require("util"); +import sinon = require("sinon"); +import Endpoints = require("../../src/server/endpoints"); + +const j = request_.jar(); +const request: typeof request_ = BluebirdPromise.promisifyAll(request_.defaults({ jar: j })); + +process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; + +const AUTHELIA_HOST = "nginx"; +const DOMAIN = "test.local"; +const PORT = 8080; + +const HOME_URL = util.format("https://%s.%s:%d", "home", DOMAIN, PORT); +const SECRET_URL = util.format("https://%s.%s:%d", "secret", DOMAIN, PORT); +const SECRET1_URL = util.format("https://%s.%s:%d", "secret1", DOMAIN, PORT); +const SECRET2_URL = util.format("https://%s.%s:%d", "secret2", DOMAIN, PORT); +const MX1_URL = util.format("https://%s.%s:%d", "mx1.mail", DOMAIN, PORT); +const MX2_URL = util.format("https://%s.%s:%d", "mx2.mail", DOMAIN, PORT); +const BASE_AUTH_URL = util.format("https://%s.%s:%d", "auth", DOMAIN, PORT); + +describe("test the server", function () { + let home_page: string; + let login_page: string; + + before(function () { + const home_page_promise = getHomePage() + .then(function (data) { + home_page = data.body; + }); + const login_page_promise = getLoginPage() + .then(function (data) { + login_page = data.body; + }); + return BluebirdPromise.all([home_page_promise, + login_page_promise]); + }); + + function str_contains(str: string, pattern: string) { + return str.indexOf(pattern) != -1; + } + + function home_page_contains(pattern: string) { + return str_contains(home_page, pattern); + } + + it("should serve a correct home page", function () { + assert(home_page_contains(BASE_AUTH_URL + Endpoints.LOGOUT_GET + "?redirect=" + HOME_URL + "/")); + assert(home_page_contains(HOME_URL + "/secret.html")); + assert(home_page_contains(SECRET_URL + "/secret.html")); + assert(home_page_contains(SECRET1_URL + "/secret.html")); + assert(home_page_contains(SECRET2_URL + "/secret.html")); + assert(home_page_contains(MX1_URL + "/secret.html")); + assert(home_page_contains(MX2_URL + "/secret.html")); + }); + + it("should serve the login page", function (done) { + getPromised(BASE_AUTH_URL + Endpoints.FIRST_FACTOR_GET + "?redirect=/") + .then(function (data: request_.RequestResponse) { + assert.equal(data.statusCode, 200); + done(); + }); + }); + + it("should serve the homepage", function (done) { + getPromised(HOME_URL + "/") + .then(function (data: request_.RequestResponse) { + assert.equal(data.statusCode, 200); + done(); + }); + }); + + it("should redirect when logout", function (done) { + getPromised(BASE_AUTH_URL + Endpoints.LOGOUT_GET + "?redirect=" + HOME_URL) + .then(function (data: request_.RequestResponse) { + assert.equal(data.statusCode, 200); + assert.equal(data.body, home_page); + done(); + }); + }); + + it("should be redirected to the login page when accessing secret while not authenticated", function (done) { + const url = HOME_URL + "/secret.html"; + getPromised(url) + .then(function (data: request_.RequestResponse) { + assert.equal(data.statusCode, 200); + assert.equal(data.body, login_page); + done(); + }); + }); + + it.skip("should fail the first factor", function (done) { + postPromised(BASE_AUTH_URL + Endpoints.FIRST_FACTOR_POST, { + form: { + username: "admin", + password: "password", + } + }) + .then(function (data: request_.RequestResponse) { + assert.equal(data.body, "Bad credentials"); + done(); + }); + }); + + function login_as(username: string, password: string) { + return postPromised(BASE_AUTH_URL + Endpoints.FIRST_FACTOR_POST, { + form: { + username: "john", + password: "password", + } + }) + .then(function (data: request_.RequestResponse) { + assert.equal(data.statusCode, 302); + return BluebirdPromise.resolve(); + }); + } + + it("should succeed the first factor", function () { + return login_as("john", "password"); + }); + + describe("test ldap connection", function () { + it("should not fail after inactivity", function () { + const clock = sinon.useFakeTimers(); + return login_as("john", "password") + .then(function () { + clock.tick(3600000 * 24); // 24 hour + return login_as("john", "password"); + }) + .then(function () { + clock.restore(); + return BluebirdPromise.resolve(); + }); + }); + }); +}); + +function getPromised(url: string) { + return request.getAsync(url); +} + +function postPromised(url: string, body: Object) { + return request.postAsync(url, body); +} + +function getHomePage(): BluebirdPromise { + return getPromised(HOME_URL + "/"); +} + +function getLoginPage(): BluebirdPromise { + return getPromised(BASE_AUTH_URL + Endpoints.FIRST_FACTOR_GET); +} diff --git a/test/unitary/AuthenticationRegulator.test.ts b/test/server/AuthenticationRegulator.test.ts similarity index 89% rename from test/unitary/AuthenticationRegulator.test.ts rename to test/server/AuthenticationRegulator.test.ts index 27053790a..549ea0549 100644 --- a/test/unitary/AuthenticationRegulator.test.ts +++ b/test/server/AuthenticationRegulator.test.ts @@ -1,8 +1,8 @@ -import AuthenticationRegulator from "../../src/lib/AuthenticationRegulator"; -import UserDataStore from "../../src/lib/UserDataStore"; +import { AuthenticationRegulator } from "../../src/server/lib/AuthenticationRegulator"; +import UserDataStore from "../../src/server/lib/UserDataStore"; import MockDate = require("mockdate"); -import exceptions = require("../../src/lib/Exceptions"); +import exceptions = require("../../src/server/lib/Exceptions"); import nedb = require("nedb"); describe("test authentication regulator", function() { diff --git a/test/server/IdentityCheckMiddleware.test.ts b/test/server/IdentityCheckMiddleware.test.ts new file mode 100644 index 000000000..12d40e837 --- /dev/null +++ b/test/server/IdentityCheckMiddleware.test.ts @@ -0,0 +1,173 @@ + +import sinon = require("sinon"); +import IdentityValidator = require("../../src/server/lib/IdentityCheckMiddleware"); +import AuthenticationSession = require("../../src/server/lib/AuthenticationSession"); +import exceptions = require("../../src/server/lib/Exceptions"); +import assert = require("assert"); +import winston = require("winston"); +import Promise = require("bluebird"); +import express = require("express"); +import BluebirdPromise = require("bluebird"); + +import ExpressMock = require("./mocks/express"); +import UserDataStoreMock = require("./mocks/UserDataStore"); +import NotifierMock = require("./mocks/Notifier"); +import IdentityValidatorMock = require("./mocks/IdentityValidator"); +import ServerVariablesMock = require("./mocks/ServerVariablesMock"); + + +describe("test identity check process", function () { + let req: ExpressMock.RequestMock; + let res: ExpressMock.ResponseMock; + let userDataStore: UserDataStoreMock.UserDataStore; + let notifier: NotifierMock.NotifierMock; + let app: express.Application; + let app_get: sinon.SinonStub; + let app_post: sinon.SinonStub; + let identityValidable: IdentityValidatorMock.IdentityValidableMock; + + beforeEach(function () { + req = ExpressMock.RequestMock(); + res = ExpressMock.ResponseMock(); + + identityValidable = IdentityValidatorMock.IdentityValidableMock(); + + userDataStore = UserDataStoreMock.UserDataStore(); + userDataStore.issue_identity_check_token = sinon.stub(); + userDataStore.issue_identity_check_token.returns(Promise.resolve()); + userDataStore.consume_identity_check_token = sinon.stub(); + userDataStore.consume_identity_check_token.returns(Promise.resolve({ userid: "user" })); + + notifier = NotifierMock.NotifierMock(); + notifier.notify = sinon.stub().returns(Promise.resolve()); + + req.headers = {}; + req.session = {}; + req.session = {}; + + req.query = {}; + req.app = {}; + const mocks = ServerVariablesMock.mock(req.app); + mocks.logger = winston; + mocks.userDataStore = userDataStore; + mocks.notifier = notifier; + + 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", test_start_get_handler); + describe("test finish GET", test_finish_get_handler); + + function test_start_get_handler() { + it("should send 401 if pre validation initialization throws a first factor error", function () { + identityValidable.preValidationInit.returns(BluebirdPromise.reject(new exceptions.FirstFactorValidationError("Error during prevalidation"))); + const callback = IdentityValidator.get_start_validation(identityValidable, "/endpoint"); + + return callback(req as any, res as any, undefined) + .then(function () { return BluebirdPromise.reject("Should fail"); }) + .catch(function () { + assert.equal(res.status.getCall(0).args[0], 401); + }); + }); + + it("should send 400 if email is missing in provided identity", function () { + const identity = { userid: "abc" }; + + identityValidable.preValidationInit.returns(BluebirdPromise.resolve(identity)); + const callback = IdentityValidator.get_start_validation(identityValidable, "/endpoint"); + + return callback(req as any, res as any, undefined) + .then(function () { return BluebirdPromise.reject("Should fail"); }) + .catch(function () { + assert.equal(res.status.getCall(0).args[0], 400); + }); + }); + + it("should send 400 if userid is missing in provided identity", function () { + const endpoint = "/protected"; + const identity = { email: "abc@example.com" }; + + identityValidable.preValidationInit.returns(BluebirdPromise.resolve(identity)); + const callback = IdentityValidator.get_start_validation(identityValidable, "/endpoint"); + + return callback(req as any, res as any, undefined) + .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) + .catch(function (err: Error) { + assert.equal(res.status.getCall(0).args[0], 400); + return BluebirdPromise.resolve(); + }); + }); + + 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.preValidationInit.returns(BluebirdPromise.resolve(identity)); + const callback = IdentityValidator.get_start_validation(identityValidable, "/finish_endpoint"); + + return callback(req as any, res as any, undefined) + .then(function () { + assert(notifier.notify.calledOnce); + assert(userDataStore.issue_identity_check_token.calledOnce); + assert.equal(userDataStore.issue_identity_check_token.getCall(0).args[0], "user"); + assert.equal(userDataStore.issue_identity_check_token.getCall(0).args[3], 240000); + }); + }); + } + + function test_finish_get_handler() { + it("should send 403 if no identity_token is provided", function () { + + const callback = IdentityValidator.get_finish_validation(identityValidable); + + return callback(req as any, res as any, undefined) + .then(function () { return BluebirdPromise.reject("Should fail"); }) + .catch(function () { + assert.equal(res.status.getCall(0).args[0], 403); + }); + }); + + 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); + return callback(req as any, res as any, undefined); + }); + + it("should return 500 if identity_token is provided but invalid", function () { + req.query.identity_token = "token"; + + userDataStore.consume_identity_check_token + .returns(BluebirdPromise.reject(new Error("Invalid token"))); + + const callback = IdentityValidator.get_finish_validation(identityValidable); + return callback(req as any, res as any, undefined) + .then(function () { return BluebirdPromise.reject("Should fail"); }) + .catch(function () { + assert.equal(res.status.getCall(0).args[0], 500); + }); + }); + + it("should set the identity_check session object even if session does not exist yet", function () { + req.query.identity_token = "token"; + + req.session = {}; + const authSession = AuthenticationSession.get(req as any); + const callback = IdentityValidator.get_finish_validation(identityValidable); + return callback(req as any, res as any, undefined) + .then(function () { return BluebirdPromise.reject("Should fail"); }) + .catch(function () { + assert.equal(authSession.identity_check.userid, "user"); + return BluebirdPromise.resolve(); + }); + }); + } +}); diff --git a/test/unitary/LdapClient.test.ts b/test/server/LdapClient.test.ts similarity index 93% rename from test/unitary/LdapClient.test.ts rename to test/server/LdapClient.test.ts index 82f49c7cc..994c79439 100644 --- a/test/unitary/LdapClient.test.ts +++ b/test/server/LdapClient.test.ts @@ -1,6 +1,6 @@ -import LdapClient = require("../../src/lib/LdapClient"); -import { LdapConfiguration } from "../../src/lib/Configuration"; +import LdapClient = require("../../src/server/lib/LdapClient"); +import { LdapConfiguration } from "../../src/types/Configuration"; import sinon = require("sinon"); import BluebirdPromise = require("bluebird"); @@ -77,7 +77,7 @@ describe("test ldap validation", function () { ldap_client.bind.yields("wrong credentials"); const promise = test_bind(); return promise.catch(function () { - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); } @@ -106,7 +106,7 @@ describe("test ldap validation", function () { return ldap.get_emails("user") .then(function (emails) { assert.deepEqual(emails, [expected_doc.object.mail]); - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); @@ -116,7 +116,7 @@ describe("test ldap validation", function () { return ldap.get_emails("username") .then(function (emails) { assert.deepEqual(emails, ["user@example.com"]); - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); @@ -128,7 +128,7 @@ describe("test ldap validation", function () { return ldap.get_emails("user") .catch(function () { - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); } @@ -163,7 +163,7 @@ describe("test ldap validation", function () { return ldap.get_groups("user") .then(function (groups) { assert.deepEqual(groups, ["group1", "group2"]); - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); @@ -192,7 +192,7 @@ describe("test ldap validation", function () { ldap_client.search.yields("error"); return ldap.get_groups("user") .catch(function () { - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); } @@ -217,7 +217,7 @@ describe("test ldap validation", function () { const userPassword = ldap_client.modify.getCall(0).args[1].modification.userPassword; assert(/{SSHA}/.test(userPassword)); - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); @@ -227,7 +227,7 @@ describe("test ldap validation", function () { return ldap.update_password("user", "new-password") .catch(function () { - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); diff --git a/test/unitary/Server.test.ts b/test/server/Server.test.ts similarity index 70% rename from test/unitary/Server.test.ts rename to test/server/Server.test.ts index 15105b265..0fa64f037 100644 --- a/test/unitary/Server.test.ts +++ b/test/server/Server.test.ts @@ -1,15 +1,17 @@ -import Server from "../../src/lib/Server"; -import LdapClient = require("../../src/lib/LdapClient"); +import Server from "../../src/server/lib/Server"; +import LdapClient = require("../../src/server/lib/LdapClient"); -import Promise = require("bluebird"); +import BluebirdPromise = require("bluebird"); import speakeasy = require("speakeasy"); import request = require("request"); import nedb = require("nedb"); import { TOTPSecret } from "../../src/types/TOTPSecret"; +import U2FMock = require("./mocks/u2f"); +import Endpoints = require("../../src/server/endpoints"); -const requestp = Promise.promisifyAll(request) as request.RequestAsync; +const requestp = BluebirdPromise.promisifyAll(request) as typeof request; const assert = require("assert"); const sinon = require("sinon"); const MockDate = require("mockdate"); @@ -24,7 +26,7 @@ const requests = require("./requests")(PORT); describe("test the server", function () { let server: Server; let transporter: object; - let u2f: any; + let u2f: U2FMock.U2FMock; beforeEach(function () { const config = { @@ -62,12 +64,7 @@ describe("test the server", function () { }) }; - u2f = { - startRegistration: sinon.stub(), - finishRegistration: sinon.stub(), - startAuthentication: sinon.stub(), - finishAuthentication: sinon.stub() - }; + u2f = U2FMock.U2FMock(); transporter = { sendMail: sinon.stub().yields() @@ -120,79 +117,70 @@ describe("test the server", function () { server.stop(); }); - describe("test GET /login", function () { + describe("test GET " + Endpoints.FIRST_FACTOR_GET, function () { test_login(); }); - describe("test GET /logout", function () { + describe("test GET " + Endpoints.LOGOUT_GET, function () { test_logout(); }); - describe("test GET /reset-password-form", function () { + describe("test GET" + Endpoints.RESET_PASSWORD_REQUEST_GET, function () { test_reset_password_form(); }); - describe("test endpoints locks", function () { - function should_post_and_reply_with(url: string, status_code: number) { + describe("Second factor endpoints must be protected if first factor is not validated", function () { + function should_post_and_reply_with(url: string, status_code: number): BluebirdPromise { return requestp.postAsync(url).then(function (response: request.RequestResponse) { assert.equal(response.statusCode, status_code); - return Promise.resolve(); + return BluebirdPromise.resolve(); }); } - function should_get_and_reply_with(url: string, status_code: number) { + function should_get_and_reply_with(url: string, status_code: number): BluebirdPromise { return requestp.getAsync(url).then(function (response: request.RequestResponse) { assert.equal(response.statusCode, status_code); - return Promise.resolve(); + return BluebirdPromise.resolve(); }); } - function should_post_and_reply_with_403(url: string) { - return should_post_and_reply_with(url, 403); - } - function should_get_and_reply_with_403(url: string) { - return should_get_and_reply_with(url, 403); - } - - function should_post_and_reply_with_401(url: string) { + function should_post_and_reply_with_401(url: string): BluebirdPromise { return should_post_and_reply_with(url, 401); } - function should_get_and_reply_with_401(url: string) { + function should_get_and_reply_with_401(url: string): BluebirdPromise { return should_get_and_reply_with(url, 401); } - function should_get_and_post_reply_with_403(url: string) { - const p1 = should_post_and_reply_with_403(url); - const p2 = should_get_and_reply_with_403(url); - return Promise.all([p1, p2]); - } - - it("should block /new-password", function () { - return should_post_and_reply_with_403(BASE_URL + "/new-password"); + it("should block " + Endpoints.SECOND_FACTOR_GET, function () { + return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_GET); }); - it("should block /u2f-register", function () { - return should_get_and_post_reply_with_403(BASE_URL + "/u2f-register"); + it("should block " + Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, function () { + return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET); }); - it("should block /reset-password", function () { - return should_get_and_post_reply_with_403(BASE_URL + "/reset-password"); + it("should block " + Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, function () { + return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET + "?identity_token=dummy"); }); - it("should block /2ndfactor/u2f/register_request", function () { - return should_get_and_reply_with_403(BASE_URL + "/2ndfactor/u2f/register_request"); + it("should block " + Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, function () { + return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET); }); - it("should block /2ndfactor/u2f/register", function () { - return should_post_and_reply_with_403(BASE_URL + "/2ndfactor/u2f/register"); + it("should block " + Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, function () { + return should_post_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_REGISTER_POST); }); - it("should block /2ndfactor/u2f/sign_request", function () { - return should_get_and_reply_with_403(BASE_URL + "/2ndfactor/u2f/sign_request"); + it("should block " + Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, function () { + return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET); }); - it("should block /2ndfactor/u2f/sign", function () { - return should_post_and_reply_with_403(BASE_URL + "/2ndfactor/u2f/sign"); + it("should block " + Endpoints.SECOND_FACTOR_U2F_SIGN_POST, function () { + return should_post_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_POST); + }); + + it("should block " + Endpoints.SECOND_FACTOR_TOTP_POST, function () { + return should_post_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_TOTP_POST); }); }); @@ -204,7 +192,7 @@ describe("test the server", function () { function test_reset_password_form() { it("should serve the reset password form page", function (done) { - requestp.getAsync(BASE_URL + "/reset-password-form") + requestp.getAsync(BASE_URL + Endpoints.RESET_PASSWORD_REQUEST_GET) .then(function (response: request.RequestResponse) { assert.equal(response.statusCode, 200); done(); @@ -214,7 +202,7 @@ describe("test the server", function () { function test_login() { it("should serve the login page", function (done) { - requestp.getAsync(BASE_URL + "/login") + requestp.getAsync(BASE_URL + Endpoints.FIRST_FACTOR_GET) .then(function (response: request.RequestResponse) { assert.equal(response.statusCode, 200); done(); @@ -224,7 +212,7 @@ describe("test the server", function () { function test_logout() { it("should logout and redirect to /", function (done) { - requestp.getAsync(BASE_URL + "/logout") + requestp.getAsync(BASE_URL + Endpoints.LOGOUT_GET) .then(function (response: any) { assert.equal(response.req.path, "/"); done(); @@ -234,10 +222,10 @@ describe("test the server", function () { function test_authentication() { it("should return status code 401 when user is not authenticated", function () { - return requestp.getAsync({ url: BASE_URL + "/verify" }) + return requestp.getAsync({ url: BASE_URL + Endpoints.VERIFY_GET }) .then(function (response: request.RequestResponse) { assert.equal(response.statusCode, 401); - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); @@ -249,24 +237,23 @@ describe("test the server", function () { return requests.first_factor(j); }) .then(function (res: request.RequestResponse) { - assert.equal(res.statusCode, 204, "first factor failed"); + assert.equal(res.statusCode, 302, "first factor failed"); return requests.register_totp(j, transporter); }) - .then(function (secret: string) { - const sec = JSON.parse(secret) as TOTPSecret; + .then(function (base32_secret: string) { const real_token = speakeasy.totp({ - secret: sec.base32, + secret: base32_secret, encoding: "base32" }); return requests.totp(j, real_token); }) .then(function (res: request.RequestResponse) { - assert.equal(res.statusCode, 204, "second factor failed"); + assert.equal(res.statusCode, 200, "second factor failed"); return requests.verify(j); }) .then(function (res: request.RequestResponse) { assert.equal(res.statusCode, 204, "verify failed"); - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); @@ -295,7 +282,7 @@ describe("test the server", function () { }) .then(function (res: request.RequestResponse) { assert.equal(res.statusCode, 204, "verify failed"); - return Promise.resolve(); + return BluebirdPromise.resolve(); }) .catch(function (err: Error) { console.error(err); @@ -307,10 +294,9 @@ describe("test the server", function () { const sign_status = {}; const registration_request = {}; const registration_status = {}; - u2f.startRegistration.returns(Promise.resolve(sign_request)); - u2f.finishRegistration.returns(Promise.resolve(sign_status)); - u2f.startAuthentication.returns(Promise.resolve(registration_request)); - u2f.finishAuthentication.returns(Promise.resolve(registration_status)); + u2f.request.returns(BluebirdPromise.resolve(sign_request)); + u2f.checkRegistration.returns(BluebirdPromise.resolve(sign_status)); + u2f.checkSignature.returns(BluebirdPromise.resolve(registration_status)); const j = requestp.jar(); return requests.login(j) @@ -319,20 +305,22 @@ describe("test the server", function () { return requests.first_factor(j); }) .then(function (res: request.RequestResponse) { - assert.equal(res.statusCode, 204, "first factor failed"); + // console.log(res); + assert.equal(res.headers.location, Endpoints.SECOND_FACTOR_GET); + assert.equal(res.statusCode, 302, "first factor failed"); return requests.u2f_registration(j, transporter); }) .then(function (res: request.RequestResponse) { - assert.equal(res.statusCode, 204, "second factor, finish register failed"); + assert.equal(res.statusCode, 200, "second factor, finish register failed"); return requests.u2f_authentication(j); }) .then(function (res: request.RequestResponse) { - assert.equal(res.statusCode, 204, "second factor, finish sign failed"); + assert.equal(res.statusCode, 200, "second factor, finish sign failed"); return requests.verify(j); }) .then(function (res: request.RequestResponse) { assert.equal(res.statusCode, 204, "verify failed"); - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); } @@ -346,12 +334,13 @@ describe("test the server", function () { return requests.first_factor(j); }) .then(function (res: request.RequestResponse) { - assert.equal(res.statusCode, 204, "first factor failed"); + assert.equal(res.headers.location, Endpoints.SECOND_FACTOR_GET); + assert.equal(res.statusCode, 302, "first factor failed"); return requests.reset_password(j, transporter, "user", "new-password"); }) .then(function (res: request.RequestResponse) { assert.equal(res.statusCode, 204, "second factor, finish register failed"); - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); } @@ -384,7 +373,7 @@ describe("test the server", function () { }) .then(function (res: request.RequestResponse) { assert.equal(res.statusCode, 401, "first factor failed"); - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); } diff --git a/test/unitary/TOTPValidator.test.ts b/test/server/TOTPValidator.test.ts similarity index 91% rename from test/unitary/TOTPValidator.test.ts rename to test/server/TOTPValidator.test.ts index 84baa0408..e848e6947 100644 --- a/test/unitary/TOTPValidator.test.ts +++ b/test/server/TOTPValidator.test.ts @@ -1,5 +1,5 @@ -import TOTPValidator from "../../src/lib/TOTPValidator"; +import { TOTPValidator } from "../../src/server/lib/TOTPValidator"; import sinon = require("sinon"); import Promise = require("bluebird"); import SpeakeasyMock = require("./mocks/speakeasy"); diff --git a/test/unitary/UserDataStore.test.ts b/test/server/UserDataStore.test.ts similarity index 88% rename from test/unitary/UserDataStore.test.ts rename to test/server/UserDataStore.test.ts index a7ce7dd99..8c7b8128d 100644 --- a/test/unitary/UserDataStore.test.ts +++ b/test/server/UserDataStore.test.ts @@ -1,6 +1,6 @@ -import UserDataStore from "../../src/lib/UserDataStore"; -import { U2FMetaDocument, Options } from "../../src/lib/UserDataStore"; +import UserDataStore from "../../src/server/lib/UserDataStore"; +import { U2FRegistrationDocument, Options } from "../../src/server/lib/UserDataStore"; import nedb = require("nedb"); import assert = require("assert"); @@ -24,11 +24,10 @@ describe("test user data store", () => { const userid = "user"; const app_id = "https://localhost"; - const meta = { - publicKey: "pbk" - }; + const keyHandle = "keyhandle"; + const publicKey = "publicKey"; - return data_store.set_u2f_meta(userid, app_id, meta) + return data_store.set_u2f_meta(userid, app_id, keyHandle, publicKey) .then(function (numUpdated) { assert.equal(1, numUpdated); return Promise.resolve(); @@ -64,19 +63,19 @@ describe("test user data store", () => { const userid = "user"; const app_id = "https://localhost"; - const meta = { - publicKey: "pbk" - }; + const keyHandle = "keyHandle"; + const publicKey = "publicKey"; - return data_store.set_u2f_meta(userid, app_id, meta) + return data_store.set_u2f_meta(userid, app_id, keyHandle, publicKey) .then(function (numUpdated: number) { assert.equal(1, numUpdated); return data_store.get_u2f_meta(userid, app_id); }) - .then(function (doc: U2FMetaDocument) { - assert.deepEqual(meta, doc.meta); - assert.deepEqual(userid, doc.userid); - assert.deepEqual(app_id, doc.appid); + .then(function (doc: U2FRegistrationDocument) { + assert.deepEqual(keyHandle, doc.keyHandle); + assert.deepEqual(publicKey, doc.publicKey); + assert.deepEqual(userid, doc.userId); + assert.deepEqual(app_id, doc.appId); assert("_id" in doc); return Promise.resolve(); }); diff --git a/test/unitary/access_control/AccessController.test.ts b/test/server/access_control/AccessController.test.ts similarity index 92% rename from test/unitary/access_control/AccessController.test.ts rename to test/server/access_control/AccessController.test.ts index 9af322277..5e5f5e3ff 100644 --- a/test/unitary/access_control/AccessController.test.ts +++ b/test/server/access_control/AccessController.test.ts @@ -1,8 +1,8 @@ import assert = require("assert"); import winston = require("winston"); -import AccessController from "../../../src/lib/access_control/AccessController"; -import { ACLConfiguration } from "../../../src/lib/Configuration"; +import { AccessController } from "../../../src/server/lib/access_control/AccessController"; +import { ACLConfiguration } from "../../../src/types/Configuration"; describe("test access control manager", function () { let accessController: AccessController; diff --git a/test/unitary/access_control/PatternBuilder.test.ts b/test/server/access_control/PatternBuilder.test.ts similarity index 96% rename from test/unitary/access_control/PatternBuilder.test.ts rename to test/server/access_control/PatternBuilder.test.ts index a563556c6..391919fb7 100644 --- a/test/unitary/access_control/PatternBuilder.test.ts +++ b/test/server/access_control/PatternBuilder.test.ts @@ -2,8 +2,8 @@ import assert = require("assert"); import winston = require("winston"); -import PatternBuilder from "../../../src/lib/access_control/PatternBuilder"; -import { ACLConfiguration } from "../../../src/lib/Configuration"; +import PatternBuilder from "../../../src/server/lib/access_control/PatternBuilder"; +import { ACLConfiguration } from "../../../src/types/Configuration"; describe("test access control manager", function () { describe("test access control pattern builder when no configuration is provided", () => { diff --git a/test/unitary/config_adapter.test.ts b/test/server/config_adapter.test.ts similarity index 95% rename from test/unitary/config_adapter.test.ts rename to test/server/config_adapter.test.ts index 0c8a651eb..9c27f43af 100644 --- a/test/unitary/config_adapter.test.ts +++ b/test/server/config_adapter.test.ts @@ -1,6 +1,6 @@ import * as Assert from "assert"; -import { UserConfiguration } from "../../src/lib/Configuration"; -import ConfigurationAdapter from "../../src/lib/ConfigurationAdapter"; +import { UserConfiguration } from "../../src/types/Configuration"; +import ConfigurationAdapter from "../../src/server/lib/ConfigurationAdapter"; describe("test config adapter", function() { function build_yaml_config(): UserConfiguration { diff --git a/test/unitary/data_persistence.test.ts b/test/server/data_persistence.test.ts similarity index 78% rename from test/unitary/data_persistence.test.ts rename to test/server/data_persistence.test.ts index 1e7218727..1beac313d 100644 --- a/test/unitary/data_persistence.test.ts +++ b/test/server/data_persistence.test.ts @@ -1,14 +1,15 @@ -import * as Promise from "bluebird"; +import * as BluebirdPromise from "bluebird"; import * as request from "request"; -import Server from "../../src/lib/Server"; -import { UserConfiguration } from "../../src/lib/Configuration"; +import Server from "../../src/server/lib/Server"; +import { UserConfiguration } from "../../src/types/Configuration"; import { GlobalDependencies } from "../../src/types/Dependencies"; import * as tmp from "tmp"; +import U2FMock = require("./mocks/u2f"); -const requestp = Promise.promisifyAll(request) as request.Request; +const requestp = BluebirdPromise.promisifyAll(request) as request.Request; const assert = require("assert"); const speakeasy = require("speakeasy"); const sinon = require("sinon"); @@ -20,7 +21,7 @@ const PORT = 8050; const requests = require("./requests")(PORT); describe("test data persistence", function () { - let u2f: any; + let u2f: U2FMock.U2FMock; let tmpDir: tmp.SynchrounousResult; const ldap_client = { bind: sinon.stub(), @@ -36,12 +37,7 @@ describe("test data persistence", function () { let config: UserConfiguration; before(function () { - u2f = { - startRegistration: sinon.stub(), - finishRegistration: sinon.stub(), - startAuthentication: sinon.stub(), - finishAuthentication: sinon.stub() - }; + u2f = U2FMock.U2FMock(); const search_doc = { object: { @@ -92,12 +88,10 @@ describe("test data persistence", function () { let server: Server; const sign_request = {}; const sign_status = {}; - const registration_request = {}; const registration_status = {}; - u2f.startRegistration.returns(Promise.resolve(sign_request)); - u2f.finishRegistration.returns(Promise.resolve(sign_status)); - u2f.startAuthentication.returns(Promise.resolve(registration_request)); - u2f.finishAuthentication.returns(Promise.resolve(registration_status)); + u2f.request.returns(sign_request); + u2f.checkRegistration.returns(sign_status); + u2f.checkSignature.returns(registration_status); const nodemailer = { createTransport: sinon.spy(function () { @@ -152,18 +146,18 @@ describe("test data persistence", function () { return requests.u2f_authentication(j2); }) .then(function (res) { - assert.equal(204, res.statusCode); + assert.equal(200, res.statusCode); server.stop(); - return Promise.resolve(); + return BluebirdPromise.resolve(); }) .catch(function (err) { console.error(err); - return Promise.reject(err); + return BluebirdPromise.reject(err); }); }); - function start_server(config: UserConfiguration, deps: GlobalDependencies): Promise { - return new Promise(function (resolve, reject) { + function start_server(config: UserConfiguration, deps: GlobalDependencies): BluebirdPromise { + return new BluebirdPromise(function (resolve, reject) { const s = new Server(); s.start(config, deps); resolve(s); @@ -171,7 +165,7 @@ describe("test data persistence", function () { } function stop_server(s: Server) { - return new Promise(function (resolve, reject) { + return new BluebirdPromise(function (resolve, reject) { s.stop(); resolve(); }); diff --git a/test/unitary/mocks/AccessController.ts b/test/server/mocks/AccessController.ts similarity index 100% rename from test/unitary/mocks/AccessController.ts rename to test/server/mocks/AccessController.ts diff --git a/test/unitary/mocks/AuthenticationRegulator.ts b/test/server/mocks/AuthenticationRegulator.ts similarity index 100% rename from test/unitary/mocks/AuthenticationRegulator.ts rename to test/server/mocks/AuthenticationRegulator.ts diff --git a/test/unitary/mocks/IdentityValidator.ts b/test/server/mocks/IdentityValidator.ts similarity index 58% rename from test/unitary/mocks/IdentityValidator.ts rename to test/server/mocks/IdentityValidator.ts index fd3417706..bd8dcd7e4 100644 --- a/test/unitary/mocks/IdentityValidator.ts +++ b/test/server/mocks/IdentityValidator.ts @@ -1,6 +1,6 @@ import sinon = require("sinon"); -import { IdentityValidable } from "../../../src/lib/IdentityValidator"; +import { IdentityValidable } from "../../../src/server/lib/IdentityCheckMiddleware"; import express = require("express"); import BluebirdPromise = require("bluebird"); import { Identity } from "../../../src/types/Identity"; @@ -8,16 +8,20 @@ import { Identity } from "../../../src/types/Identity"; export interface IdentityValidableMock { challenge: sinon.SinonStub; - templateName: sinon.SinonStub; - preValidation: sinon.SinonStub; + preValidationInit: sinon.SinonStub; + preValidationResponse: sinon.SinonStub | sinon.SinonSpy; + postValidationInit: sinon.SinonStub; + postValidationResponse: sinon.SinonStub | sinon.SinonSpy; mailSubject: sinon.SinonStub; } export function IdentityValidableMock() { return { challenge: sinon.stub(), - templateName: sinon.stub(), - preValidation: sinon.stub(), + preValidationInit: sinon.stub(), + preValidationResponse: sinon.stub(), + postValidationInit: sinon.stub(), + postValidationResponse: sinon.stub(), mailSubject: sinon.stub() }; } diff --git a/test/unitary/mocks/LdapClient.ts b/test/server/mocks/LdapClient.ts similarity index 100% rename from test/unitary/mocks/LdapClient.ts rename to test/server/mocks/LdapClient.ts diff --git a/test/unitary/mocks/Notifier.ts b/test/server/mocks/Notifier.ts similarity index 100% rename from test/unitary/mocks/Notifier.ts rename to test/server/mocks/Notifier.ts diff --git a/test/server/mocks/ServerVariablesMock.ts b/test/server/mocks/ServerVariablesMock.ts new file mode 100644 index 000000000..b5dda7daa --- /dev/null +++ b/test/server/mocks/ServerVariablesMock.ts @@ -0,0 +1,34 @@ +import sinon = require("sinon"); +import express = require("express"); +import {  ServerVariables, VARIABLES_KEY }  from "../../../src/server/lib/ServerVariables"; + +export interface ServerVariablesMock { + logger: any; + ldap: any; + totpValidator: any; + totpGenerator: any; + u2f: any; + userDataStore: any; + notifier: any; + regulator: any; + config: any; + accessController: any; +} + + +export function mock(app: express.Application): ServerVariablesMock { + const mocks: ServerVariablesMock = { + accessController: sinon.stub(), + config: sinon.stub(), + ldap: sinon.stub(), + logger: sinon.stub(), + notifier: sinon.stub(), + regulator: sinon.stub(), + totpGenerator: sinon.stub(), + totpValidator: sinon.stub(), + u2f: sinon.stub(), + userDataStore: sinon.stub() + }; + app.get = sinon.stub().withArgs(VARIABLES_KEY).returns(mocks); + return mocks; +} \ No newline at end of file diff --git a/test/unitary/mocks/TOTPValidator.ts b/test/server/mocks/TOTPValidator.ts similarity index 100% rename from test/unitary/mocks/TOTPValidator.ts rename to test/server/mocks/TOTPValidator.ts diff --git a/test/unitary/mocks/UserDataStore.ts b/test/server/mocks/UserDataStore.ts similarity index 100% rename from test/unitary/mocks/UserDataStore.ts rename to test/server/mocks/UserDataStore.ts diff --git a/test/unitary/mocks/express.ts b/test/server/mocks/express.ts similarity index 98% rename from test/unitary/mocks/express.ts rename to test/server/mocks/express.ts index daa3e1703..b2adda983 100644 --- a/test/unitary/mocks/express.ts +++ b/test/server/mocks/express.ts @@ -32,7 +32,7 @@ export interface ResponseMock { clearCookie: sinon.SinonStub; cookie: sinon.SinonStub; location: sinon.SinonStub; - redirect: sinon.SinonStub; + redirect: sinon.SinonStub | sinon.SinonSpy; render: sinon.SinonStub | sinon.SinonSpy; locals: sinon.SinonStub; charset: string; diff --git a/test/unitary/mocks/ldapjs.ts b/test/server/mocks/ldapjs.ts similarity index 100% rename from test/unitary/mocks/ldapjs.ts rename to test/server/mocks/ldapjs.ts diff --git a/test/unitary/mocks/nodemailer.ts b/test/server/mocks/nodemailer.ts similarity index 100% rename from test/unitary/mocks/nodemailer.ts rename to test/server/mocks/nodemailer.ts diff --git a/test/unitary/mocks/speakeasy.ts b/test/server/mocks/speakeasy.ts similarity index 100% rename from test/unitary/mocks/speakeasy.ts rename to test/server/mocks/speakeasy.ts diff --git a/test/server/mocks/u2f.ts b/test/server/mocks/u2f.ts new file mode 100644 index 000000000..234b28c11 --- /dev/null +++ b/test/server/mocks/u2f.ts @@ -0,0 +1,16 @@ + +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/test/unitary/notifiers/FileSystemNotifier.test.ts b/test/server/notifiers/FileSystemNotifier.test.ts similarity index 84% rename from test/unitary/notifiers/FileSystemNotifier.test.ts rename to test/server/notifiers/FileSystemNotifier.test.ts index b5197157c..add77dcc6 100644 --- a/test/unitary/notifiers/FileSystemNotifier.test.ts +++ b/test/server/notifiers/FileSystemNotifier.test.ts @@ -1,9 +1,10 @@ import * as sinon from "sinon"; import * as assert from "assert"; -import { FileSystemNotifier } from "../../../src/lib/notifiers/FileSystemNotifier"; +import { FileSystemNotifier } from "../../../src/server/lib/notifiers/FileSystemNotifier"; import * as tmp from "tmp"; import * as fs from "fs"; +import BluebirdPromise = require("bluebird"); const NOTIFICATIONS_DIRECTORY = "notifications"; @@ -36,7 +37,7 @@ describe("test FS notifier", function() { .then(function() { const content = fs.readFileSync(options.filename, "UTF-8"); assert(content.length > 0); - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); }); diff --git a/test/unitary/notifiers/GMailNotifier.test.ts b/test/server/notifiers/GMailNotifier.test.ts similarity index 87% rename from test/unitary/notifiers/GMailNotifier.test.ts rename to test/server/notifiers/GMailNotifier.test.ts index feaae4792..c9deb8fef 100644 --- a/test/unitary/notifiers/GMailNotifier.test.ts +++ b/test/server/notifiers/GMailNotifier.test.ts @@ -1,8 +1,9 @@ import * as sinon from "sinon"; import * as assert from "assert"; +import BluebirdPromise = require("bluebird"); import NodemailerMock = require("../mocks/nodemailer"); -import GMailNotifier = require("../../../src/lib/notifiers/GMailNotifier"); +import GMailNotifier = require("../../../src/server/lib/notifiers/GMailNotifier"); describe("test gmail notifier", function () { @@ -34,7 +35,7 @@ describe("test gmail notifier", function () { assert.equal(nodemailerMock.createTransport.getCall(0).args[0].auth.pass, "pass_gmail"); assert.equal(transporter.sendMail.getCall(0).args[0].to, "user@example.com"); assert.equal(transporter.sendMail.getCall(0).args[0].subject, "subject"); - return Promise.resolve(); + return BluebirdPromise.resolve(); }); }); }); diff --git a/test/unitary/notifiers/NotifierFactory.test.ts b/test/server/notifiers/NotifierFactory.test.ts similarity index 77% rename from test/unitary/notifiers/NotifierFactory.test.ts rename to test/server/notifiers/NotifierFactory.test.ts index d327a9ba9..bf6c79a5f 100644 --- a/test/unitary/notifiers/NotifierFactory.test.ts +++ b/test/server/notifiers/NotifierFactory.test.ts @@ -3,9 +3,9 @@ import * as sinon from "sinon"; import * as BluebirdPromise from "bluebird"; import * as assert from "assert"; -import { NotifierFactory } from "../../../src/lib/notifiers/NotifierFactory"; -import { GMailNotifier } from "../../../src/lib/notifiers/GMailNotifier"; -import { FileSystemNotifier } from "../../../src/lib/notifiers/FileSystemNotifier"; +import { NotifierFactory } from "../../../src/server/lib/notifiers/NotifierFactory"; +import { GMailNotifier } from "../../../src/server/lib/notifiers/GMailNotifier"; +import { FileSystemNotifier } from "../../../src/server/lib/notifiers/FileSystemNotifier"; import NodemailerMock = require("../mocks/nodemailer"); diff --git a/test/unitary/requests.ts b/test/server/requests.ts similarity index 70% rename from test/unitary/requests.ts rename to test/server/requests.ts index 221f4b37f..a7837436e 100644 --- a/test/unitary/requests.ts +++ b/test/server/requests.ts @@ -4,36 +4,37 @@ import request = require("request"); import assert = require("assert"); import express = require("express"); import nodemailer = require("nodemailer"); +import Endpoints = require("../../src/server/endpoints"); import NodemailerMock = require("./mocks/nodemailer"); -const requestAsync = BluebirdPromise.promisifyAll(request) as request.RequestAsync; +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_reset_password(jar: request.CookieJar, transporter: NodemailerMock.NodemailerTransporterMock, user: string, new_password: string) { - return requestAsync.postAsync({ - url: BASE_URL + "/reset-password", + return requestAsync.getAsync({ + url: BASE_URL + Endpoints.RESET_PASSWORD_IDENTITY_START_GET, jar: jar, - form: { userid: user } + qs: { userid: user } }) .then(function (res: request.RequestResponse) { - assert.equal(res.statusCode, 204); + assert.equal(res.statusCode, 200); const html_content = transporter.sendMail.getCall(0).args[0].html; const regexp = /identity_token=([a-zA-Z0-9]+)/; const token = regexp.exec(html_content)[1]; // console.log(html_content, token); return requestAsync.getAsync({ - url: BASE_URL + "/reset-password?identity_token=" + token, + url: BASE_URL + Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET + "?identity_token=" + token, jar: jar }); }) .then(function (res: request.RequestResponse) { assert.equal(res.statusCode, 200); return requestAsync.postAsync({ - url: BASE_URL + "/new-password", + url: BASE_URL + Endpoints.RESET_PASSWORD_FORM_POST, jar: jar, form: { password: new_password @@ -43,39 +44,31 @@ export = function (port: number) { } function execute_register_totp(jar: request.CookieJar, transporter: NodemailerMock.NodemailerTransporterMock) { - return requestAsync.postAsync({ - url: BASE_URL + "/totp-register", + return requestAsync.getAsync({ + url: BASE_URL + Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, jar: jar }) .then(function (res: request.RequestResponse) { - assert.equal(res.statusCode, 204); + assert.equal(res.statusCode, 200); const html_content = transporter.sendMail.getCall(0).args[0].html; const regexp = /identity_token=([a-zA-Z0-9]+)/; const token = regexp.exec(html_content)[1]; - // console.log(html_content, token); return requestAsync.getAsync({ - url: BASE_URL + "/totp-register?identity_token=" + token, + url: BASE_URL + Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET + "?identity_token=" + token, jar: jar }); }) .then(function (res: request.RequestResponse) { assert.equal(res.statusCode, 200); - return requestAsync.postAsync({ - url: BASE_URL + "/new-totp-secret", - jar: jar, - }); - }) - .then(function (res: request.RequestResponse) { - console.log(res.statusCode); - console.log(res.body); - assert.equal(res.statusCode, 200); - return Promise.resolve(res.body); + const regex = /

    ([A-Z0-9]+)<\/p>/g; + const secret = regex.exec(res.body); + return BluebirdPromise.resolve(secret[1]); }); } function execute_totp(jar: request.CookieJar, token: string) { return requestAsync.postAsync({ - url: BASE_URL + "/2ndfactor/totp", + url: BASE_URL + Endpoints.SECOND_FACTOR_TOTP_POST, jar: jar, form: { token: token @@ -85,13 +78,13 @@ export = function (port: number) { function execute_u2f_authentication(jar: request.CookieJar) { return requestAsync.getAsync({ - url: BASE_URL + "/2ndfactor/u2f/sign_request", + 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 + "/2ndfactor/u2f/sign", + url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_POST, jar: jar, form: { } @@ -100,40 +93,40 @@ export = function (port: number) { } function execute_verification(jar: request.CookieJar) { - return requestAsync.getAsync({ url: BASE_URL + "/verify", jar: jar }); + return requestAsync.getAsync({ url: BASE_URL + Endpoints.VERIFY_GET, jar: jar }); } function execute_login(jar: request.CookieJar) { - return requestAsync.getAsync({ url: BASE_URL + "/login", jar: jar }); + return requestAsync.getAsync({ url: BASE_URL + Endpoints.FIRST_FACTOR_GET, jar: jar }); } function execute_u2f_registration(jar: request.CookieJar, transporter: NodemailerMock.NodemailerTransporterMock) { - return requestAsync.postAsync({ - url: BASE_URL + "/u2f-register", + return requestAsync.getAsync({ + url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, jar: jar }) .then(function (res: request.RequestResponse) { - assert.equal(res.statusCode, 204); + assert.equal(res.statusCode, 200); const html_content = transporter.sendMail.getCall(0).args[0].html; const regexp = /identity_token=([a-zA-Z0-9]+)/; const token = regexp.exec(html_content)[1]; // console.log(html_content, token); return requestAsync.getAsync({ - url: BASE_URL + "/u2f-register?identity_token=" + token, + url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET + "?identity_token=" + token, jar: jar }); }) .then(function (res: request.RequestResponse) { assert.equal(res.statusCode, 200); return requestAsync.getAsync({ - url: BASE_URL + "/2ndfactor/u2f/register_request", + url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, jar: jar, }); }) .then(function (res: request.RequestResponse) { assert.equal(res.statusCode, 200); return requestAsync.postAsync({ - url: BASE_URL + "/2ndfactor/u2f/register", + url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, jar: jar, form: { s: "test" @@ -144,7 +137,7 @@ export = function (port: number) { function execute_first_factor(jar: request.CookieJar) { return requestAsync.postAsync({ - url: BASE_URL + "/1stfactor", + url: BASE_URL + Endpoints.FIRST_FACTOR_POST, jar: jar, form: { username: "test_ok", @@ -155,7 +148,7 @@ export = function (port: number) { function execute_failing_first_factor(jar: request.CookieJar) { return requestAsync.postAsync({ - url: BASE_URL + "/1stfactor", + url: BASE_URL + Endpoints.FIRST_FACTOR_POST, jar: jar, form: { username: "test_nok", diff --git a/test/unitary/routes/FirstFactor.test.ts b/test/server/routes/firstfactor/post.test.ts similarity index 57% rename from test/unitary/routes/FirstFactor.test.ts rename to test/server/routes/firstfactor/post.test.ts index 0ee5b07e7..e38bbec00 100644 --- a/test/unitary/routes/FirstFactor.test.ts +++ b/test/server/routes/firstfactor/post.test.ts @@ -4,12 +4,16 @@ import BluebirdPromise = require("bluebird"); import assert = require("assert"); import winston = require("winston"); -import FirstFactor = require("../../../src/lib/routes/FirstFactor"); -import exceptions = require("../../../src/lib/Exceptions"); -import AuthenticationRegulatorMock = require("../mocks/AuthenticationRegulator"); -import AccessControllerMock = require("../mocks/AccessController"); -import { LdapClientMock } from "../mocks/LdapClient"; -import ExpressMock = require("../mocks/express"); +import FirstFactorPost = require("../../../../src/server/lib/routes/firstfactor/post"); +import exceptions = require("../../../../src/server/lib/Exceptions"); +import AuthenticationSession = require("../../../../src/server/lib/AuthenticationSession"); +import Endpoints = require("../../../../src/server/endpoints"); + +import AuthenticationRegulatorMock = require("../../mocks/AuthenticationRegulator"); +import AccessControllerMock = require("../../mocks/AccessController"); +import { LdapClientMock } from "../../mocks/LdapClient"; +import ExpressMock = require("../../mocks/express"); +import ServerVariablesMock = require("../../mocks/ServerVariablesMock"); describe("test the first factor validation route", function () { let req: ExpressMock.RequestMock; @@ -41,65 +45,59 @@ describe("test the first factor validation route", function () { regulator.regulate.returns(BluebirdPromise.resolve()); regulator.mark.returns(BluebirdPromise.resolve()); - const app_get = sinon.stub(); - app_get.withArgs("ldap").returns(ldapMock); - app_get.withArgs("configuration").returns(configuration); - app_get.withArgs("logger").returns(winston); - app_get.withArgs("authentication regulator").returns(regulator); - app_get.withArgs("access controller").returns(accessController); - req = { app: { - get: app_get }, body: { username: "username", password: "password" }, session: { - auth_session: { - FirstFactor: false, - second_factor: false - } }, headers: { host: "home.example.com" } }; + + AuthenticationSession.reset(req as any); + + const mocks = ServerVariablesMock.mock(req.app); + mocks.ldap = ldapMock; + mocks.config = configuration; + mocks.logger = winston; + mocks.regulator = regulator; + mocks.accessController = accessController; + res = ExpressMock.ResponseMock(); }); - it("should return status code 204 when LDAP binding succeeds", function () { - return new Promise(function (resolve, reject) { - res.send = sinon.spy(function () { - assert.equal("username", req.session.auth_session.userid); - assert.equal(204, res.status.getCall(0).args[0]); - resolve(); + it("should redirect client to second factor page", function () { + ldapMock.bind.withArgs("username").returns(BluebirdPromise.resolve()); + ldapMock.get_emails.returns(BluebirdPromise.resolve(emails)); + const authSession = AuthenticationSession.get(req as any); + return FirstFactorPost.default(req as any, res as any) + .then(function () { + assert.equal("username", authSession.userid); + assert.equal(Endpoints.SECOND_FACTOR_GET, res.redirect.getCall(0).args[0]); }); - ldapMock.bind.withArgs("username").returns(BluebirdPromise.resolve()); - ldapMock.get_emails.returns(BluebirdPromise.resolve(emails)); - FirstFactor(req as any, res as any); - }); }); it("should retrieve email from LDAP", function (done) { - res.send = sinon.spy(function () { done(); }); + res.redirect = sinon.spy(function () { done(); }); ldapMock.bind.returns(BluebirdPromise.resolve()); ldapMock.get_emails = sinon.stub().withArgs("username").returns(BluebirdPromise.resolve([{ mail: ["test@example.com"] }])); - FirstFactor(req as any, res as any); + FirstFactorPost.default(req as any, res as any); }); it("should set email as session variables", function () { - return new Promise(function (resolve, reject) { - res.send = sinon.spy(function () { - assert.equal("test_ok@example.com", req.session.auth_session.email); - resolve(); + const emails = ["test_ok@example.com"]; + const authSession = AuthenticationSession.get(req as any); + ldapMock.bind.returns(BluebirdPromise.resolve()); + ldapMock.get_emails.returns(BluebirdPromise.resolve(emails)); + return FirstFactorPost.default(req as any, res as any) + .then(function () { + assert.equal("test_ok@example.com", authSession.email); }); - const emails = ["test_ok@example.com"]; - ldapMock.bind.returns(BluebirdPromise.resolve()); - ldapMock.get_emails.returns(BluebirdPromise.resolve(emails)); - FirstFactor(req as any, res as any); - }); }); it("should return status code 401 when LDAP binding throws", function (done) { @@ -109,7 +107,7 @@ describe("test the first factor validation route", function () { done(); }); ldapMock.bind.returns(BluebirdPromise.reject(new exceptions.LdapBindError("Bad credentials"))); - FirstFactor(req as any, res as any); + FirstFactorPost.default(req as any, res as any); }); it("should return status code 500 when LDAP search throws", function (done) { @@ -118,8 +116,8 @@ describe("test the first factor validation route", function () { done(); }); ldapMock.bind.returns(BluebirdPromise.resolve()); - ldapMock.get_emails.returns(BluebirdPromise.reject(new exceptions.LdapSeachError("error while retrieving emails"))); - FirstFactor(req as any, res as any); + ldapMock.get_emails.returns(BluebirdPromise.reject(new exceptions.LdapSearchError("error while retrieving emails"))); + FirstFactorPost.default(req as any, res as any); }); it("should return status code 403 when regulator rejects authentication", function (done) { @@ -132,7 +130,7 @@ describe("test the first factor validation route", function () { }); ldapMock.bind.returns(BluebirdPromise.resolve()); ldapMock.get_emails.returns(BluebirdPromise.resolve()); - FirstFactor(req as any, res as any); + FirstFactorPost.default(req as any, res as any); }); }); diff --git a/test/server/routes/password-reset/identity/PasswordResetHandler.test.ts b/test/server/routes/password-reset/identity/PasswordResetHandler.test.ts new file mode 100644 index 000000000..3b90b8938 --- /dev/null +++ b/test/server/routes/password-reset/identity/PasswordResetHandler.test.ts @@ -0,0 +1,110 @@ + +import PasswordResetHandler from "../../../../../src/server/lib/routes/password-reset/identity/PasswordResetHandler"; +import LdapClient = require("../../../../../src/server/lib/LdapClient"); +import sinon = require("sinon"); +import winston = require("winston"); +import assert = require("assert"); +import BluebirdPromise = require("bluebird"); + +import ExpressMock = require("../../../mocks/express"); +import { LdapClientMock } from "../../../mocks/LdapClient"; +import { UserDataStore } from "../../../mocks/UserDataStore"; +import ServerVariablesMock = require("../../../mocks/ServerVariablesMock"); + +describe("test reset password identity check", function () { + let req: ExpressMock.RequestMock; + let res: ExpressMock.ResponseMock; + let user_data_store: UserDataStore; + let ldap_client: LdapClientMock; + let configuration: any; + + beforeEach(function () { + req = { + query: { + userid: "user" + }, + app: { + get: sinon.stub() + }, + session: { + auth_session: { + userid: "user", + email: "user@example.com", + first_factor: true, + second_factor: false + } + }, + headers: { + host: "localhost" + } + }; + + const options = { + inMemoryOnly: true + }; + + const mocks = ServerVariablesMock.mock(req.app); + + + user_data_store = UserDataStore(); + user_data_store.set_u2f_meta.returns(BluebirdPromise.resolve({})); + user_data_store.get_u2f_meta.returns(BluebirdPromise.resolve({})); + user_data_store.issue_identity_check_token.returns(BluebirdPromise.resolve({})); + user_data_store.consume_identity_check_token.returns(BluebirdPromise.resolve({})); + mocks.userDataStore = user_data_store; + + + configuration = { + ldap: { + base_dn: "dc=example,dc=com", + user_name_attribute: "cn" + } + }; + + mocks.logger = winston; + mocks.config = configuration; + + ldap_client = LdapClientMock(); + mocks.ldap = ldap_client; + + 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(); + 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 (done) { + ldap_client.get_emails.returns(BluebirdPromise.reject("Internal error")); + new PasswordResetHandler().preValidationInit(req as any) + .catch(function (err: Error) { + done(); + }); + }); + + it("should perform a search in ldap to find email address", function (done) { + configuration.ldap.user_name_attribute = "uid"; + ldap_client.get_emails.returns(BluebirdPromise.resolve([])); + new PasswordResetHandler().preValidationInit(req as any) + .then(function () { + assert.equal("user", ldap_client.get_emails.getCall(0).args[0]); + done(); + }); + }); + + it("should returns identity when ldap replies", function (done) { + ldap_client.get_emails.returns(BluebirdPromise.resolve(["test@example.com"])); + new PasswordResetHandler().preValidationInit(req as any) + .then(function () { + done(); + }); + }); + }); +}); diff --git a/test/server/routes/password-reset/post.test.ts b/test/server/routes/password-reset/post.test.ts new file mode 100644 index 000000000..9548998d8 --- /dev/null +++ b/test/server/routes/password-reset/post.test.ts @@ -0,0 +1,123 @@ + +import PasswordResetFormPost = require("../../../../src/server/lib/routes/password-reset/form/post"); +import LdapClient = require("../../../../src/server/lib/LdapClient"); +import AuthenticationSession = require("../../../../src/server/lib/AuthenticationSession"); +import sinon = require("sinon"); +import winston = require("winston"); +import assert = require("assert"); +import BluebirdPromise = require("bluebird"); + +import ExpressMock = require("../../mocks/express"); +import { LdapClientMock } from "../../mocks/LdapClient"; +import { UserDataStore } from "../../mocks/UserDataStore"; +import ServerVariablesMock = require("../../mocks/ServerVariablesMock"); + +describe("test reset password route", function () { + let req: ExpressMock.RequestMock; + let res: ExpressMock.ResponseMock; + let user_data_store: UserDataStore; + let ldap_client: LdapClientMock; + let configuration: any; + let authSession: AuthenticationSession.AuthenticationSession; + + beforeEach(function () { + req = { + body: { + userid: "user" + }, + app: { + get: sinon.stub() + }, + session: {}, + headers: { + host: "localhost" + } + }; + + AuthenticationSession.reset(req as any); + authSession = AuthenticationSession.get(req as any); + authSession.userid = "user"; + authSession.email = "user@example.com"; + authSession.first_factor = true; + authSession.second_factor = false; + + const options = { + inMemoryOnly: true + }; + + const mocks = ServerVariablesMock.mock(req.app); + user_data_store = UserDataStore(); + user_data_store.set_u2f_meta.returns(BluebirdPromise.resolve({})); + user_data_store.get_u2f_meta.returns(BluebirdPromise.resolve({})); + user_data_store.issue_identity_check_token.returns(BluebirdPromise.resolve({})); + user_data_store.consume_identity_check_token.returns(BluebirdPromise.resolve({})); + mocks.userDataStore = user_data_store; + + + configuration = { + ldap: { + base_dn: "dc=example,dc=com", + user_name_attribute: "cn" + } + }; + + mocks.logger = winston; + mocks.config = configuration; + + ldap_client = LdapClientMock(); + mocks.ldap = ldap_client; + + res = ExpressMock.ResponseMock(); + }); + + describe("test reset password post", () => { + it("should update the password and reset auth_session for reauthentication", function () { + authSession.identity_check = { + userid: "user", + challenge: "reset-password" + }; + req.body = {}; + req.body.password = "new-password"; + + ldap_client.update_password.returns(BluebirdPromise.resolve()); + ldap_client.bind.returns(BluebirdPromise.resolve()); + return PasswordResetFormPost.default(req as any, res as any) + .then(function () { + const authSession = AuthenticationSession.get(req as any); + assert.equal(res.status.getCall(0).args[0], 204); + assert.equal(authSession.first_factor, false); + assert.equal(authSession.second_factor, false); + return BluebirdPromise.resolve(); + }); + }); + + it("should fail if identity_challenge does not exist", function (done) { + authSession.identity_check = { + userid: "user", + challenge: undefined + }; + res.send = sinon.spy(function () { + assert.equal(res.status.getCall(0).args[0], 403); + done(); + }); + PasswordResetFormPost.default(req as any, res as any); + }); + + it("should fail when ldap fails", function (done) { + authSession.identity_check = { + challenge: "reset-password", + userid: "user" + }; + req.body = {}; + req.body.password = "new-password"; + + ldap_client.bind.yields(undefined); + ldap_client.update_password.returns(BluebirdPromise.reject("Internal error with LDAP")); + res.send = sinon.spy(function () { + assert.equal(res.status.getCall(0).args[0], 500); + done(); + }); + PasswordResetFormPost.default(req as any, res as any); + }); + }); +}); diff --git a/test/server/routes/secondfactor/totp/register/RegistrationHandler.test.ts b/test/server/routes/secondfactor/totp/register/RegistrationHandler.test.ts new file mode 100644 index 000000000..e3f2cb89b --- /dev/null +++ b/test/server/routes/secondfactor/totp/register/RegistrationHandler.test.ts @@ -0,0 +1,90 @@ +import sinon = require("sinon"); +import winston = require("winston"); +import RegistrationHandler from "../../../../../../src/server/lib/routes/secondfactor/totp/identity/RegistrationHandler"; +import { Identity } from "../../../../../../src/types/Identity"; +import AuthenticationSession = require("../../../../../../src/server/lib/AuthenticationSession"); +import assert = require("assert"); +import BluebirdPromise = require("bluebird"); + +import ExpressMock = require("../../../../mocks/express"); +import UserDataStoreMock = require("../../../../mocks/UserDataStore"); +import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); + +describe("test totp register", function () { + let req: ExpressMock.RequestMock; + let res: ExpressMock.ResponseMock; + let userDataStore: UserDataStoreMock.UserDataStore; + const registrationHandler: RegistrationHandler = new RegistrationHandler(); + let authSession: AuthenticationSession.AuthenticationSession; + + beforeEach(function () { + req = ExpressMock.RequestMock(); + const mocks = ServerVariablesMock.mock(req.app); + mocks.logger = winston; + req.session = {}; + + AuthenticationSession.reset(req as any); + authSession = AuthenticationSession.get(req as any); + authSession.userid = "user"; + authSession.email = "user@example.com"; + authSession.first_factor = true; + authSession.second_factor = false; + + req.headers = {}; + req.headers.host = "localhost"; + + const options = { + inMemoryOnly: true + }; + + userDataStore = UserDataStoreMock.UserDataStore(); + userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); + userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); + userDataStore.issue_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({})); + userDataStore.consume_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({})); + userDataStore.set_totp_secret = sinon.stub().returns(BluebirdPromise.resolve({})); + mocks.userDataStore = userDataStore; + + res = ExpressMock.ResponseMock(); + }); + + describe("test totp registration check", test_registration_check); + + function test_registration_check() { + it("should fail if first_factor has not been passed", function () { + authSession.first_factor = false; + return registrationHandler.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) { + authSession.first_factor = false; + authSession.userid = undefined; + + registrationHandler.preValidationInit(req as any) + .catch(function (err: Error) { + done(); + }); + }); + + it("should fail if email is missing", function (done) { + authSession.first_factor = false; + authSession.email = undefined; + + registrationHandler.preValidationInit(req as any) + .catch(function (err: Error) { + done(); + }); + }); + + it("should succeed if first factor passed, userid and email are provided", function (done) { + registrationHandler.preValidationInit(req as any) + .then(function (identity: Identity) { + done(); + }); + }); + } +}); diff --git a/test/server/routes/secondfactor/totp/sign/post.test.ts b/test/server/routes/secondfactor/totp/sign/post.test.ts new file mode 100644 index 000000000..d12595357 --- /dev/null +++ b/test/server/routes/secondfactor/totp/sign/post.test.ts @@ -0,0 +1,93 @@ + +import BluebirdPromise = require("bluebird"); +import sinon = require("sinon"); +import assert = require("assert"); +import winston = require("winston"); + +import exceptions = require("../../../../../../src/server/lib/Exceptions"); +import AuthenticationSession = require("../../../../../../src/server/lib/AuthenticationSession"); +import SignPost = require("../../../../../../src/server/lib/routes/secondfactor/totp/sign/post"); + +import ExpressMock = require("../../../../mocks/express"); +import UserDataStoreMock = require("../../../../mocks/UserDataStore"); +import TOTPValidatorMock = require("../../../../mocks/TOTPValidator"); +import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); + +describe("test totp route", function () { + let req: ExpressMock.RequestMock; + let res: ExpressMock.ResponseMock; + let totpValidator: TOTPValidatorMock.TOTPValidatorMock; + let userDataStore: UserDataStoreMock.UserDataStore; + let authSession: AuthenticationSession.AuthenticationSession; + + beforeEach(function () { + const app_get = sinon.stub(); + req = { + app: { + }, + body: { + token: "abc" + }, + session: {} + }; + AuthenticationSession.reset(req as any); + authSession = AuthenticationSession.get(req as any); + authSession.userid = "user"; + authSession.first_factor = true; + authSession.second_factor = false; + + const mocks = ServerVariablesMock.mock(req.app); + res = ExpressMock.ResponseMock(); + + const config = { totp_secret: "secret" }; + totpValidator = TOTPValidatorMock.TOTPValidatorMock(); + + userDataStore = UserDataStoreMock.UserDataStore(); + + const doc = { + userid: "user", + secret: { + base32: "ABCDEF" + } + }; + userDataStore.get_totp_secret.returns(BluebirdPromise.resolve(doc)); + + mocks.logger = winston; + mocks.totpValidator = totpValidator; + mocks.config = config; + mocks.userDataStore = userDataStore; + }); + + + it("should send status code 200 when totp is valid", function () { + totpValidator.validate.returns(BluebirdPromise.resolve("ok")); + return SignPost.default(req as any, res as any) + .then(function () { + assert.equal(true, authSession.second_factor); + return BluebirdPromise.resolve(); + }); + }); + + it("should send status code 401 when totp is not valid", function () { + totpValidator.validate.returns(BluebirdPromise.reject(new exceptions.InvalidTOTPError("Bad TOTP token"))); + SignPost.default(req as any, res as any) + .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) + .catch(function () { + assert.equal(false, authSession.second_factor); + assert.equal(401, res.status.getCall(0).args[0]); + return BluebirdPromise.resolve(); + }); + }); + + it("should send status code 401 when session has not been initiated", function () { + totpValidator.validate.returns(BluebirdPromise.resolve("abc")); + req.session = {}; + return SignPost.default(req as any, res as any) + .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) + .catch(function () { + assert.equal(401, res.status.getCall(0).args[0]); + return BluebirdPromise.resolve(); + }); + }); +}); + diff --git a/test/server/routes/secondfactor/u2f/identity/RegistrationHandler.test.ts b/test/server/routes/secondfactor/u2f/identity/RegistrationHandler.test.ts new file mode 100644 index 000000000..ba0db3494 --- /dev/null +++ b/test/server/routes/secondfactor/u2f/identity/RegistrationHandler.test.ts @@ -0,0 +1,91 @@ +import sinon = require("sinon"); +import winston = require("winston"); +import assert = require("assert"); +import BluebirdPromise = require("bluebird"); + +import { Identity } from "../../../../../../src/types/Identity"; +import RegistrationHandler from "../../../../../../src/server/lib/routes/secondfactor/u2f/identity/RegistrationHandler"; +import AuthenticationSession = require("../../../../../../src/server/lib/AuthenticationSession"); + +import ExpressMock = require("../../../../mocks/express"); +import UserDataStoreMock = require("../../../../mocks/UserDataStore"); +import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); + +describe("test register handler", function () { + let req: ExpressMock.RequestMock; + let res: ExpressMock.ResponseMock; + let userDataStore: UserDataStoreMock.UserDataStore; + let authSession: AuthenticationSession.AuthenticationSession; + + beforeEach(function () { + req = ExpressMock.RequestMock; + req.app = {}; + const mocks = ServerVariablesMock.mock(req.app); + mocks.logger = winston; + req.session = {}; + AuthenticationSession.reset(req as any); + authSession = AuthenticationSession.get(req as any); + authSession.userid = "user"; + authSession.email = "user@example.com"; + authSession.first_factor = true; + authSession.second_factor = false; + req.headers = {}; + req.headers.host = "localhost"; + + const options = { + inMemoryOnly: true + }; + + userDataStore = UserDataStoreMock.UserDataStore(); + userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); + userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); + userDataStore.issue_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({})); + userDataStore.consume_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({})); + mocks.userDataStore = userDataStore; + + 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 () { + authSession.first_factor = false; + return new RegistrationHandler().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) { + authSession.first_factor = false; + authSession.userid = undefined; + + new RegistrationHandler().preValidationInit(req as any) + .catch(function (err: Error) { + done(); + }); + }); + + it("should fail if email is missing", function (done) { + authSession.first_factor = false; + authSession.email = undefined; + + new RegistrationHandler().preValidationInit(req as any) + .catch(function (err: Error) { + done(); + }); + }); + + it("should succeed if first factor passed, userid and email are provided", function (done) { + new RegistrationHandler().preValidationInit(req as any) + .then(function (identity: Identity) { + done(); + }); + }); + } +}); diff --git a/test/server/routes/secondfactor/u2f/register/post.test.ts b/test/server/routes/secondfactor/u2f/register/post.test.ts new file mode 100644 index 000000000..a1d2f7781 --- /dev/null +++ b/test/server/routes/secondfactor/u2f/register/post.test.ts @@ -0,0 +1,145 @@ + +import sinon = require("sinon"); +import BluebirdPromise = require("bluebird"); +import assert = require("assert"); +import U2FRegisterPost = require("../../../../../../src/server/lib/routes/secondfactor/u2f/register/post"); +import AuthenticationSession = require("../../../../../../src/server/lib/AuthenticationSession"); +import winston = require("winston"); + +import ExpressMock = require("../../../../mocks/express"); +import UserDataStoreMock = require("../../../../mocks/UserDataStore"); +import U2FMock = require("../../../../mocks/u2f"); +import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); +import U2f = require("u2f"); + +describe("test u2f routes: register", function () { + let req: ExpressMock.RequestMock; + let res: ExpressMock.ResponseMock; + let userDataStore: UserDataStoreMock.UserDataStore; + let mocks: ServerVariablesMock.ServerVariablesMock; + let authSession: AuthenticationSession.AuthenticationSession; + + beforeEach(function () { + req = ExpressMock.RequestMock(); + req.app = {}; + mocks = ServerVariablesMock.mock(req.app); + mocks.logger = winston; + + req.session = {}; + AuthenticationSession.reset(req as any); + authSession = AuthenticationSession.get(req as any); + authSession.userid = "user"; + authSession.first_factor = true; + authSession.second_factor = false; + authSession.identity_check = { + challenge: "u2f-register", + userid: "user" + }; + + req.headers = {}; + req.headers.host = "localhost"; + + const options = { + inMemoryOnly: true + }; + + userDataStore = UserDataStoreMock.UserDataStore(); + userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); + userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); + mocks.userDataStore = userDataStore; + + res = ExpressMock.ResponseMock(); + res.send = sinon.spy(); + res.json = sinon.spy(); + res.status = sinon.spy(); + }); + + 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" + }; + const u2f_mock = U2FMock.U2FMock(); + u2f_mock.checkRegistration.returns(BluebirdPromise.resolve(expectedStatus)); + + authSession.register_request = { + appId: "app", + challenge: "challenge", + keyHandle: "key", + version: "U2F_V2" + }; + mocks.u2f = u2f_mock; + return U2FRegisterPost.default(req as any, res as any) + .then(function () { + assert.equal("user", userDataStore.set_u2f_meta.getCall(0).args[0]); + assert.equal(authSession.identity_check, undefined); + }); + }); + + it("should return unauthorized on finishRegistration error", function () { + const user_key_container = {}; + const u2f_mock = U2FMock.U2FMock(); + u2f_mock.checkRegistration.returns({ errorCode: 500 }); + + authSession.register_request = { + appId: "app", + challenge: "challenge", + keyHandle: "key", + version: "U2F_V2" + }; + mocks.u2f = u2f_mock; + return U2FRegisterPost.default(req as any, res as any) + .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) + .catch(function () { + assert.equal(500, res.status.getCall(0).args[0]); + return BluebirdPromise.resolve(); + }); + }); + + it("should return 403 when register_request is not provided", function () { + const user_key_container = {}; + const u2f_mock = U2FMock.U2FMock(); + u2f_mock.checkRegistration.returns(BluebirdPromise.resolve()); + + authSession.register_request = undefined; + mocks.u2f = u2f_mock; + return U2FRegisterPost.default(req as any, res as any) + .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) + .catch(function () { + assert.equal(403, res.status.getCall(0).args[0]); + return BluebirdPromise.resolve(); + }); + }); + + it("should return forbidden error when no auth request has been initiated", function () { + const user_key_container = {}; + const u2f_mock = U2FMock.U2FMock(); + u2f_mock.checkRegistration.returns(BluebirdPromise.resolve()); + + authSession.register_request = undefined; + mocks.u2f = u2f_mock; + return U2FRegisterPost.default(req as any, res as any) + .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) + .catch(function () { + assert.equal(403, res.status.getCall(0).args[0]); + return BluebirdPromise.resolve(); + }); + }); + + it("should return forbidden error when identity has not been verified", function () { + authSession.identity_check = undefined; + return U2FRegisterPost.default(req as any, res as any) + .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) + .catch(function () { + assert.equal(403, res.status.getCall(0).args[0]); + return BluebirdPromise.resolve(); + }); + }); + } +}); + diff --git a/test/server/routes/secondfactor/u2f/register_request/get.test.ts b/test/server/routes/secondfactor/u2f/register_request/get.test.ts new file mode 100644 index 000000000..1f5405a69 --- /dev/null +++ b/test/server/routes/secondfactor/u2f/register_request/get.test.ts @@ -0,0 +1,96 @@ + +import sinon = require("sinon"); +import BluebirdPromise = require("bluebird"); +import assert = require("assert"); +import U2FRegisterRequestGet = require("../../../../../../src/server/lib/routes/secondfactor/u2f/register_request/get"); +import AuthenticationSession = require("../../../../../../src/server/lib/AuthenticationSession"); +import winston = require("winston"); + +import ExpressMock = require("../../../../mocks/express"); +import UserDataStoreMock = require("../../../../mocks/UserDataStore"); +import U2FMock = require("../../../../mocks/u2f"); +import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); +import U2f = require("u2f"); + +describe("test u2f routes: register_request", function () { + let req: ExpressMock.RequestMock; + let res: ExpressMock.ResponseMock; + let userDataStore: UserDataStoreMock.UserDataStore; + let mocks: ServerVariablesMock.ServerVariablesMock; + let authSession: AuthenticationSession.AuthenticationSession; + + beforeEach(function () { + req = ExpressMock.RequestMock(); + req.app = {}; + mocks = ServerVariablesMock.mock(req.app); + mocks.logger = winston; + req.session = {}; + AuthenticationSession.reset(req as any); + authSession = AuthenticationSession.get(req as any); + + authSession.userid = "user"; + authSession.first_factor = true; + authSession.second_factor = false; + authSession.identity_check = { + challenge: "u2f-register", + userid: "user" + }; + + req.headers = {}; + req.headers.host = "localhost"; + + const options = { + inMemoryOnly: true + }; + + userDataStore = UserDataStoreMock.UserDataStore(); + userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); + userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); + mocks.userDataStore = userDataStore; + + 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" + }; + const user_key_container = {}; + const u2f_mock = U2FMock.U2FMock(); + u2f_mock.request.returns(BluebirdPromise.resolve(expectedRequest)); + + mocks.u2f = u2f_mock; + return U2FRegisterRequestGet.default(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 (done) { + res.send = sinon.spy(function (data: any) { + assert.equal(500, res.status.getCall(0).args[0]); + done(); + }); + const user_key_container = {}; + const u2f_mock = U2FMock.U2FMock(); + u2f_mock.request.returns(BluebirdPromise.reject("Internal error")); + + mocks.u2f = u2f_mock; + U2FRegisterRequestGet.default(req as any, res as any); + }); + + it("should return forbidden if identity has not been verified", function (done) { + res.send = sinon.spy(function (data: any) { + assert.equal(403, res.status.getCall(0).args[0]); + done(); + }); + authSession.identity_check = undefined; + U2FRegisterRequestGet.default(req as any, res as any); + }); + }); +}); + diff --git a/test/server/routes/secondfactor/u2f/sign/post.test.ts b/test/server/routes/secondfactor/u2f/sign/post.test.ts new file mode 100644 index 000000000..0308dfec7 --- /dev/null +++ b/test/server/routes/secondfactor/u2f/sign/post.test.ts @@ -0,0 +1,100 @@ + +import sinon = require("sinon"); +import BluebirdPromise = require("bluebird"); +import assert = require("assert"); +import U2FSignPost = require("../../../../../../src/server/lib/routes/secondfactor/u2f/sign/post"); +import AuthenticationSession = require("../../../../../../src/server/lib/AuthenticationSession"); +import winston = require("winston"); + +import ExpressMock = require("../../../../mocks/express"); +import UserDataStoreMock = require("../../../../mocks/UserDataStore"); +import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); +import U2FMock = require("../../../../mocks/u2f"); +import U2f = require("u2f"); + +describe("test u2f routes: sign", function () { + let req: ExpressMock.RequestMock; + let res: ExpressMock.ResponseMock; + let userDataStore: UserDataStoreMock.UserDataStore; + let mocks: ServerVariablesMock.ServerVariablesMock; + let authSession: AuthenticationSession.AuthenticationSession; + + beforeEach(function () { + req = ExpressMock.RequestMock(); + req.app = {}; + + mocks = ServerVariablesMock.mock(req.app); + mocks.logger = winston; + + req.session = {}; + AuthenticationSession.reset(req as any); + authSession = AuthenticationSession.get(req as any); + authSession.userid = "user"; + authSession.first_factor = true; + authSession.second_factor = false; + authSession.identity_check = { + challenge: "u2f-register", + userid: "user" + }; + req.headers = {}; + req.headers.host = "localhost"; + + const options = { + inMemoryOnly: true + }; + + userDataStore = UserDataStoreMock.UserDataStore(); + userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); + userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); + mocks.userDataStore = userDataStore; + + res = ExpressMock.ResponseMock(); + res.send = sinon.spy(); + res.json = sinon.spy(); + res.status = sinon.spy(); + }); + + describe("test signing", () => { + it("should return status code 204", function () { + const expectedStatus = { + keyHandle: "keyHandle", + publicKey: "pbk", + certificate: "cert" + }; + const u2f_mock = U2FMock.U2FMock(); + u2f_mock.checkSignature.returns(expectedStatus); + + authSession.sign_request = { + appId: "app", + challenge: "challenge", + keyHandle: "key", + version: "U2F_V2" + }; + mocks.u2f = u2f_mock; + return U2FSignPost.default(req as any, res as any) + .then(function () { + assert(authSession.second_factor); + }); + }); + + it("should return unauthorized error on registration request internal error", function (done) { + res.send = sinon.spy(function (data: any) { + assert.equal(500, res.status.getCall(0).args[0]); + done(); + }); + + const u2f_mock = U2FMock.U2FMock(); + u2f_mock.checkSignature.returns({ errorCode: 500 }); + + authSession.sign_request = { + appId: "app", + challenge: "challenge", + keyHandle: "key", + version: "U2F_V2" + }; + mocks.u2f = u2f_mock; + U2FSignPost.default(req as any, res as any); + }); + }); +}); + diff --git a/test/server/routes/secondfactor/u2f/sign_request/get.test.ts b/test/server/routes/secondfactor/u2f/sign_request/get.test.ts new file mode 100644 index 000000000..3ffc5dbb9 --- /dev/null +++ b/test/server/routes/secondfactor/u2f/sign_request/get.test.ts @@ -0,0 +1,83 @@ + +import sinon = require("sinon"); +import BluebirdPromise = require("bluebird"); +import assert = require("assert"); +import U2FSignRequestGet = require("../../../../../../src/server/lib/routes/secondfactor/u2f/sign_request/get"); +import AuthenticationSession = require("../../../../../../src/server/lib/AuthenticationSession"); +import winston = require("winston"); + +import ExpressMock = require("../../../../mocks/express"); +import UserDataStoreMock = require("../../../../mocks/UserDataStore"); +import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); +import U2FMock = require("../../../../mocks/u2f"); +import U2f = require("u2f"); + +import { SignMessage } from "../../../../../../src/server/lib/routes/secondfactor/u2f/sign_request/SignMessage"; + +describe("test u2f routes: sign_request", function () { + let req: ExpressMock.RequestMock; + let res: ExpressMock.ResponseMock; + let userDataStore: UserDataStoreMock.UserDataStore; + let mocks: ServerVariablesMock.ServerVariablesMock; + let authSession: AuthenticationSession.AuthenticationSession; + + beforeEach(function () { + req = ExpressMock.RequestMock(); + req.app = {}; + + mocks = ServerVariablesMock.mock(req.app); + mocks.logger = winston; + + req.session = {}; + + AuthenticationSession.reset(req as any); + authSession = AuthenticationSession.get(req as any); + authSession.userid = "user"; + authSession.first_factor = true; + authSession.second_factor = false; + authSession.identity_check = { + challenge: "u2f-register", + userid: "user" + }; + + req.headers = {}; + req.headers.host = "localhost"; + + const options = { + inMemoryOnly: true + }; + + userDataStore = UserDataStoreMock.UserDataStore(); + userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); + userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); + mocks.userDataStore = userDataStore; + + res = ExpressMock.ResponseMock(); + res.send = sinon.spy(); + res.json = sinon.spy(); + res.status = sinon.spy(); + }); + + describe("test signing request", test_signing_request); + + function test_signing_request() { + it("should send back the sign request and save it in the session", function () { + const expectedRequest: U2f.RegistrationResult = { + keyHandle: "keyHandle", + publicKey: "publicKey", + certificate: "Certificate", + successful: true + }; + const u2f_mock = U2FMock.U2FMock(); + u2f_mock.request.returns(expectedRequest); + + mocks.u2f = u2f_mock; + return U2FSignRequestGet.default(req as any, res as any) + .then(function () { + assert.deepEqual(expectedRequest, authSession.sign_request); + assert.deepEqual(expectedRequest, res.json.getCall(0).args[0].request); + }); + }); + } +}); + diff --git a/test/unitary/routes/AuthenticationValidator.test.ts b/test/server/routes/verify/get.test.ts similarity index 59% rename from test/unitary/routes/AuthenticationValidator.test.ts rename to test/server/routes/verify/get.test.ts index 014f7e150..3f93f0a3c 100644 --- a/test/unitary/routes/AuthenticationValidator.test.ts +++ b/test/server/routes/verify/get.test.ts @@ -1,13 +1,17 @@ import assert = require("assert"); -import AuthenticationValidator = require("../../../src/lib/routes/AuthenticationValidator"); +import VerifyGet = require("../../../../src/server/lib/routes/verify/get"); +import AuthenticationSession = require("../../../../src/server/lib/AuthenticationSession"); + import sinon = require("sinon"); import winston = require("winston"); +import BluebirdPromise = require("bluebird"); import express = require("express"); -import ExpressMock = require("../mocks/express"); -import AccessControllerMock = require("../mocks/AccessController"); +import ExpressMock = require("../../mocks/express"); +import AccessControllerMock = require("../../mocks/AccessController"); +import ServerVariablesMock = require("../../mocks/ServerVariablesMock"); describe("test authentication token verification", function () { let req: ExpressMock.RequestMock; @@ -22,38 +26,31 @@ describe("test authentication token verification", function () { res = ExpressMock.ResponseMock(); req.headers = {}; req.headers.host = "secret.example.com"; - req.app.get = sinon.stub(); - req.app.get.withArgs("config").returns({}); - req.app.get.withArgs("logger").returns(winston); - req.app.get.withArgs("access controller").returns(accessController); + const mocks = ServerVariablesMock.mock(req.app); + mocks.config = {}; + mocks.logger = winston; + mocks.accessController = accessController; }); - interface AuthenticationSession { - first_factor?: boolean; - second_factor?: boolean; - userid?: string; - groups?: string[]; - } - it("should be already authenticated", function (done) { req.session = {}; - req.session.auth_session = { - first_factor: true, - second_factor: true, - userid: "myuser", - } as AuthenticationSession; + AuthenticationSession.reset(req as any); + const authSession = AuthenticationSession.get(req as any); + authSession.first_factor = true; + authSession.second_factor = true; + authSession.userid = "myuser"; res.send = sinon.spy(function () { assert.equal(204, res.status.getCall(0).args[0]); done(); }); - AuthenticationValidator(req as express.Request, res as any); + VerifyGet.default(req as express.Request, res as any); }); describe("given different cases of session", function () { - function test_session(auth_session: AuthenticationSession, status_code: number) { - return new Promise(function (resolve, reject) { + function test_session(auth_session: AuthenticationSession.AuthenticationSession, status_code: number) { + return new BluebirdPromise(function (resolve, reject) { req.session = {}; req.session.auth_session = auth_session; @@ -62,15 +59,15 @@ describe("test authentication token verification", function () { resolve(); }); - AuthenticationValidator(req as express.Request, res as any); + VerifyGet.default(req as express.Request, res as any); }); } - function test_unauthorized(auth_session: AuthenticationSession) { + function test_unauthorized(auth_session: AuthenticationSession.AuthenticationSession) { return test_session(auth_session, 401); } - function test_authorized(auth_session: AuthenticationSession) { + function test_authorized(auth_session: AuthenticationSession.AuthenticationSession) { return test_session(auth_session, 204); } @@ -78,45 +75,58 @@ describe("test authentication token verification", function () { return test_unauthorized({ userid: "user", first_factor: true, - second_factor: false + second_factor: false, + email: undefined, + groups: [], }); }); it("should not be authenticated when first factor is missing", function () { - return test_unauthorized({ first_factor: false, second_factor: true }); + return test_unauthorized({ + userid: "user", + first_factor: false, + second_factor: true, + email: undefined, + groups: [], + }); }); it("should not be authenticated when userid is missing", function () { return test_unauthorized({ + userid: undefined, first_factor: true, - second_factor: true, - groups: ["mygroup"], + second_factor: false, + email: undefined, + groups: [], }); }); it("should not be authenticated when first and second factor are missing", function () { - return test_unauthorized({ first_factor: false, second_factor: false }); + return test_unauthorized({ + userid: "user", + first_factor: false, + second_factor: false, + email: undefined, + groups: [], + }); }); it("should not be authenticated when session has not be initiated", function () { return test_unauthorized(undefined); }); - it("should not be authenticated when session is partially initialized", function () { - return test_unauthorized({ first_factor: true }); - }); - it("should not be authenticated when domain is not allowed for user", function () { req.headers.host = "test.example.com"; accessController.isDomainAllowedForUser.returns(false); accessController.isDomainAllowedForUser.withArgs("test.example.com", "user", ["group1", "group2"]).returns(true); - return test_authorized({ + return test_unauthorized({ first_factor: true, second_factor: true, userid: "user", - groups: ["group1", "group2"] + groups: ["group1", "group2"], + email: undefined }); }); }); diff --git a/test/unitary/server_config.test.ts b/test/server/server_config.test.ts similarity index 91% rename from test/unitary/server_config.test.ts rename to test/server/server_config.test.ts index 350d2d661..83570f021 100644 --- a/test/unitary/server_config.test.ts +++ b/test/server/server_config.test.ts @@ -5,13 +5,13 @@ import nedb = require("nedb"); import express = require("express"); import winston = require("winston"); import speakeasy = require("speakeasy"); -import u2f = require("authdog"); +import u2f = require("u2f"); import nodemailer = require("nodemailer"); import session = require("express-session"); -import { AppConfiguration, UserConfiguration } from "../../src/lib/Configuration"; +import { AppConfiguration, UserConfiguration } from "../../src/types/Configuration"; import { GlobalDependencies, Nodemailer } from "../../src/types/Dependencies"; -import Server from "../../src/lib/Server"; +import Server from "../../src/server/lib/Server"; describe("test server configuration", function () { diff --git a/test/unitary/user_data_store/authentication_audit.test.ts b/test/server/user_data_store/authentication_audit.test.ts similarity index 97% rename from test/unitary/user_data_store/authentication_audit.test.ts rename to test/server/user_data_store/authentication_audit.test.ts index 8a8be4df0..c0037fd01 100644 --- a/test/unitary/user_data_store/authentication_audit.test.ts +++ b/test/server/user_data_store/authentication_audit.test.ts @@ -3,7 +3,7 @@ import * as assert from "assert"; import * as Promise from "bluebird"; import * as sinon from "sinon"; import * as MockDate from "mockdate"; -import UserDataStore from "../../../src/lib/UserDataStore"; +import UserDataStore from "../../../src/server/lib/UserDataStore"; import nedb = require("nedb"); describe("test user data store", function() { diff --git a/test/unitary/user_data_store/totp_secret.test.ts b/test/server/user_data_store/totp_secret.test.ts similarity index 96% rename from test/unitary/user_data_store/totp_secret.test.ts rename to test/server/user_data_store/totp_secret.test.ts index bd5223aca..08adcf6dc 100644 --- a/test/unitary/user_data_store/totp_secret.test.ts +++ b/test/server/user_data_store/totp_secret.test.ts @@ -3,7 +3,7 @@ import * as assert from "assert"; import * as Promise from "bluebird"; import * as sinon from "sinon"; import * as MockDate from "mockdate"; -import UserDataStore from "../../../src/lib/UserDataStore"; +import UserDataStore from "../../../src/server/lib/UserDataStore"; import nedb = require("nedb"); describe("test user data store", function() { diff --git a/test/unitary/IdentityValidator.test.ts b/test/unitary/IdentityValidator.test.ts deleted file mode 100644 index c9def5d8c..000000000 --- a/test/unitary/IdentityValidator.test.ts +++ /dev/null @@ -1,242 +0,0 @@ - -import sinon = require("sinon"); -import IdentityValidator = require("../../src/lib/IdentityValidator"); -import exceptions = require("../../src/lib/Exceptions"); -import assert = require("assert"); -import winston = require("winston"); -import Promise = require("bluebird"); -import express = require("express"); -import BluebirdPromise = require("bluebird"); - -import ExpressMock = require("./mocks/express"); -import UserDataStoreMock = require("./mocks/UserDataStore"); -import NotifierMock = require("./mocks/Notifier"); -import IdentityValidatorMock = require("./mocks/IdentityValidator"); - - -describe("test identity check process", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let userDataStore: UserDataStoreMock.UserDataStore; - let notifier: NotifierMock.NotifierMock; - let app: express.Application; - let app_get: sinon.SinonStub; - let app_post: sinon.SinonStub; - let identityValidable: IdentityValidatorMock.IdentityValidableMock; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - res = ExpressMock.ResponseMock(); - - userDataStore = UserDataStoreMock.UserDataStore(); - userDataStore.issue_identity_check_token = sinon.stub(); - userDataStore.issue_identity_check_token.returns(Promise.resolve()); - userDataStore.consume_identity_check_token = sinon.stub(); - userDataStore.consume_identity_check_token.returns(Promise.resolve({ userid: "user" })); - - notifier = NotifierMock.NotifierMock(); - notifier.notify = sinon.stub().returns(Promise.resolve()); - - req.headers = {}; - req.session = {}; - req.session.auth_session = {}; - - req.query = {}; - req.app = {}; - req.app.get = sinon.stub(); - req.app.get.withArgs("logger").returns(winston); - req.app.get.withArgs("user data store").returns(userDataStore); - req.app.get.withArgs("notifier").returns(notifier); - - app = express(); - app_get = sinon.stub(app, "get"); - app_post = sinon.stub(app, "post"); - - identityValidable = IdentityValidatorMock.IdentityValidableMock(); - }); - - afterEach(function () { - app_get.restore(); - app_post.restore(); - }); - - it("should register a POST and GET endpoint", function () { - const endpoint = "/test"; - const icheck_interface = {}; - - IdentityValidator.IdentityValidator.setup(app, endpoint, identityValidable, userDataStore as any, winston); - - assert(app_get.calledOnce); - assert(app_get.calledWith(endpoint)); - - assert(app_post.calledOnce); - assert(app_post.calledWith(endpoint)); - }); - - describe("test POST", test_post_handler); - describe("test GET", test_get_handler); - - function test_post_handler() { - it("should send 403 if pre check rejects", function (done) { - const endpoint = "/protected"; - - identityValidable.preValidation.returns(Promise.reject("No access")); - IdentityValidator.IdentityValidator.setup(app, endpoint, identityValidable, userDataStore as any, winston); - - res.send = sinon.spy(function () { - assert.equal(res.status.getCall(0).args[0], 403); - done(); - }); - - const handler = app_post.getCall(0).args[1]; - handler(req, res); - }); - - it("should send 400 if email is missing in provided identity", function (done) { - const endpoint = "/protected"; - const identity = { userid: "abc" }; - - identityValidable.preValidation.returns(Promise.resolve(identity)); - IdentityValidator.IdentityValidator.setup(app, endpoint, identityValidable, userDataStore as any, winston); - - res.send = sinon.spy(function () { - assert.equal(res.status.getCall(0).args[0], 400); - done(); - }); - - const handler = app_post.getCall(0).args[1]; - handler(req, res); - }); - - it("should send 400 if userid is missing in provided identity", function (done) { - const endpoint = "/protected"; - const identity = { email: "abc@example.com" }; - - identityValidable.preValidation.returns(Promise.resolve(identity)); - IdentityValidator.IdentityValidator.setup(app, endpoint, identityValidable, userDataStore as any, winston); - - res.send = sinon.spy(function () { - assert.equal(res.status.getCall(0).args[0], 400); - done(); - }); - const handler = app_post.getCall(0).args[1]; - handler(req, res); - }); - - describe("should issue a token, send an email and return 204", () => { - function contains(str: string, pattern: string): boolean { - return str.indexOf(pattern) > -1; - } - - it("with x-original-uri", function(done) { - const endpoint = "/protected"; - const identity = { userid: "user", email: "abc@example.com" }; - req.headers.host = "localhost"; - req.headers["x-original-uri"] = "/auth/test"; - - identityValidable.preValidation.returns(Promise.resolve(identity)); - IdentityValidator.IdentityValidator.setup(app, endpoint, identityValidable, userDataStore as any, winston); - - res.send = sinon.spy(function () { - assert.equal(res.status.getCall(0).args[0], 204); - assert(notifier.notify.calledOnce); - console.log(notifier.notify.getCall(0).args[2]); - assert(contains(notifier.notify.getCall(0).args[2], "https://localhost/auth/test?identity_token=")); - assert(userDataStore.issue_identity_check_token.calledOnce); - assert.equal(userDataStore.issue_identity_check_token.getCall(0).args[0], "user"); - assert.equal(userDataStore.issue_identity_check_token.getCall(0).args[3], 240000); - done(); - }); - const handler = app_post.getCall(0).args[1]; - handler(req, res); - }); - - it("without x-original-uri", function(done) { - const endpoint = "/protected"; - const identity = { userid: "user", email: "abc@example.com" }; - req.headers.host = "localhost"; - - identityValidable.preValidation.returns(Promise.resolve(identity)); - IdentityValidator.IdentityValidator.setup(app, endpoint, identityValidable, userDataStore as any, winston); - - res.send = sinon.spy(function () { - assert.equal(res.status.getCall(0).args[0], 204); - assert(notifier.notify.calledOnce); - assert(contains(notifier.notify.getCall(0).args[2], "https://localhost?identity_token=")); - assert(userDataStore.issue_identity_check_token.calledOnce); - assert.equal(userDataStore.issue_identity_check_token.getCall(0).args[0], "user"); - assert.equal(userDataStore.issue_identity_check_token.getCall(0).args[3], 240000); - done(); - }); - const handler = app_post.getCall(0).args[1]; - handler(req, res); - }); - }); - } - - function test_get_handler() { - it("should send 403 if no identity_token is provided", function (done) { - const endpoint = "/protected"; - - IdentityValidator.IdentityValidator.setup(app, endpoint, identityValidable, userDataStore as any, winston); - - res.send = sinon.spy(function () { - assert.equal(res.status.getCall(0).args[0], 403); - done(); - }); - const handler = app_get.getCall(0).args[1]; - handler(req, res); - }); - - it("should render template if identity_token is provided and still valid", function (done) { - req.query.identity_token = "token"; - const endpoint = "/protected"; - identityValidable.templateName.returns("template"); - - IdentityValidator.IdentityValidator.setup(app, endpoint, identityValidable, userDataStore as any, winston); - - res.render = sinon.spy(function (template: string) { - assert.equal(template, "template"); - done(); - }); - const handler = app_get.getCall(0).args[1]; - handler(req, res); - }); - - it("should return 403 if identity_token is provided but invalid", function (done) { - req.query.identity_token = "token"; - const endpoint = "/protected"; - - identityValidable.templateName.returns("template"); - userDataStore.consume_identity_check_token - .returns(Promise.reject("Invalid token")); - - IdentityValidator.IdentityValidator.setup(app, endpoint, identityValidable, userDataStore as any, winston); - - res.send = sinon.spy(function (template: string) { - assert.equal(res.status.getCall(0).args[0], 403); - done(); - }); - const handler = app_get.getCall(0).args[1]; - handler(req, res); - }); - - it("should set the identity_check session object even if session does not exist yet", function (done) { - req.query.identity_token = "token"; - const endpoint = "/protected"; - - req.session = {}; - identityValidable.templateName.returns("template"); - - IdentityValidator.IdentityValidator.setup(app, endpoint, identityValidable, userDataStore as any, winston); - - res.render = sinon.spy(function (template: string) { - assert.equal(req.session.auth_session.identity_check.userid, "user"); - assert.equal(template, "template"); - done(); - }); - const handler = app_get.getCall(0).args[1]; - handler(req, res); - }); - } -}); diff --git a/test/unitary/mocks/authdog.ts b/test/unitary/mocks/authdog.ts deleted file mode 100644 index 843a9e7c3..000000000 --- a/test/unitary/mocks/authdog.ts +++ /dev/null @@ -1,19 +0,0 @@ - -import sinon = require("sinon"); -import authdog = require("authdog"); - -export interface AuthdogMock { - startRegistration: sinon.SinonStub; - finishRegistration: sinon.SinonStub; - startAuthentication: sinon.SinonStub; - finishAuthentication: sinon.SinonStub; -} - -export function AuthdogMock(): AuthdogMock { - return { - startRegistration: sinon.stub(), - finishAuthentication: sinon.stub(), - startAuthentication: sinon.stub(), - finishRegistration: sinon.stub() - }; -} diff --git a/test/unitary/routes/DenyNotLogged.test.ts b/test/unitary/routes/DenyNotLogged.test.ts deleted file mode 100644 index 246787371..000000000 --- a/test/unitary/routes/DenyNotLogged.test.ts +++ /dev/null @@ -1,82 +0,0 @@ - -import sinon = require("sinon"); -import Promise = require("bluebird"); -import assert = require("assert"); -import express = require("express"); - -import ExpressMock = require("../mocks/express"); -import DenyNotLogged = require("../../../src/lib/routes/DenyNotLogged"); - -describe("test not logged", function () { - it("should return status code 403 when auth_session has not been previously created", function () { - return test_auth_session_not_created(); - }); - - it("should return status code 403 when auth_session has failed first factor", function () { - return test_auth_first_factor_not_validated(); - }); - - it("should return status code 204 when auth_session has succeeded first factor stage", function () { - return test_auth_with_first_factor_validated(); - }); -}); - -function test_auth_session_not_created() { - return new Promise(function (resolve, reject) { - const send = sinon.spy(resolve); - const status = sinon.spy(function (code: number) { - assert.equal(403, code); - }); - const req = ExpressMock.RequestMock(); - const res = ExpressMock.ResponseMock(); - req.session = {}; - res.send = send; - res.status = status; - - DenyNotLogged(reject)(req as any, res as any); - }); -} - -function test_auth_first_factor_not_validated() { - return new Promise(function (resolve, reject) { - const send = sinon.spy(resolve); - const status = sinon.spy(function (code: number) { - assert.equal(403, code); - }); - const req = { - session: { - auth_session: { - first_factor: false, - second_factor: false - } - } - }; - - const res = { - send: send, - status: status - }; - - DenyNotLogged(reject)(req as any, res as any); - }); -} - -function test_auth_with_first_factor_validated() { - return new Promise(function (resolve, reject) { - const req = { - session: { - auth_session: { - first_factor: true, - second_factor: false - } - } - }; - - const res = { - send: sinon.spy(), - status: sinon.spy() - }; - - DenyNotLogged(resolve)(req as any, res as any); - }); -} diff --git a/test/unitary/routes/PasswordReset.test.ts b/test/unitary/routes/PasswordReset.test.ts deleted file mode 100644 index cb1ec07d2..000000000 --- a/test/unitary/routes/PasswordReset.test.ts +++ /dev/null @@ -1,151 +0,0 @@ - -import PasswordReset = require("../../../src/lib/routes/PasswordReset"); -import LdapClient = require("../../../src/lib/LdapClient"); -import sinon = require("sinon"); -import winston = require("winston"); -import assert = require("assert"); -import BluebirdPromise = require("bluebird"); - -import ExpressMock = require("../mocks/express"); -import { LdapClientMock } from "../mocks/LdapClient"; -import { UserDataStore } from "../mocks/UserDataStore"; - -describe("test reset password", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let user_data_store: UserDataStore; - let ldap_client: LdapClientMock; - let configuration: any; - - beforeEach(function () { - req = { - body: { - userid: "user" - }, - app: { - get: sinon.stub() - }, - session: { - auth_session: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false - } - }, - headers: { - host: "localhost" - } - }; - - const options = { - inMemoryOnly: true - }; - - user_data_store = UserDataStore(); - user_data_store.set_u2f_meta.returns(Promise.resolve({})); - user_data_store.get_u2f_meta.returns(Promise.resolve({})); - user_data_store.issue_identity_check_token.returns(Promise.resolve({})); - user_data_store.consume_identity_check_token.returns(Promise.resolve({})); - req.app.get.withArgs("user data store").returns(user_data_store); - - - configuration = { - ldap: { - base_dn: "dc=example,dc=com", - user_name_attribute: "cn" - } - }; - - req.app.get.withArgs("logger").returns(winston); - req.app.get.withArgs("config").returns(configuration); - - ldap_client = LdapClientMock(); - req.app.get.withArgs("ldap").returns(ldap_client); - - res = ExpressMock.ResponseMock(); - }); - - describe("test reset password identity pre check", test_reset_password_check); - describe("test reset password post", test_reset_password_post); - - function test_reset_password_check() { - it("should fail when no userid is provided", function (done) { - req.body.userid = undefined; - PasswordReset.icheck_interface.preValidation(req as any) - .catch(function (err: Error) { - done(); - }); - }); - - it("should fail if ldap fail", function (done) { - ldap_client.get_emails.returns(BluebirdPromise.reject("Internal error")); - PasswordReset.icheck_interface.preValidation(req as any) - .catch(function (err: Error) { - done(); - }); - }); - - it("should perform a search in ldap to find email address", function (done) { - configuration.ldap.user_name_attribute = "uid"; - ldap_client.get_emails.returns(BluebirdPromise.resolve([])); - PasswordReset.icheck_interface.preValidation(req as any) - .then(function () { - assert.equal("user", ldap_client.get_emails.getCall(0).args[0]); - done(); - }); - }); - - it("should returns identity when ldap replies", function (done) { - ldap_client.get_emails.returns(BluebirdPromise.resolve(["test@example.com"])); - PasswordReset.icheck_interface.preValidation(req as any) - .then(function () { - done(); - }); - }); - } - - function test_reset_password_post() { - it("should update the password and reset auth_session for reauthentication", function (done) { - req.session.auth_session.identity_check = {}; - req.session.auth_session.identity_check.userid = "user"; - req.session.auth_session.identity_check.challenge = "reset-password"; - req.body = {}; - req.body.password = "new-password"; - - ldap_client.update_password.returns(BluebirdPromise.resolve()); - ldap_client.bind.returns(BluebirdPromise.resolve()); - res.send = sinon.spy(function () { - assert.equal(res.status.getCall(0).args[0], 204); - assert.equal(req.session.auth_session, undefined); - done(); - }); - PasswordReset.post(req as any, res as any); - }); - - it("should fail if identity_challenge does not exist", function (done) { - req.session.auth_session.identity_check = {}; - req.session.auth_session.identity_check.challenge = undefined; - res.send = sinon.spy(function () { - assert.equal(res.status.getCall(0).args[0], 403); - done(); - }); - PasswordReset.post(req as any, res as any); - }); - - it("should fail when ldap fails", function (done) { - req.session.auth_session.identity_check = {}; - req.session.auth_session.identity_check.challenge = "reset-password"; - req.body = {}; - req.body.password = "new-password"; - - ldap_client.bind.yields(undefined); - ldap_client.update_password.returns(BluebirdPromise.reject("Internal error with LDAP")); - res.send = sinon.spy(function () { - assert.equal(res.status.getCall(0).args[0], 500); - done(); - }); - PasswordReset.post(req as any, res as any); - }); - } -}); diff --git a/test/unitary/routes/TOTPAuthenticator.test.ts b/test/unitary/routes/TOTPAuthenticator.test.ts deleted file mode 100644 index eab66d672..000000000 --- a/test/unitary/routes/TOTPAuthenticator.test.ts +++ /dev/null @@ -1,90 +0,0 @@ - -import BluebirdPromise = require("bluebird"); -import sinon = require("sinon"); -import assert = require("assert"); -import winston = require("winston"); - -import exceptions = require("../../../src/lib/Exceptions"); -import TOTPAuthenticator = require("../../../src/lib/routes/TOTPAuthenticator"); - -import ExpressMock = require("../mocks/express"); -import UserDataStoreMock = require("../mocks/UserDataStore"); -import TOTPValidatorMock = require("../mocks/TOTPValidator"); - -describe("test totp route", function() { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let totpValidator: TOTPValidatorMock.TOTPValidatorMock; - let userDataStore: UserDataStoreMock.UserDataStore; - - beforeEach(function() { - const app_get = sinon.stub(); - req = { - app: { - get: app_get - }, - body: { - token: "abc" - }, - session: { - auth_session: { - userid: "user", - first_factor: false, - second_factor: false - } - } - }; - res = ExpressMock.ResponseMock(); - - const config = { totp_secret: "secret" }; - totpValidator = TOTPValidatorMock.TOTPValidatorMock(); - - userDataStore = UserDataStoreMock.UserDataStore(); - - const doc = { - userid: "user", - secret: { - base32: "ABCDEF" - } - }; - userDataStore.get_totp_secret.returns(BluebirdPromise.resolve(doc)); - - app_get.withArgs("logger").returns(winston); - app_get.withArgs("totp validator").returns(totpValidator); - app_get.withArgs("config").returns(config); - app_get.withArgs("user data store").returns(userDataStore); - }); - - - it("should send status code 204 when totp is valid", function(done) { - totpValidator.validate.returns(Promise.resolve("ok")); - res.send = sinon.spy(function() { - // Second factor passed - assert.equal(true, req.session.auth_session.second_factor); - assert.equal(204, res.status.getCall(0).args[0]); - done(); - }); - TOTPAuthenticator(req as any, res as any); - }); - - it("should send status code 401 when totp is not valid", function(done) { - totpValidator.validate.returns(Promise.reject(new exceptions.InvalidTOTPError("Bad TOTP token"))); - res.send = sinon.spy(function() { - assert.equal(false, req.session.auth_session.second_factor); - assert.equal(401, res.status.getCall(0).args[0]); - done(); - }); - TOTPAuthenticator(req as any, res as any); - }); - - it("should send status code 401 when session has not been initiated", function(done) { - totpValidator.validate.returns(Promise.resolve("abc")); - res.send = sinon.spy(function() { - assert.equal(403, res.status.getCall(0).args[0]); - done(); - }); - req.session = {}; - TOTPAuthenticator(req as any, res as any); - }); -}); - diff --git a/test/unitary/routes/TOTPRegistration.test.ts b/test/unitary/routes/TOTPRegistration.test.ts deleted file mode 100644 index 4667b618c..000000000 --- a/test/unitary/routes/TOTPRegistration.test.ts +++ /dev/null @@ -1,137 +0,0 @@ -import sinon = require("sinon"); -import winston = require("winston"); -import TOTPRegistration = require("../../../src/lib/routes/TOTPRegistration"); -import assert = require("assert"); -import BluebirdPromise = require("bluebird"); - -import ExpressMock = require("../mocks/express"); -import UserDataStoreMock = require("../mocks/UserDataStore"); - -describe("test totp register", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let userDataStore: UserDataStoreMock.UserDataStore; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.app.get = sinon.stub(); - req.app.get.withArgs("logger").returns(winston); - req.session = {}; - req.session.auth_session = {}; - req.session.auth_session.userid = "user"; - req.session.auth_session.email = "user@example.com"; - req.session.auth_session.first_factor = true; - req.session.auth_session.second_factor = false; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - userDataStore = UserDataStoreMock.UserDataStore(); - userDataStore.set_u2f_meta = sinon.stub().returns(Promise.resolve({})); - userDataStore.get_u2f_meta = sinon.stub().returns(Promise.resolve({})); - userDataStore.issue_identity_check_token = sinon.stub().returns(Promise.resolve({})); - userDataStore.consume_identity_check_token = sinon.stub().returns(Promise.resolve({})); - userDataStore.set_totp_secret = sinon.stub().returns(Promise.resolve({})); - req.app.get.withArgs("user data store").returns(userDataStore); - - res = ExpressMock.ResponseMock(); - }); - - describe("test totp registration check", test_registration_check); - describe("test totp post secret", test_post_secret); - - function test_registration_check() { - it("should fail if first_factor has not been passed", function (done) { - req.session.auth_session.first_factor = false; - TOTPRegistration.icheck_interface.preValidation(req as any) - .catch(function (err) { - done(); - }); - }); - - it("should fail if userid is missing", function (done) { - req.session.auth_session.first_factor = false; - req.session.auth_session.userid = undefined; - - TOTPRegistration.icheck_interface.preValidation(req as any) - .catch(function (err) { - done(); - }); - }); - - it("should fail if email is missing", function (done) { - req.session.auth_session.first_factor = false; - req.session.auth_session.email = undefined; - - TOTPRegistration.icheck_interface.preValidation(req as any) - .catch(function (err) { - done(); - }); - }); - - it("should succeed if first factor passed, userid and email are provided", function (done) { - TOTPRegistration.icheck_interface.preValidation(req as any) - .then(function (err) { - done(); - }); - }); - } - - function test_post_secret() { - it("should send the secret in json format", function (done) { - req.app.get.withArgs("totp generator").returns({ - generate: sinon.stub().returns({ otpauth_url: "abc" }) - }); - req.session.auth_session.identity_check = {}; - req.session.auth_session.identity_check.userid = "user"; - req.session.auth_session.identity_check.challenge = "totp-register"; - res.json = sinon.spy(function () { - done(); - }); - TOTPRegistration.post(req as any, res as any); - }); - - it("should clear the session for reauthentication", function (done) { - req.app.get.withArgs("totp generator").returns({ - generate: sinon.stub().returns({ otpauth_url: "abc" }) - }); - req.session.auth_session.identity_check = {}; - req.session.auth_session.identity_check.userid = "user"; - req.session.auth_session.identity_check.challenge = "totp-register"; - res.json = sinon.spy(function () { - assert.equal(req.session, undefined); - done(); - }); - TOTPRegistration.post(req as any, res as any); - }); - - it("should return 403 if the identity check challenge is not set", function (done) { - req.session.auth_session.identity_check = {}; - req.session.auth_session.identity_check.challenge = undefined; - res.send = sinon.spy(function () { - assert.equal(res.status.getCall(0).args[0], 403); - done(); - }); - TOTPRegistration.post(req as any, res as any); - }); - - it("should return 500 if db throws", function (done) { - req.app.get.withArgs("totp generator").returns({ - generate: sinon.stub().returns({ otpauth_url: "abc" }) - }); - req.session.auth_session.identity_check = {}; - req.session.auth_session.identity_check.userid = "user"; - req.session.auth_session.identity_check.challenge = "totp-register"; - userDataStore.set_totp_secret.returns(BluebirdPromise.reject("internal error")); - - res.send = sinon.spy(function () { - assert.equal(res.status.getCall(0).args[0], 500); - done(); - }); - TOTPRegistration.post(req as any, res as any); - }); - } -}); diff --git a/test/unitary/routes/U2FRegistration.test.ts b/test/unitary/routes/U2FRegistration.test.ts deleted file mode 100644 index a89faad33..000000000 --- a/test/unitary/routes/U2FRegistration.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -import sinon = require("sinon"); -import winston = require("winston"); -import u2f_register = require("../../../src/lib/routes/U2FRegistration"); -import assert = require("assert"); - -import ExpressMock = require("../mocks/express"); -import UserDataStoreMock = require("../mocks/UserDataStore"); - -describe("test register handler", function() { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let user_data_store: UserDataStoreMock.UserDataStore; - - beforeEach(function() { - req = ExpressMock.RequestMock; - req.app = {}; - req.app.get = sinon.stub(); - req.app.get.withArgs("logger").returns(winston); - req.session = {}; - req.session.auth_session = {}; - req.session.auth_session.userid = "user"; - req.session.auth_session.email = "user@example.com"; - req.session.auth_session.first_factor = true; - req.session.auth_session.second_factor = false; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - user_data_store = UserDataStoreMock.UserDataStore(); - user_data_store.set_u2f_meta = sinon.stub().returns(Promise.resolve({})); - user_data_store.get_u2f_meta = sinon.stub().returns(Promise.resolve({})); - user_data_store.issue_identity_check_token = sinon.stub().returns(Promise.resolve({})); - user_data_store.consume_identity_check_token = sinon.stub().returns(Promise.resolve({})); - req.app.get.withArgs("user data store").returns(user_data_store); - - 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(done) { - req.session.auth_session.first_factor = false; - u2f_register.icheck_interface.preValidation(req as any) - .catch(function(err: Error) { - done(); - }); - }); - - it("should fail if userid is missing", function(done) { - req.session.auth_session.first_factor = false; - req.session.auth_session.userid = undefined; - - u2f_register.icheck_interface.preValidation(req as any) - .catch(function(err: Error) { - done(); - }); - }); - - it("should fail if email is missing", function(done) { - req.session.auth_session.first_factor = false; - req.session.auth_session.email = undefined; - - u2f_register.icheck_interface.preValidation(req as any) - .catch(function(err) { - done(); - }); - }); - - it("should succeed if first factor passed, userid and email are provided", function(done) { - u2f_register.icheck_interface.preValidation(req as any) - .then(function(err) { - done(); - }); - }); - } -}); diff --git a/test/unitary/routes/U2FRoutes.test.ts b/test/unitary/routes/U2FRoutes.test.ts deleted file mode 100644 index 5274351c1..000000000 --- a/test/unitary/routes/U2FRoutes.test.ts +++ /dev/null @@ -1,278 +0,0 @@ - -import sinon = require("sinon"); -import Promise = require("bluebird"); -import assert = require("assert"); -import u2f = require("../../../src/lib/routes/U2FRoutes"); -import winston = require("winston"); - -import ExpressMock = require("../mocks/express"); -import UserDataStoreMock = require("../mocks/UserDataStore"); -import AuthdogMock = require("../mocks/authdog"); - -describe("test u2f routes", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let user_data_store: UserDataStoreMock.UserDataStore; - - beforeEach(function () { - req = ExpressMock.RequestMock(); - req.app = {}; - req.app.get = sinon.stub(); - req.app.get.withArgs("logger").returns(winston); - req.session = {}; - req.session.auth_session = {}; - req.session.auth_session.userid = "user"; - req.session.auth_session.first_factor = true; - req.session.auth_session.second_factor = false; - req.session.auth_session.identity_check = {}; - req.session.auth_session.identity_check.challenge = "u2f-register"; - req.session.auth_session.register_request = {}; - req.headers = {}; - req.headers.host = "localhost"; - - const options = { - inMemoryOnly: true - }; - - user_data_store = UserDataStoreMock.UserDataStore(); - user_data_store.set_u2f_meta = sinon.stub().returns(Promise.resolve({})); - user_data_store.get_u2f_meta = sinon.stub().returns(Promise.resolve({})); - req.app.get.withArgs("user data store").returns(user_data_store); - - res = ExpressMock.ResponseMock(); - res.send = sinon.spy(); - res.json = sinon.spy(); - res.status = sinon.spy(); - }); - - describe("test registration request", test_registration_request); - describe("test registration", test_registration); - describe("test signing request", test_signing_request); - describe("test signing", test_signing); - - function test_registration_request() { - it("should send back the registration request and save it in the session", function (done) { - const expectedRequest = { - test: "abc" - }; - res.json = sinon.spy(function (data: any) { - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(expectedRequest, data); - done(); - }); - const user_key_container = {}; - const u2f_mock = AuthdogMock.AuthdogMock(); - u2f_mock.startRegistration.returns(Promise.resolve(expectedRequest)); - - req.app.get.withArgs("u2f").returns(u2f_mock); - u2f.register_request(req as any, res as any, undefined); - }); - - it("should return internal error on registration request", function (done) { - res.send = sinon.spy(function (data: any) { - assert.equal(500, res.status.getCall(0).args[0]); - done(); - }); - const user_key_container = {}; - const u2f_mock = AuthdogMock.AuthdogMock(); - u2f_mock.startRegistration.returns(Promise.reject("Internal error")); - - req.app.get.withArgs("u2f").returns(u2f_mock); - u2f.register_request(req as any, res as any, undefined); - }); - - it("should return forbidden if identity has not been verified", function (done) { - res.send = sinon.spy(function (data: any) { - assert.equal(403, res.status.getCall(0).args[0]); - done(); - }); - req.session.auth_session.identity_check = undefined; - u2f.register_request(req as any, res as any, undefined); - }); - } - - function test_registration() { - it("should save u2f meta and return status code 200", function (done) { - const expectedStatus = { - keyHandle: "keyHandle", - publicKey: "pbk", - certificate: "cert" - }; - res.send = sinon.spy(function (data: any) { - assert.equal("user", user_data_store.set_u2f_meta.getCall(0).args[0]); - assert.equal(req.session.auth_session.identity_check, undefined); - done(); - }); - const u2f_mock = AuthdogMock.AuthdogMock(); - u2f_mock.finishRegistration.returns(Promise.resolve(expectedStatus)); - - req.session.auth_session.register_request = {}; - req.app.get.withArgs("u2f").returns(u2f_mock); - u2f.register(req as any, res as any, undefined); - }); - - it("should return unauthorized on finishRegistration error", function (done) { - res.send = sinon.spy(function (data: any) { - assert.equal(500, res.status.getCall(0).args[0]); - done(); - }); - const user_key_container = {}; - const u2f_mock = AuthdogMock.AuthdogMock(); - u2f_mock.finishRegistration.returns(Promise.reject("Internal error")); - - req.session.auth_session.register_request = "abc"; - req.app.get.withArgs("u2f").returns(u2f_mock); - u2f.register(req as any, res as any, undefined); - }); - - it("should return 403 when register_request is not provided", function (done) { - res.send = sinon.spy(function (data: any) { - assert.equal(403, res.status.getCall(0).args[0]); - done(); - }); - const user_key_container = {}; - const u2f_mock = AuthdogMock.AuthdogMock(); - u2f_mock.finishRegistration.returns(Promise.resolve()); - - req.session.auth_session.register_request = undefined; - req.app.get.withArgs("u2f").returns(u2f_mock); - u2f.register(req as any, res as any, undefined); - }); - - it("should return forbidden error when no auth request has been initiated", function (done) { - res.send = sinon.spy(function (data: any) { - assert.equal(403, res.status.getCall(0).args[0]); - done(); - }); - const user_key_container = {}; - const u2f_mock = AuthdogMock.AuthdogMock(); - u2f_mock.finishRegistration.returns(Promise.resolve()); - - req.session.auth_session.register_request = undefined; - req.app.get.withArgs("u2f").returns(u2f_mock); - u2f.register(req as any, res as any, undefined); - }); - - it("should return forbidden error when identity has not been verified", function (done) { - res.send = sinon.spy(function (data: any) { - assert.equal(403, res.status.getCall(0).args[0]); - done(); - }); - req.session.auth_session.identity_check = undefined; - u2f.register(req as any, res as any, undefined); - }); - } - - function test_signing_request() { - it("should send back the sign request and save it in the session", function (done) { - const expectedRequest = { - test: "abc" - }; - res.json = sinon.spy(function (data: any) { - assert.deepEqual(expectedRequest, req.session.auth_session.sign_request); - assert.equal(200, res.status.getCall(0).args[0]); - assert.deepEqual(expectedRequest, data); - done(); - }); - const user_key_container = { - user: {} - }; - const u2f_mock = AuthdogMock.AuthdogMock(); - u2f_mock.startAuthentication.returns(Promise.resolve(expectedRequest)); - - req.app.get.withArgs("u2f").returns(u2f_mock); - u2f.sign_request(req as any, res as any, undefined); - }); - - it("should return unauthorized error on registration request error", function (done) { - res.send = sinon.spy(function (data: any) { - assert.equal(500, res.status.getCall(0).args[0]); - done(); - }); - const user_key_container = { - user: {} - }; - const u2f_mock = AuthdogMock.AuthdogMock(); - u2f_mock.startAuthentication.returns(Promise.reject("Internal error")); - - req.app.get.withArgs("u2f").returns(u2f_mock); - u2f.sign_request(req as any, res as any, undefined); - }); - - it("should send unauthorized error when no registration exists", function (done) { - const expectedRequest = { - test: "abc" - }; - res.send = sinon.spy(function (data: any) { - assert.equal(401, res.status.getCall(0).args[0]); - done(); - }); - const user_key_container = {}; // no entry means no registration - const u2f_mock = AuthdogMock.AuthdogMock(); - u2f_mock.startAuthentication.returns(Promise.resolve(expectedRequest)); - - user_data_store.get_u2f_meta = sinon.stub().returns(Promise.resolve()); - - req.app.get = sinon.stub(); - req.app.get.withArgs("logger").returns(winston); - req.app.get.withArgs("user data store").returns(user_data_store); - req.app.get.withArgs("u2f").returns(u2f_mock); - u2f.sign_request(req as any, res as any, undefined); - }); - } - - function test_signing() { - it("should return status code 204", function (done) { - const user_key_container = { - user: {} - }; - const expectedStatus = { - keyHandle: "keyHandle", - publicKey: "pbk", - certificate: "cert" - }; - res.send = sinon.spy(function (data: any) { - assert(204, res.status.getCall(0).args[0]); - assert(req.session.auth_session.second_factor); - done(); - }); - const u2f_mock = AuthdogMock.AuthdogMock(); - u2f_mock.finishAuthentication.returns(Promise.resolve(expectedStatus)); - - req.session.auth_session.sign_request = {}; - req.app.get.withArgs("u2f").returns(u2f_mock); - u2f.sign(req as any, res as any, undefined); - }); - - it("should return unauthorized error on registration request internal error", function (done) { - res.send = sinon.spy(function (data: any) { - assert.equal(500, res.status.getCall(0).args[0]); - done(); - }); - const user_key_container = { - user: {} - }; - - const u2f_mock = AuthdogMock.AuthdogMock(); - u2f_mock.finishAuthentication.returns(Promise.reject("Internal error")); - - req.session.auth_session.sign_request = {}; - req.app.get.withArgs("u2f").returns(u2f_mock); - u2f.sign(req as any, res as any, undefined); - }); - - it("should return unauthorized error when no sign request has been initiated", function (done) { - res.send = sinon.spy(function (data: any) { - assert.equal(401, res.status.getCall(0).args[0]); - done(); - }); - const user_key_container = {}; - const u2f_mock = AuthdogMock.AuthdogMock(); - u2f_mock.finishAuthentication.returns(Promise.resolve()); - - req.app.get.withArgs("u2f").returns(u2f_mock); - u2f.sign(req as any, res as any, undefined); - }); - } -}); - diff --git a/tsconfig.json b/tsconfig.json index 547417dd4..f59e34c0e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,19 +2,20 @@ "compilerOptions": { "module": "commonjs", "target": "es6", - "noImplicitAny": true, "moduleResolution": "node", + "noImplicitAny": true, "sourceMap": true, + "removeComments": true, "outDir": "dist", "baseUrl": ".", "paths": { "*": [ - "src/types/*", - "node_modules/@types/*" + "./src/types/*", + "./node_modules/@types/*" ] } }, - "include": [ + "includes": [ "src/**/*", "test/**/*" ]

  • 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 diff --git a/images/u2f.png b/images/u2f.png index 6ca0beefcdfeb4a37110fe7ec661ad33c26d8a18..15abd4894bcf960efbfe6f786073b134935f573b 100644 GIT binary patch literal 22513 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 literal 42811 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