From ca9a0b072eabecfed69a9f4ac138ad09d4ada9bb Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 21 Jun 2017 15:17:44 -0400 Subject: [PATCH] duo 2fa config and login with web sdk --- gulpfile.js | 4 + package.json | 3 +- src/app/accounts/accountsLoginController.js | 17 +++++ .../views/accountsLoginTwoFactor.html | 27 +++++++ src/app/services/apiService.js | 2 + .../settingsTwoStepAuthenticatorController.js | 5 +- src/app/settings/settingsTwoStepController.js | 24 ++++-- .../settings/settingsTwoStepDuoController.js | 75 ++++++++++++++++++ .../settingsTwoStepEmailController.js | 3 +- .../settings/settingsTwoStepYubiController.js | 3 +- .../settings/views/settingsTwoStepDuo.html | 76 +++++++++++++++++++ src/images/loading.svg | 6 ++ src/index.html | 8 +- src/less/vault.less | 21 +++++ 14 files changed, 262 insertions(+), 12 deletions(-) create mode 100644 src/app/settings/settingsTwoStepDuoController.js create mode 100644 src/app/settings/views/settingsTwoStepDuo.html create mode 100644 src/images/loading.svg diff --git a/gulpfile.js b/gulpfile.js index 16186a93f68..090cdd4f2a1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -177,6 +177,10 @@ gulp.task('lib', ['clean:lib'], function () { paths.npmDir + 'angulartics/src/angulartics.js' ], dest: paths.libDir + 'angulartics' + }, + { + src: paths.npmDir + 'duo_web_sdk/index.js', + dest: paths.libDir + 'duo' } ]; diff --git a/package.json b/package.json index 03e840decde..4259a1f2dce 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "browserify": "14.1.0", "vinyl-source-stream": "1.1.0", "gulp-derequire": "2.1.0", - "exposify": "0.5.0" + "exposify": "0.5.0", + "duo_web_sdk": "git+https://github.com/duosecurity/duo_web_sdk.git" } } diff --git a/src/app/accounts/accountsLoginController.js b/src/app/accounts/accountsLoginController.js index 1917538c882..cc25604c2a6 100644 --- a/src/app/accounts/accountsLoginController.js +++ b/src/app/accounts/accountsLoginController.js @@ -57,6 +57,7 @@ angular $state.go('frontend.login.twoFactor', { returnState: returnState }).then(function () { $timeout(function () { $("#code").focus(); + init(); }); }); } @@ -90,6 +91,7 @@ angular $scope.twoFactorProvider = provider; $timeout(function () { $("#code").focus(); + init(); }); }); }; @@ -102,4 +104,19 @@ angular $state.go('backend.user.vault'); } } + + function init() { + if ($scope.twoFactorProvider === constants.twoFactorProvider.duo) { + var params = $scope.twoFactorProviders[constants.twoFactorProvider.duo]; + + Duo.init({ + host: params.Host, + sig_request: params.Signature, + submit_callback: function (theForm) { + var response = $(theForm).find('input[name="sig_response"]').val(); + $scope.twoFactor(response); + } + }); + } + } }); diff --git a/src/app/accounts/views/accountsLoginTwoFactor.html b/src/app/accounts/views/accountsLoginTwoFactor.html index ead9d22fc80..aef91aadf5f 100644 --- a/src/app/accounts/views/accountsLoginTwoFactor.html +++ b/src/app/accounts/views/accountsLoginTwoFactor.html @@ -57,3 +57,30 @@ + +
+ +
+
+

Errors have occurred

+
    +
  • {{e}}
  • +
+
+
+ +
+
+ +
+ +
+
+
+
diff --git a/src/app/services/apiService.js b/src/app/services/apiService.js index 511ec7f222b..27b7e404c18 100644 --- a/src/app/services/apiService.js +++ b/src/app/services/apiService.js @@ -114,11 +114,13 @@ _service.twoFactor = $resource(_apiUri + '/two-factor', {}, { list: { method: 'GET', params: {} }, getEmail: { url: _apiUri + '/two-factor/get-email', method: 'POST', params: {} }, + getDuo: { url: _apiUri + '/two-factor/get-duo', method: 'POST', params: {} }, getAuthenticator: { url: _apiUri + '/two-factor/get-authenticator', method: 'POST', params: {} }, getYubi: { url: _apiUri + '/two-factor/get-yubikey', method: 'POST', params: {} }, sendEmail: { url: _apiUri + '/two-factor/send-email', method: 'POST', params: {} }, putEmail: { url: _apiUri + '/two-factor/email', method: 'POST', params: {} }, putAuthenticator: { url: _apiUri + '/two-factor/authenticator', method: 'POST', params: {} }, + putDuo: { url: _apiUri + '/two-factor/duo', method: 'POST', params: {} }, putYubi: { url: _apiUri + '/two-factor/yubikey', method: 'POST', params: {} }, disable: { url: _apiUri + '/two-factor/disable', method: 'POST', params: {} }, recover: { url: _apiUri + '/two-factor/recover', method: 'POST', params: {} }, diff --git a/src/app/settings/settingsTwoStepAuthenticatorController.js b/src/app/settings/settingsTwoStepAuthenticatorController.js index bec1dfa4a78..97eb22cb43a 100644 --- a/src/app/settings/settingsTwoStepAuthenticatorController.js +++ b/src/app/settings/settingsTwoStepAuthenticatorController.js @@ -7,7 +7,7 @@ var _issuer = 'bitwarden', _profile = null, _masterPasswordHash - _key = null; + _key = null; $scope.auth = function (model) { _masterPasswordHash = cryptoService.hashPassword(model.masterPassword); @@ -69,6 +69,7 @@ }, function (response) { $analytics.eventTrack('Disabled Two-step Authenticator'); toastr.success('Authenticator app has been disabled.'); + $scope.enabled = response.Enabled; $scope.close(); }).$promise; } @@ -86,6 +87,6 @@ } $scope.close = function () { - $uibModalInstance.close(); + $uibModalInstance.close($scope.enabled); }; }); diff --git a/src/app/settings/settingsTwoStepController.js b/src/app/settings/settingsTwoStepController.js index 26aed1cb561..620c22f2192 100644 --- a/src/app/settings/settingsTwoStepController.js +++ b/src/app/settings/settingsTwoStepController.js @@ -65,8 +65,8 @@ } }); - authenticatorModal.result.then(function () { - + authenticatorModal.result.then(function (enabled) { + provider.enabled = enabled; }); } else if(provider.type === constants.twoFactorProvider.email) { @@ -79,8 +79,8 @@ } }); - emailModal.result.then(function () { - + emailModal.result.then(function (enabled) { + provider.enabled = enabled; }); } else if (provider.type === constants.twoFactorProvider.yubikey) { @@ -93,8 +93,22 @@ } }); - yubiModal.result.then(function () { + yubiModal.result.then(function (enabled) { + provider.enabled = enabled; + }); + } + else if (provider.type === constants.twoFactorProvider.duo) { + var yubiModal = $uibModal.open({ + animation: true, + templateUrl: 'app/settings/views/settingsTwoStepDuo.html', + controller: 'settingsTwoStepDuoController', + resolve: { + enabled: function () { return provider.enabled; } + } + }); + yubiModal.result.then(function (enabled) { + provider.enabled = enabled; }); } }; diff --git a/src/app/settings/settingsTwoStepDuoController.js b/src/app/settings/settingsTwoStepDuoController.js new file mode 100644 index 00000000000..e073d6b0467 --- /dev/null +++ b/src/app/settings/settingsTwoStepDuoController.js @@ -0,0 +1,75 @@ +angular + .module('bit.settings') + + .controller('settingsTwoStepDuoController', function ($scope, apiService, $uibModalInstance, cryptoService, + toastr, $analytics, constants) { + $analytics.eventTrack('settingsTwoStepDuoController', { category: 'Modal' }); + var _masterPasswordHash; + + $scope.updateModel = { + token: null, + host: null, + ikey: null, + skey: null + }; + + $scope.auth = function (model) { + _masterPasswordHash = cryptoService.hashPassword(model.masterPassword); + $scope.authPromise = apiService.twoFactor.getDuo({}, { + masterPasswordHash: _masterPasswordHash + }).$promise.then(function (apiResponse) { + processResult(apiResponse); + $scope.authed = true; + }); + }; + + $scope.submit = function (model) { + if ($scope.enabled) { + disable(); + return; + } + + update(model); + }; + + function disable() { + if (!confirm('Are you sure you want to disable the Duo provider?')) { + return; + } + + $scope.submitPromise = apiService.twoFactor.disable({}, { + masterPasswordHash: _masterPasswordHash, + type: constants.twoFactorProvider.duo + }, function (response) { + $analytics.eventTrack('Disabled Two-step Duo'); + toastr.success('Duo has been disabled.'); + $scope.enabled = response.Enabled; + $scope.close(); + }).$promise; + } + + function update(model) { + $scope.submitPromise = apiService.twoFactor.putDuo({}, { + integrationKey: model.ikey, + secretKey: model.skey, + host: model.host, + masterPasswordHash: _masterPasswordHash + }, function (response) { + $analytics.eventTrack('Enabled Two-step Duo'); + processResult(response); + }).$promise; + } + + function processResult(response) { + $scope.enabled = response.Enabled; + $scope.updateModel = { + ikey: response.IntegrationKey, + skey: response.SecretKey, + host: response.Host + }; + } + + $scope.close = function () { + $uibModalInstance.close($scope.enabled); + }; + }); diff --git a/src/app/settings/settingsTwoStepEmailController.js b/src/app/settings/settingsTwoStepEmailController.js index 04d30409bef..5eb0dfb8075 100644 --- a/src/app/settings/settingsTwoStepEmailController.js +++ b/src/app/settings/settingsTwoStepEmailController.js @@ -74,6 +74,7 @@ }, function (response) { $analytics.eventTrack('Disabled Two-step Email'); toastr.success('Email has been disabled.'); + $scope.enabled = response.Enabled; $scope.close(); }).$promise; } @@ -92,6 +93,6 @@ } $scope.close = function () { - $uibModalInstance.close(); + $uibModalInstance.close($scope.enabled); }; }); diff --git a/src/app/settings/settingsTwoStepYubiController.js b/src/app/settings/settingsTwoStepYubiController.js index 77943fe7361..a9f606c74b7 100644 --- a/src/app/settings/settingsTwoStepYubiController.js +++ b/src/app/settings/settingsTwoStepYubiController.js @@ -56,6 +56,7 @@ $scope.disableLoading = false; $analytics.eventTrack('Disabled Two-step YubiKey'); toastr.success('YubiKey has been disabled.'); + $scope.enabled = response.Enabled; $scope.close(); }, function (response) { toastr.error('Failed to disable.'); @@ -102,6 +103,6 @@ } $scope.close = function () { - $uibModalInstance.close(); + $uibModalInstance.close($scope.enabled); }; }); diff --git a/src/app/settings/views/settingsTwoStepDuo.html b/src/app/settings/views/settingsTwoStepDuo.html new file mode 100644 index 00000000000..82fc6a6ac42 --- /dev/null +++ b/src/app/settings/views/settingsTwoStepDuo.html @@ -0,0 +1,76 @@ + +
+ + +
+
+ + +
diff --git a/src/images/loading.svg b/src/images/loading.svg new file mode 100644 index 00000000000..70763105168 --- /dev/null +++ b/src/images/loading.svg @@ -0,0 +1,6 @@ + + + Loading... + + diff --git a/src/index.html b/src/index.html index 41f1e7f806b..c93b7243bf5 100644 --- a/src/index.html +++ b/src/index.html @@ -29,10 +29,12 @@ https://fonts.gstatic.com; child-src 'self' - https://js.stripe.com; + https://js.stripe.com + https://*.duosecurity.com; frame-src 'self' - https://js.stripe.com; + https://js.stripe.com + https://*.duosecurity.com; connect-src *;"> @@ -85,6 +87,7 @@ + @@ -190,6 +193,7 @@ + diff --git a/src/less/vault.less b/src/less/vault.less index 4697726081d..48c4ce67265 100644 --- a/src/less/vault.less +++ b/src/less/vault.less @@ -560,3 +560,24 @@ h1, h2, h3, h4, h5, h6 { background-position: -73px -88px; } } + +#duoFrameWrapper { + margin: 0 -36px 10px -36px; + background: ~"url('../images/loading.svg') 0 0 no-repeat"; + + @media (min-width: 360px) { + margin: 0 -10px 10px -10px; + } + + @media (min-width: @screen-sm) { + margin: 0 0 10px; + } + + iframe { + width: 100%; + min-width: 304px; + max-width: 620px; + height: 500px; + border: none; + } +}