Update 2017-07-26T17:32:55.910Z
15
app-id.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"trustedFacets": [
|
||||
{
|
||||
"version": {
|
||||
"major": 1,
|
||||
"minor": 0
|
||||
},
|
||||
"ids": [
|
||||
"https://vault.bitwarden.com",
|
||||
"ios:bundle-id:com.8bit.bitwarden",
|
||||
"android:apk-key-hash:dUGFzUzf3lmHSLBDBIv+WaFyZMI"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -36,7 +36,7 @@
|
||||
<hr />
|
||||
<ul>
|
||||
<li>
|
||||
<a ui-sref="frontend.register({returnState: state.params.returnState, email: state.params.email})">
|
||||
<a ui-sref="frontend.register({returnState: returnState, email: stateEmail})">
|
||||
Create a new account
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -1,25 +1,163 @@
|
||||
<p class="login-box-msg">Enter your two-step verification code.</p>
|
||||
<form name="twoFactorForm" ng-submit="twoFactorForm.$valid && twoFactor(model)" api-form="twoFactorPromise">
|
||||
<div class="callout callout-danger validation-errors" ng-show="twoFactorForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in twoFactorForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
<div ng-if="twoFactorProvider === twoFactorProviderConstants.authenticator ||
|
||||
twoFactorProvider === twoFactorProviderConstants.email">
|
||||
<p class="login-box-msg" ng-if="twoFactorProvider === twoFactorProviderConstants.authenticator">
|
||||
Enter the 6 digit verification code from your authenticator app.
|
||||
</p>
|
||||
<div ng-if="twoFactorProvider === twoFactorProviderConstants.email" class="text-center">
|
||||
<p class="login-box-msg">
|
||||
Enter the 6 digit verification code that was emailed to <b>{{twoFactorEmail}}</b>.
|
||||
</p>
|
||||
<p>
|
||||
Didn't get the email?
|
||||
<a href="#" stop-click ng-click="sendEmail(true)" ng-if="twoFactorProvider === twoFactorProviderConstants.email">
|
||||
Send it again
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group has-feedback" show-errors>
|
||||
<label for="code" class="sr-only">Code</label>
|
||||
<input type="text" id="code" name="Code" class="form-control" placeholder="Verification code" ng-model="model.code"
|
||||
required api-field />
|
||||
<span class="fa fa-lock form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-7">
|
||||
<a ui-sref="frontend.recover">Lost authenticator app?</a>
|
||||
<form name="twoFactorForm" ng-submit="twoFactorForm.$valid && twoFactor(token)" api-form="twoFactorPromise">
|
||||
<div class="callout callout-danger validation-errors" ng-show="twoFactorForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in twoFactorForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-5">
|
||||
<button type="submit" class="btn btn-primary btn-block btn-flat" ng-disabled="twoFactorForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="twoFactorForm.$loading"></i>Log In
|
||||
</button>
|
||||
<div class="form-group has-feedback" show-errors>
|
||||
<label for="code" class="sr-only">Code</label>
|
||||
<input type="text" id="code" name="Code" class="form-control" placeholder="Verification code"
|
||||
ng-model="token" required api-field />
|
||||
<span class="fa fa-lock form-control-feedback"></span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="row">
|
||||
<div class="col-xs-7">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="rememberMe" ng-model="rememberTwoFactor.checked" /> Remember Me
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-5">
|
||||
<button type="submit" class="btn btn-primary btn-block btn-flat" ng-disabled="twoFactorForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="twoFactorForm.$loading"></i>Log In
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div ng-if="twoFactorProvider === twoFactorProviderConstants.yubikey">
|
||||
<p class="login-box-msg">
|
||||
Complete logging in with YubiKey.
|
||||
</p>
|
||||
<form name="twoFactorForm" ng-submit="twoFactorForm.$valid && twoFactor(token)" api-form="twoFactorPromise">
|
||||
<div class="callout callout-danger validation-errors" ng-show="twoFactorForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in twoFactorForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>Insert your YubiKey into your computer's USB port, then touch its button.</p>
|
||||
<p>
|
||||
<img src="images/two-factor/yubikey.jpg" alt="" class="img-rounded img-responsive" />
|
||||
</p>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="code" class="sr-only">Token</label>
|
||||
<input type="password" id="code" name="Token" class="form-control" ng-model="token" required api-field />
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-7">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="rememberMe" ng-model="rememberTwoFactor.checked" /> Remember Me
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-5">
|
||||
<button type="submit" class="btn btn-primary btn-block btn-flat" ng-disabled="twoFactorForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="twoFactorForm.$loading"></i>Log In
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div ng-if="twoFactorProvider === twoFactorProviderConstants.duo">
|
||||
<p class="login-box-msg">
|
||||
Complete logging in with Duo.
|
||||
</p>
|
||||
<form name="twoFactorForm" ng-submit="twoFactorForm.$valid && twoFactor(token)" api-form="twoFactorPromise">
|
||||
<div class="callout callout-danger validation-errors" ng-show="twoFactorForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in twoFactorForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="duoFrameWrapper">
|
||||
<iframe id="duo_iframe"></iframe>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-7">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="rememberMe" ng-model="rememberTwoFactor.checked" /> Remember Me
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-5">
|
||||
<span ng-show="twoFactorForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon"></i> Logging in...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div ng-if="twoFactorProvider === twoFactorProviderConstants.u2f">
|
||||
<p class="login-box-msg">
|
||||
Complete logging in with FIDO U2F.
|
||||
</p>
|
||||
<form name="twoFactorForm" api-form="twoFactorPromise">
|
||||
<div class="callout callout-danger validation-errors" ng-show="twoFactorForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in twoFactorForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>Insert your Security Key into your computer's USB port. If it has a button, touch it.</p>
|
||||
<p>
|
||||
<img src="images/two-factor/u2fkey.jpg" alt="" class="img-rounded img-responsive" />
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-xs-7">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="rememberMe" ng-model="rememberTwoFactor.checked" /> Remember Me
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-5">
|
||||
<span ng-show="twoFactorForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon"></i> Logging in...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div ng-if="twoFactorProvider === null">
|
||||
<p>
|
||||
This account has two-step login enabled, however, none of the configured two-step providers are supported by this
|
||||
web browser.
|
||||
</p>
|
||||
Please use a supported web browser (such as Chrome) and/or add additional providers that are better supported
|
||||
across web browsers (such as an authenticator app).
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<ul>
|
||||
<li>
|
||||
<a stop-click href="#" ng-click="anotherMethod()">Use another two-step login method</a>
|
||||
</li>
|
||||
<li>
|
||||
<a ui-sref="frontend.login.info({returnState: returnState})">Back to log in</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
</div>
|
||||
<div class="login-box-body">
|
||||
<p class="login-box-msg">
|
||||
Lost your authenticator app?
|
||||
<a href="https://help.bitwarden.com/article/lost-two-step-device/" target="_blank">
|
||||
Help me!
|
||||
</a>
|
||||
In the event that you cannot access your account through your normal two-step login methods, you can use your
|
||||
two-step login recovery code to disable all two-step providers on your account.
|
||||
<a href="https://help.bitwarden.com/article/lost-two-step-device/" target="_blank">Learn more</a>
|
||||
</p>
|
||||
<div class="text-center" ng-show="success">
|
||||
<div class="callout callout-success">
|
||||
|
||||
25
app/accounts/views/accountsTwoFactorMethods.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title"><i class="fa fa-key"></i> Two-step Providers</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="list-group" ng-repeat="provider in providers | orderBy: 'displayOrder'">
|
||||
<a href="#" stop-click class="list-group-item" ng-click="choose(provider)">
|
||||
<img alt="{{::provider.name}}" ng-src="{{'images/two-factor/' + provider.image}}" class="pull-right hidden-xs" />
|
||||
<h4 class="list-group-item-heading">{{::provider.name}}</h4>
|
||||
<p class="list-group-item-text">{{::provider.description}}</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="list-group" style="margin-bottom: 0;">
|
||||
<a href="https://help.bitwarden.com/article/lost-two-step-device/" target="_blank" class="list-group-item">
|
||||
<h4 class="list-group-item-heading">Recovery Code</h4>
|
||||
<p class="list-group-item-text">
|
||||
Lost access to all of your two-factor providers? Use your recovery code to disable
|
||||
all two-factor providers from your account.
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
8
app/accounts/views/accountsVerifyEmail.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<div class="login-box">
|
||||
<div class="login-logo">
|
||||
<i class="fa fa-shield"></i> <b>bit</b>warden
|
||||
</div>
|
||||
<div class="login-box-body">
|
||||
Verifying email...
|
||||
</div>
|
||||
</div>
|
||||
@@ -6,7 +6,7 @@
|
||||
</section>
|
||||
<section class="content">
|
||||
<div class="callout callout-warning" ng-if="subscription && subscription.cancelled">
|
||||
<h4><i class="fa fa-warning"></i> Cancelled</h4>
|
||||
<h4><i class="fa fa-warning"></i> Canceled</h4>
|
||||
The subscription to this organization has been canceled.
|
||||
</div>
|
||||
<div class="callout callout-warning" ng-if="subscription && subscription.markedForCancel">
|
||||
@@ -19,7 +19,7 @@
|
||||
Reinstate Plan
|
||||
</button>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Plan</h3>
|
||||
</div>
|
||||
@@ -36,7 +36,10 @@
|
||||
<div class="col-sm-6">
|
||||
<dl>
|
||||
<dt>Status</dt>
|
||||
<dd style="text-transform: capitalize;">{{(subscription && subscription.status) || '-'}}</dd>
|
||||
<dd>
|
||||
<span style="text-transform: capitalize;">{{(subscription && subscription.status) || '-'}}</span>
|
||||
<span ng-if="subscription.markedForCancel">- marked for cancellation</span>
|
||||
</dd>
|
||||
<dt>Next Charge</dt>
|
||||
<dd>{{nextInvoice ? ((nextInvoice.date | date: format: mediumDate) + ', ' + (nextInvoice.amount | currency:'$')) : '-'}}</dd>
|
||||
</dl>
|
||||
@@ -78,7 +81,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">User Seats</h3>
|
||||
</div>
|
||||
@@ -99,7 +102,33 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box box-default" ng-if="storage">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Storage</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<p>
|
||||
You plan has a total of {{storage.maxGb}} GB of encrypted file storage.
|
||||
You are currently using {{storage.currentName}}.
|
||||
</p>
|
||||
<div class="progress" style="margin: 0;">
|
||||
<div class="progress-bar progress-bar-info" role="progressbar"
|
||||
aria-valuenow="{{storage.percentage}}" aria-valuemin="0" aria-valuemax="1"
|
||||
style="min-width: 50px; width: {{storage.percentage}}%;">
|
||||
{{storage.percentage}}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="adjustStorage(true)">
|
||||
Add Storage
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="adjustStorage(false)">
|
||||
Remove Storage
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Payment Method</h3>
|
||||
</div>
|
||||
@@ -122,7 +151,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Charges</h3>
|
||||
</div>
|
||||
@@ -140,7 +169,7 @@
|
||||
<td style="width: 200px">
|
||||
{{charge.date | date: format: mediumDate}}
|
||||
</td>
|
||||
<td>
|
||||
<td style="min-width: 150px">
|
||||
{{charge.paymentSource}}
|
||||
</td>
|
||||
<td style="width: 150px; text-transform: capitalize;">
|
||||
@@ -155,7 +184,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
Note: Any charges will appears on your credit card statement as <b>BITWARDEN</b>.
|
||||
Note: Any charges will appear on your statement as <b>BITWARDEN</b>.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -46,6 +46,11 @@
|
||||
<i class="fa fa-fw fa-pencil"></i> Edit
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" stop-click ng-click="attachments(login)">
|
||||
<i class="fa fa-fw fa-paperclip"></i> Attachments
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" stop-click ng-click="editCollections(login)">
|
||||
<i class="fa fa-fw fa-cubes"></i> Collections
|
||||
|
||||
@@ -85,27 +85,6 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Two-step Log In</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<p>
|
||||
Current Status:
|
||||
<span class="label bg-green" ng-show="model.twoFactorEnabled">ENABLED</span>
|
||||
<span class="label bg-gray" ng-show="!model.twoFactorEnabled">DISABLED</span>
|
||||
</p>
|
||||
<p>
|
||||
Two-step login helps keep your account more secure by requiring a code provided by an app on your mobile device
|
||||
while logging in (in addition to your master password).
|
||||
</p>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="twoFactor()">
|
||||
Manage Two-step Log In
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Organizations</h3>
|
||||
|
||||
153
app/settings/views/settingsBilling.html
Normal file
@@ -0,0 +1,153 @@
|
||||
<section class="content-header">
|
||||
<h1>Billing <small>manage your membership</small></h1>
|
||||
</section>
|
||||
<section class="content">
|
||||
<div class="callout callout-warning" ng-if="subscription && subscription.cancelled">
|
||||
<h4><i class="fa fa-warning"></i> Canceled</h4>
|
||||
The premium membership subscription has been canceled.
|
||||
</div>
|
||||
<div class="callout callout-warning" ng-if="subscription && subscription.markedForCancel">
|
||||
<h4><i class="fa fa-warning"></i> Pending Cancellation</h4>
|
||||
<p>
|
||||
The premium membership has been marked for cancellation at the end of the
|
||||
current billing period.
|
||||
</p>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="reinstate()">
|
||||
Reinstate
|
||||
</button>
|
||||
</div>
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Premium Membership</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<dl>
|
||||
<dt>Status</dt>
|
||||
<dd>
|
||||
<span style="text-transform: capitalize;">{{(subscription && subscription.status) || '-'}}</span>
|
||||
<span ng-if="subscription.markedForCancel">- marked for cancellation</span>
|
||||
</dd>
|
||||
<dt>Next Charge</dt>
|
||||
<dd>{{nextInvoice ? ((nextInvoice.date | date: format: mediumDate) + ', ' + (nextInvoice.amount | currency:'$')) : '-'}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<strong>Details</strong>
|
||||
<div ng-show="loading">
|
||||
Loading...
|
||||
</div>
|
||||
<div class="table-responsive" style="margin: 0;" ng-show="!loading">
|
||||
<table class="table" style="margin: 0;">
|
||||
<tbody>
|
||||
<tr ng-repeat="item in subscription.items">
|
||||
<td>
|
||||
{{item.name}} {{item.qty > 1 ? '×' + item.qty : ''}}
|
||||
@ {{item.amount | currency:'$'}}
|
||||
</td>
|
||||
<td class="text-right">{{(item.qty * item.amount) | currency:'$'}} /{{item.interval}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="cancel()"
|
||||
ng-if="!subscription.cancelled && !subscription.markedForCancel">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="reinstate()"
|
||||
ng-if="subscription.markedForCancel">
|
||||
Reinstate
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box box-default" ng-if="storage">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Storage</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<p>
|
||||
You membership has a total of {{storage.maxGb}} GB of encrypted file storage.
|
||||
You are currently using {{storage.currentName}}.
|
||||
</p>
|
||||
<div class="progress" style="margin: 0;">
|
||||
<div class="progress-bar progress-bar-info" role="progressbar"
|
||||
aria-valuenow="{{storage.percentage}}" aria-valuemin="0" aria-valuemax="1"
|
||||
style="min-width: 50px; width: {{storage.percentage}}%;">
|
||||
{{storage.percentage}}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="adjustStorage(true)">
|
||||
Add Storage
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="adjustStorage(false)">
|
||||
Remove Storage
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Payment Method</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div ng-show="loading">
|
||||
Loading...
|
||||
</div>
|
||||
<div ng-show="!loading && !paymentSource">
|
||||
<i class="fa fa-credit-card"></i> No payment method on file.
|
||||
</div>
|
||||
<div ng-show="!loading && paymentSource">
|
||||
<i class="fa" ng-class="{'fa-credit-card': paymentSource.type === 0,
|
||||
'fa-university': paymentSource.type === 1}"></i>
|
||||
{{paymentSource.description}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="changePayment()">
|
||||
{{ paymentSource ? 'Change Payment Method' : 'Add Payment Method' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Charges</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div ng-show="loading">
|
||||
Loading...
|
||||
</div>
|
||||
<div ng-show="!loading && !charges.length">
|
||||
No charges.
|
||||
</div>
|
||||
<div class="table-responsive" ng-show="charges.length">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr ng-repeat="charge in charges">
|
||||
<td style="width: 200px">
|
||||
{{charge.date | date: format: mediumDate}}
|
||||
</td>
|
||||
<td style="min-width: 150px">
|
||||
{{charge.paymentSource}}
|
||||
</td>
|
||||
<td style="width: 150px; text-transform: capitalize;">
|
||||
{{charge.status}}
|
||||
</td>
|
||||
<td class="text-right" style="width: 150px;">
|
||||
{{charge.amount | currency:'$'}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
Note: Any charges will appear on your statement as <b>BITWARDEN</b>.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
46
app/settings/views/settingsBillingAdjustStorage.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">
|
||||
<i class="fa fa-database"></i>
|
||||
{{add ? 'Add Storage' : 'Remove Storage'}}
|
||||
</h4>
|
||||
</div>
|
||||
<form name="form" ng-submit="form.$valid && submit()" api-form="submitPromise">
|
||||
<div class="modal-body">
|
||||
<div class="callout callout-default" ng-show="add">
|
||||
<h4><i class="fa fa-dollar"></i> Note About Charges</h4>
|
||||
<p>
|
||||
Adding storage to your plan will result in adjustments to your billing totals and immediately charge your
|
||||
payment method on file. The first charge will be prorated for the remainder of the current billing cycle.
|
||||
</p>
|
||||
</div>
|
||||
<div class="callout callout-default" ng-show="!add">
|
||||
<h4><i class="fa fa-dollar"></i> Note About Charges</h4>
|
||||
<p>
|
||||
Removing storage will result in adjustments to your billing totals that will be prorated as credits
|
||||
to your next billing charge.
|
||||
</p>
|
||||
</div>
|
||||
<div class="callout callout-danger validation-errors" ng-show="form.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in form.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="gb">{{add ? 'GB of Storage To Add' : 'GB of Storage To Remove'}}</label>
|
||||
<input type="number" id="gb" name="StroageGbAdjustment" ng-model="storageAdjustment" class="form-control"
|
||||
required min="0" max="99" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="form.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="form.$loading"></i>Submit
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -7,17 +7,17 @@
|
||||
for a specific entity (such as a family, small team, or large company).
|
||||
</p>
|
||||
<form name="createOrgForm" ng-submit="createOrgForm.$valid && submit(model)" api-form="submitPromise">
|
||||
<div class="callout callout-danger validation-errors" ng-show="createOrgForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in createOrgForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">General Information</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="callout callout-danger validation-errors" ng-show="createOrgForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in createOrgForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group" show-errors>
|
||||
@@ -75,6 +75,7 @@
|
||||
<span>For personal users such as families & friends.</span>
|
||||
<span>- Add and share with up to 10 users (5 included with base price)</span>
|
||||
<span>- Create unlimited collections</span>
|
||||
<span>- 1 GB encrypted file storage</span>
|
||||
<span>- Priority customer support</span>
|
||||
<span>- 7 day free trial, cancel anytime</span>
|
||||
<span class="bottom-line">
|
||||
@@ -90,6 +91,7 @@
|
||||
<span>For businesses and other team organizations.</span>
|
||||
<span>- Add and share with unlimited users</span>
|
||||
<span>- Create unlimited collections</span>
|
||||
<span>- 1 GB encrypted file storage</span>
|
||||
<span>- Priority customer support</span>
|
||||
<span>- 7 day free trial, cancel anytime</span>
|
||||
<span class="bottom-line">
|
||||
@@ -105,6 +107,7 @@
|
||||
<span>For businesses and other large organizations.</span>
|
||||
<span>- Add and share with unlimited users</span>
|
||||
<span>- Create unlimited collections</span>
|
||||
<span>- 1 GB encrypted file storage</span>
|
||||
<span>- Control user access with groups</span>
|
||||
<span>- Sync your users and groups from a directory (AD, Azure AD, GSuite, LDAP)</span>
|
||||
<span>- Priority customer support</span>
|
||||
@@ -166,6 +169,27 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box box-default" ng-if="!plans[model.plan].noPayment">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Additional Storage</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="form-group" show-errors style="margin: 0;">
|
||||
<p>
|
||||
Your plan comes with 1 GB of encrypted file storage. You can add additional
|
||||
storage for {{storageGb.price | currency:"$":2}} per GB /month.
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<label for="additionalStorage" class="sr-only">Storage</label>
|
||||
<input type="number" id="additionalStorage" name="AdditionalStorageGb"
|
||||
ng-model="model.additionalStorageGb" min="0" max="99" step="1" class="form-control"
|
||||
placeholder="# of additional GB" api-field />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box box-default" ng-if="!plans[model.plan].noPayment">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Billing Summary</h3>
|
||||
@@ -187,6 +211,12 @@
|
||||
×12 mo. =
|
||||
{{((model.additionalSeats || 0) * plans[model.plan].annualSeatPrice) | currency:"$":2}} /year
|
||||
</span>
|
||||
<span>
|
||||
Additional storage:
|
||||
{{model.additionalStorageGb || 0}} GB × {{storageGb.price | currency:"$":2}}
|
||||
×12 mo. =
|
||||
{{(model.additionalStorageGb || 0) * storageGb.yearlyPrice | currency:"$":2}} /year
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio radio-block" ng-if="model.plan !== 'personal'">
|
||||
@@ -204,6 +234,11 @@
|
||||
×{{plans[model.plan].monthlySeatPrice | currency:"$":2}} =
|
||||
{{((model.additionalSeats || 0) * plans[model.plan].monthlySeatPrice) | currency:"$":2}} /month
|
||||
</span>
|
||||
<span>
|
||||
Additional storage:
|
||||
{{model.additionalStorageGb || 0}} GB × {{storageGb.monthlyPrice | currency:"$":2}} =
|
||||
{{(model.additionalStorageGb || 0) * storageGb.monthlyPrice | currency:"$":2}} /month
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
444
app/settings/views/settingsPremium.html
Normal file
@@ -0,0 +1,444 @@
|
||||
<section class="content-header">
|
||||
<h1>Premium<span class="hidden-xs"> Membership</span><small>get started today!</small></h1>
|
||||
</section>
|
||||
<section class="content">
|
||||
<form name="form" ng-submit="form.$valid && submit(model)" api-form="submitPromise">
|
||||
<div class="callout callout-danger validation-errors" ng-show="form.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in form.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="box box-default">
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<p>Sign up for a premium membership and get:</p>
|
||||
<ul class="fa-ul">
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-green"></i>
|
||||
1 GB of encrypted file storage.
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-green"></i>
|
||||
Additional two-step login options such as YubiKey, FIDO U2F, and Duo.
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-green"></i>
|
||||
TOTP verification code (2FA) generator for logins in your vault.
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-green"></i>
|
||||
Priority customer support.
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-green"></i>
|
||||
All future premium features. More coming soon!
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
all for just<br />
|
||||
<span style="font-size: 30px;">{{premiumPrice | currency:"$":0}}</span> /year
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Addons</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="form-group" show-errors style="margin: 0;">
|
||||
<label for="additionalStorage">Storage</label>
|
||||
<p>
|
||||
Your plan comes with 1 GB of encrypted file storage. You can add additional
|
||||
storage for {{storageGbPrice | currency:"$":0}} per GB /year.
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<input type="number" id="additionalStorage" name="AdditionalStorageGb"
|
||||
ng-model="model.additionalStorageGb" min="0" max="99" step="1" class="form-control"
|
||||
placeholder="# of additional GB" api-field />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Billing Summary</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
Premium membership:
|
||||
{{premiumPrice | currency:"$"}}<br />
|
||||
Additional storage:
|
||||
{{model.additionalStorageGb || 0}} GB × {{storageGbPrice | currency:"$"}} =
|
||||
{{(model.additionalStorageGb || 0) * storageGbPrice | currency:"$"}}
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<h4>
|
||||
<b>Total:</b>
|
||||
{{totalPrice() | currency:"USD $"}} /year
|
||||
</h4>
|
||||
Your card will be charged immediately and on a recurring basis each year. You may cancel at any time.
|
||||
</div>
|
||||
</div>
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Payment Information</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<div class="form-group" show-errors>
|
||||
<label for="card_number">Card Number</label>
|
||||
<input type="text" id="card_number" name="card_number" ng-model="model.card.number"
|
||||
class="form-control" cc-number required api-field />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<br class="hidden-sm hidden-xs" />
|
||||
<ul class="list-inline" style="margin: 0;">
|
||||
<li><div class="cc visa"></div></li>
|
||||
<li><div class="cc mastercard"></div></li>
|
||||
<li><div class="cc amex"></div></li>
|
||||
<li><div class="cc discover"></div></li>
|
||||
<li><div class="cc diners"></div></li>
|
||||
<li><div class="cc jcb"></div></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group" show-errors>
|
||||
<label for="exp_month">Expiration Month</label>
|
||||
<select id="exp_month" class="form-control" ng-model="model.card.exp_month" required cc-exp-month
|
||||
name="exp_month" api-field>
|
||||
<option value="">-- Select --</option>
|
||||
<option value="01">01 - January</option>
|
||||
<option value="02">02 - February</option>
|
||||
<option value="03">03 - March</option>
|
||||
<option value="04">04 - April</option>
|
||||
<option value="05">05 - May</option>
|
||||
<option value="06">06 - June</option>
|
||||
<option value="07">07 - July</option>
|
||||
<option value="08">08 - August</option>
|
||||
<option value="09">09 - September</option>
|
||||
<option value="10">10 - October</option>
|
||||
<option value="11">11 - November</option>
|
||||
<option value="12">12 - December</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group" show-errors>
|
||||
<label for="exp_year">Expiration Year</label>
|
||||
<select id="exp_year" class="form-control" ng-model="model.card.exp_year" required cc-exp-year
|
||||
name="exp_year" api-field>
|
||||
<option value="">-- Select --</option>
|
||||
<option value="17">2017</option>
|
||||
<option value="18">2018</option>
|
||||
<option value="19">2019</option>
|
||||
<option value="20">2020</option>
|
||||
<option value="21">2021</option>
|
||||
<option value="22">2022</option>
|
||||
<option value="23">2023</option>
|
||||
<option value="24">2024</option>
|
||||
<option value="25">2025</option>
|
||||
<option value="26">2026</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group" show-errors>
|
||||
<label for="cvc">
|
||||
CVC
|
||||
<a href="https://www.cvvnumber.com/cvv.html" target="_blank" title="What is this?"
|
||||
rel="noopener noreferrer">
|
||||
<i class="fa fa-question-circle"></i>
|
||||
</a>
|
||||
</label>
|
||||
<input type="text" id="cvc" ng-model="model.card.cvc" class="form-control" name="cvc"
|
||||
cc-type="number.$ccType" cc-cvc required api-field />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group" show-errors>
|
||||
<label for="address_country">Country</label>
|
||||
<select id="address_country" class="form-control" ng-model="model.card.address_country"
|
||||
required name="address_country" api-field>
|
||||
<option value="">-- Select --</option>
|
||||
<option value="US">United States</option>
|
||||
<option value="CN">China</option>
|
||||
<option value="FR">France</option>
|
||||
<option value="DE">Germany</option>
|
||||
<option value="CA">Canada</option>
|
||||
<option value="GB">United Kingdom</option>
|
||||
<option value="AU">Australia</option>
|
||||
<option value="IN">India</option>
|
||||
<option value="-" disabled></option>
|
||||
<option value="AF">Afghanistan</option>
|
||||
<option value="AX">Åland Islands</option>
|
||||
<option value="AL">Albania</option>
|
||||
<option value="DZ">Algeria</option>
|
||||
<option value="AS">American Samoa</option>
|
||||
<option value="AD">Andorra</option>
|
||||
<option value="AO">Angola</option>
|
||||
<option value="AI">Anguilla</option>
|
||||
<option value="AQ">Antarctica</option>
|
||||
<option value="AG">Antigua and Barbuda</option>
|
||||
<option value="AR">Argentina</option>
|
||||
<option value="AM">Armenia</option>
|
||||
<option value="AW">Aruba</option>
|
||||
<option value="AT">Austria</option>
|
||||
<option value="AZ">Azerbaijan</option>
|
||||
<option value="BS">Bahamas</option>
|
||||
<option value="BH">Bahrain</option>
|
||||
<option value="BD">Bangladesh</option>
|
||||
<option value="BB">Barbados</option>
|
||||
<option value="BY">Belarus</option>
|
||||
<option value="BE">Belgium</option>
|
||||
<option value="BZ">Belize</option>
|
||||
<option value="BJ">Benin</option>
|
||||
<option value="BM">Bermuda</option>
|
||||
<option value="BT">Bhutan</option>
|
||||
<option value="BO">Bolivia, Plurinational State of</option>
|
||||
<option value="BQ">Bonaire, Sint Eustatius and Saba</option>
|
||||
<option value="BA">Bosnia and Herzegovina</option>
|
||||
<option value="BW">Botswana</option>
|
||||
<option value="BV">Bouvet Island</option>
|
||||
<option value="BR">Brazil</option>
|
||||
<option value="IO">British Indian Ocean Territory</option>
|
||||
<option value="BN">Brunei Darussalam</option>
|
||||
<option value="BG">Bulgaria</option>
|
||||
<option value="BF">Burkina Faso</option>
|
||||
<option value="BI">Burundi</option>
|
||||
<option value="KH">Cambodia</option>
|
||||
<option value="CM">Cameroon</option>
|
||||
<option value="CV">Cape Verde</option>
|
||||
<option value="KY">Cayman Islands</option>
|
||||
<option value="CF">Central African Republic</option>
|
||||
<option value="TD">Chad</option>
|
||||
<option value="CL">Chile</option>
|
||||
<option value="CX">Christmas Island</option>
|
||||
<option value="CC">Cocos (Keeling) Islands</option>
|
||||
<option value="CO">Colombia</option>
|
||||
<option value="KM">Comoros</option>
|
||||
<option value="CG">Congo</option>
|
||||
<option value="CD">Congo, the Democratic Republic of the</option>
|
||||
<option value="CK">Cook Islands</option>
|
||||
<option value="CR">Costa Rica</option>
|
||||
<option value="CI">Côte d'Ivoire</option>
|
||||
<option value="HR">Croatia</option>
|
||||
<option value="CU">Cuba</option>
|
||||
<option value="CW">Curaçao</option>
|
||||
<option value="CY">Cyprus</option>
|
||||
<option value="CZ">Czech Republic</option>
|
||||
<option value="DK">Denmark</option>
|
||||
<option value="DJ">Djibouti</option>
|
||||
<option value="DM">Dominica</option>
|
||||
<option value="DO">Dominican Republic</option>
|
||||
<option value="EC">Ecuador</option>
|
||||
<option value="EG">Egypt</option>
|
||||
<option value="SV">El Salvador</option>
|
||||
<option value="GQ">Equatorial Guinea</option>
|
||||
<option value="ER">Eritrea</option>
|
||||
<option value="EE">Estonia</option>
|
||||
<option value="ET">Ethiopia</option>
|
||||
<option value="FK">Falkland Islands (Malvinas)</option>
|
||||
<option value="FO">Faroe Islands</option>
|
||||
<option value="FJ">Fiji</option>
|
||||
<option value="FI">Finland</option>
|
||||
<option value="GF">French Guiana</option>
|
||||
<option value="PF">French Polynesia</option>
|
||||
<option value="TF">French Southern Territories</option>
|
||||
<option value="GA">Gabon</option>
|
||||
<option value="GM">Gambia</option>
|
||||
<option value="GE">Georgia</option>
|
||||
<option value="GH">Ghana</option>
|
||||
<option value="GI">Gibraltar</option>
|
||||
<option value="GR">Greece</option>
|
||||
<option value="GL">Greenland</option>
|
||||
<option value="GD">Grenada</option>
|
||||
<option value="GP">Guadeloupe</option>
|
||||
<option value="GU">Guam</option>
|
||||
<option value="GT">Guatemala</option>
|
||||
<option value="GG">Guernsey</option>
|
||||
<option value="GN">Guinea</option>
|
||||
<option value="GW">Guinea-Bissau</option>
|
||||
<option value="GY">Guyana</option>
|
||||
<option value="HT">Haiti</option>
|
||||
<option value="HM">Heard Island and McDonald Islands</option>
|
||||
<option value="VA">Holy See (Vatican City State)</option>
|
||||
<option value="HN">Honduras</option>
|
||||
<option value="HK">Hong Kong</option>
|
||||
<option value="HU">Hungary</option>
|
||||
<option value="IS">Iceland</option>
|
||||
<option value="ID">Indonesia</option>
|
||||
<option value="IR">Iran, Islamic Republic of</option>
|
||||
<option value="IQ">Iraq</option>
|
||||
<option value="IE">Ireland</option>
|
||||
<option value="IM">Isle of Man</option>
|
||||
<option value="IL">Israel</option>
|
||||
<option value="IT">Italy</option>
|
||||
<option value="JM">Jamaica</option>
|
||||
<option value="JP">Japan</option>
|
||||
<option value="JE">Jersey</option>
|
||||
<option value="JO">Jordan</option>
|
||||
<option value="KZ">Kazakhstan</option>
|
||||
<option value="KE">Kenya</option>
|
||||
<option value="KI">Kiribati</option>
|
||||
<option value="KP">Korea, Democratic People's Republic of</option>
|
||||
<option value="KR">Korea, Republic of</option>
|
||||
<option value="KW">Kuwait</option>
|
||||
<option value="KG">Kyrgyzstan</option>
|
||||
<option value="LA">Lao People's Democratic Republic</option>
|
||||
<option value="LV">Latvia</option>
|
||||
<option value="LB">Lebanon</option>
|
||||
<option value="LS">Lesotho</option>
|
||||
<option value="LR">Liberia</option>
|
||||
<option value="LY">Libya</option>
|
||||
<option value="LI">Liechtenstein</option>
|
||||
<option value="LT">Lithuania</option>
|
||||
<option value="LU">Luxembourg</option>
|
||||
<option value="MO">Macao</option>
|
||||
<option value="MK">Macedonia, the former Yugoslav Republic of</option>
|
||||
<option value="MG">Madagascar</option>
|
||||
<option value="MW">Malawi</option>
|
||||
<option value="MY">Malaysia</option>
|
||||
<option value="MV">Maldives</option>
|
||||
<option value="ML">Mali</option>
|
||||
<option value="MT">Malta</option>
|
||||
<option value="MH">Marshall Islands</option>
|
||||
<option value="MQ">Martinique</option>
|
||||
<option value="MR">Mauritania</option>
|
||||
<option value="MU">Mauritius</option>
|
||||
<option value="YT">Mayotte</option>
|
||||
<option value="MX">Mexico</option>
|
||||
<option value="FM">Micronesia, Federated States of</option>
|
||||
<option value="MD">Moldova, Republic of</option>
|
||||
<option value="MC">Monaco</option>
|
||||
<option value="MN">Mongolia</option>
|
||||
<option value="ME">Montenegro</option>
|
||||
<option value="MS">Montserrat</option>
|
||||
<option value="MA">Morocco</option>
|
||||
<option value="MZ">Mozambique</option>
|
||||
<option value="MM">Myanmar</option>
|
||||
<option value="NA">Namibia</option>
|
||||
<option value="NR">Nauru</option>
|
||||
<option value="NP">Nepal</option>
|
||||
<option value="NL">Netherlands</option>
|
||||
<option value="NC">New Caledonia</option>
|
||||
<option value="NZ">New Zealand</option>
|
||||
<option value="NI">Nicaragua</option>
|
||||
<option value="NE">Niger</option>
|
||||
<option value="NG">Nigeria</option>
|
||||
<option value="NU">Niue</option>
|
||||
<option value="NF">Norfolk Island</option>
|
||||
<option value="MP">Northern Mariana Islands</option>
|
||||
<option value="NO">Norway</option>
|
||||
<option value="OM">Oman</option>
|
||||
<option value="PK">Pakistan</option>
|
||||
<option value="PW">Palau</option>
|
||||
<option value="PS">Palestinian Territory, Occupied</option>
|
||||
<option value="PA">Panama</option>
|
||||
<option value="PG">Papua New Guinea</option>
|
||||
<option value="PY">Paraguay</option>
|
||||
<option value="PE">Peru</option>
|
||||
<option value="PH">Philippines</option>
|
||||
<option value="PN">Pitcairn</option>
|
||||
<option value="PL">Poland</option>
|
||||
<option value="PT">Portugal</option>
|
||||
<option value="PR">Puerto Rico</option>
|
||||
<option value="QA">Qatar</option>
|
||||
<option value="RE">Réunion</option>
|
||||
<option value="RO">Romania</option>
|
||||
<option value="RU">Russian Federation</option>
|
||||
<option value="RW">Rwanda</option>
|
||||
<option value="BL">Saint Barthélemy</option>
|
||||
<option value="SH">Saint Helena, Ascension and Tristan da Cunha</option>
|
||||
<option value="KN">Saint Kitts and Nevis</option>
|
||||
<option value="LC">Saint Lucia</option>
|
||||
<option value="MF">Saint Martin (French part)</option>
|
||||
<option value="PM">Saint Pierre and Miquelon</option>
|
||||
<option value="VC">Saint Vincent and the Grenadines</option>
|
||||
<option value="WS">Samoa</option>
|
||||
<option value="SM">San Marino</option>
|
||||
<option value="ST">Sao Tome and Principe</option>
|
||||
<option value="SA">Saudi Arabia</option>
|
||||
<option value="SN">Senegal</option>
|
||||
<option value="RS">Serbia</option>
|
||||
<option value="SC">Seychelles</option>
|
||||
<option value="SL">Sierra Leone</option>
|
||||
<option value="SG">Singapore</option>
|
||||
<option value="SX">Sint Maarten (Dutch part)</option>
|
||||
<option value="SK">Slovakia</option>
|
||||
<option value="SI">Slovenia</option>
|
||||
<option value="SB">Solomon Islands</option>
|
||||
<option value="SO">Somalia</option>
|
||||
<option value="ZA">South Africa</option>
|
||||
<option value="GS">South Georgia and the South Sandwich Islands</option>
|
||||
<option value="SS">South Sudan</option>
|
||||
<option value="ES">Spain</option>
|
||||
<option value="LK">Sri Lanka</option>
|
||||
<option value="SD">Sudan</option>
|
||||
<option value="SR">Suriname</option>
|
||||
<option value="SJ">Svalbard and Jan Mayen</option>
|
||||
<option value="SZ">Swaziland</option>
|
||||
<option value="SE">Sweden</option>
|
||||
<option value="CH">Switzerland</option>
|
||||
<option value="SY">Syrian Arab Republic</option>
|
||||
<option value="TW">Taiwan, Province of China</option>
|
||||
<option value="TJ">Tajikistan</option>
|
||||
<option value="TZ">Tanzania, United Republic of</option>
|
||||
<option value="TH">Thailand</option>
|
||||
<option value="TL">Timor-Leste</option>
|
||||
<option value="TG">Togo</option>
|
||||
<option value="TK">Tokelau</option>
|
||||
<option value="TO">Tonga</option>
|
||||
<option value="TT">Trinidad and Tobago</option>
|
||||
<option value="TN">Tunisia</option>
|
||||
<option value="TR">Turkey</option>
|
||||
<option value="TM">Turkmenistan</option>
|
||||
<option value="TC">Turks and Caicos Islands</option>
|
||||
<option value="TV">Tuvalu</option>
|
||||
<option value="UG">Uganda</option>
|
||||
<option value="UA">Ukraine</option>
|
||||
<option value="AE">United Arab Emirates</option>
|
||||
<option value="UM">United States Minor Outlying Islands</option>
|
||||
<option value="UY">Uruguay</option>
|
||||
<option value="UZ">Uzbekistan</option>
|
||||
<option value="VU">Vanuatu</option>
|
||||
<option value="VE">Venezuela, Bolivarian Republic of</option>
|
||||
<option value="VN">Viet Nam</option>
|
||||
<option value="VG">Virgin Islands, British</option>
|
||||
<option value="VI">Virgin Islands, U.S.</option>
|
||||
<option value="WF">Wallis and Futuna</option>
|
||||
<option value="EH">Western Sahara</option>
|
||||
<option value="YE">Yemen</option>
|
||||
<option value="ZM">Zambia</option>
|
||||
<option value="ZW">Zimbabwe</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group" show-errors>
|
||||
<label for="address_zip"
|
||||
ng-bind="model.card.address_country === 'US' ? 'Zip Code' : 'Postal Code'"></label>
|
||||
<input type="text" id="address_zip" ng-model="model.card.address_zip"
|
||||
class="form-control" required name="address_zip" api-field />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="form.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="form.$loading"></i>Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
@@ -6,10 +6,14 @@
|
||||
<div class="modal-body">
|
||||
<p>Concerned your account is logged in on another device?</p>
|
||||
<p>Proceed below to deauthorize all computers or devices that you have previously used.</p>
|
||||
<p>This security step is recommended if you previously used a public PC or accidentally saved your password on a device that isn't yours.</p>
|
||||
<p>
|
||||
This security step is recommended if you previously used a public PC or accidentally saved your password
|
||||
on a device that isn't yours. This step will also clear all previously remembered two-step login sessions.
|
||||
</p>
|
||||
<div class="callout callout-warning">
|
||||
<h4><i class="fa fa-warning"></i> Warning</h4>
|
||||
Proceeding will log you out of your current session as well, requiring you to log back in.
|
||||
Proceeding will also log you out of your current session, requiring you to log back in. You will also be prompted
|
||||
for two-step login again, if enabled.
|
||||
</div>
|
||||
<div class="callout callout-danger validation-errors" ng-show="logoutSessionsForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="twoFactorModelLabel"><i class="fa fa-key"></i> Two-step Log In</h4>
|
||||
</div>
|
||||
<form name="authTwoStepForm" ng-submit="authTwoStepForm.$valid && auth(authModel)" api-form="authPromise"
|
||||
ng-if="!twoFactorModel">
|
||||
<div class="modal-body">
|
||||
<p ng-show="enabled()">
|
||||
Two-step log in is already enabled on your account. To access your two-step
|
||||
settings enter your master password below.
|
||||
</p>
|
||||
<p ng-show="!enabled()">To get started with two-step log in enter your master password below.</p>
|
||||
<div class="callout callout-danger validation-errors" ng-show="authTwoStepForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in authTwoStepForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="masterPassword">Master Password</label>
|
||||
<input type="password" id="masterPassword" name="MasterPasswordHash" ng-model="authModel.masterPassword"
|
||||
class="form-control" required api-field />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="authTwoStepForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="authTwoStepForm.$loading"></i>Continue
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
<form name="updateTwoStepForm" ng-submit="updateTwoStepForm.$valid && update(updateModel)" api-form="updatePromise"
|
||||
ng-if="twoFactorModel">
|
||||
<div class="modal-body">
|
||||
<div ng-show="enabled()">
|
||||
<div class="callout callout-success">
|
||||
<h4><i class="fa fa-check-circle"></i> Enabled</h4>
|
||||
<p>
|
||||
Two-step log in is enabled on your account. Incase you need to add it to another device, below is the QR
|
||||
code (or key) required by your verification app.
|
||||
</p>
|
||||
</div>
|
||||
<p>Need a two-step verification app? Download one of the following:</p>
|
||||
</div>
|
||||
<div ng-show="!enabled()">
|
||||
<p>Setting up two-step verification is easy, just follow these steps:</p>
|
||||
<h4>1. Download a two-step verification app</h4>
|
||||
</div>
|
||||
<ul class="fa-ul">
|
||||
<li>
|
||||
<i class="fa-li fa fa-apple fa-lg"></i>
|
||||
iOS devices:
|
||||
<a href="https://itunes.apple.com/us/app/authy/id494168017?mt=8" target="_blank">
|
||||
Authy for iOS
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-android fa-lg"></i>
|
||||
Android devices:
|
||||
<a href="https://play.google.com/store/apps/details?id=com.authy.authy" target="_blank">
|
||||
Authy for Android
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-windows fa-lg"></i>
|
||||
Windows devices:
|
||||
<a href="https://www.microsoft.com/en-us/store/apps/authenticator/9wzdncrfj3rj" target="_blank">
|
||||
Microsoft Authenticator
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p>These apps are recommended, however, other authenticator apps will also work.</p>
|
||||
<hr ng-show="enabled()" />
|
||||
<h4 ng-show="!enabled()" style="margin-top: 30px;">2. Scan this QR code with your verification app</h4>
|
||||
<div class="row">
|
||||
<div class="col-md-4 text-center">
|
||||
<p><img ng-src="{{twoFactorModel.qr}}" alt="QR" class="img-thumbnail" /></p>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<p>
|
||||
<strong>Can't scan the code?</strong> You can add the code to your application manually using the
|
||||
following details:
|
||||
</p>
|
||||
<ul class="list-unstyled">
|
||||
<li><strong>Key:</strong> <code>{{twoFactorModel.key}}</code></li>
|
||||
<li><strong>Account:</strong> {{account}}</li>
|
||||
<li><strong>Time based:</strong> Yes</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="enabled()">
|
||||
<hr />
|
||||
<div class="callout callout-warning">
|
||||
<h4><i class="fa fa-warning"></i> Recovery Code <i class="fa fa-warning"></i></h4>
|
||||
<p>
|
||||
The recovery code allows you to access your account in the event that you lose your authenticator app.
|
||||
bitwarden support won't be able to assist you if you lose access to your account. We recommend
|
||||
you write down or print the recovery code below and keep it in a safe place.
|
||||
</p>
|
||||
<p><strong>Recovery Code:</strong> <code>{{twoFactorModel.recovery}}</code></p>
|
||||
<button type="button" class="btn btn-default" ng-click="print(twoFactorModel.recovery)">
|
||||
<i class="fa fa-print"></i> Print
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr ng-show="enabled()" />
|
||||
<div class="callout callout-danger validation-errors" ng-show="updateTwoStepForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in updateTwoStepForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h4 style="margin-top: 30px;">
|
||||
<span ng-show="enabled()">Want to disable? </span><span ng-show="!enabled()">3. </span>
|
||||
Enter the resulting verification code from the app
|
||||
</h4>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="token" class="sr-only">Verification Code</label>
|
||||
<input type="text" id="token" name="Token" placeholder="Verification Code" ng-model="updateModel.token"
|
||||
class="form-control" required api-field />
|
||||
</div>
|
||||
<p ng-show="!enabled()">
|
||||
NOTE: After enabling two-step log in, you will be required to enter the current code
|
||||
generated by your verification app each time you log in.
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="updateTwoStepForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="updateTwoStepForm.$loading"></i>
|
||||
<span ng-show="enabled()">Disable Two-step</span>
|
||||
<span ng-show="!enabled()">Enable Two-step</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
52
app/settings/views/settingsTwoStep.html
Normal file
@@ -0,0 +1,52 @@
|
||||
<section class="content-header">
|
||||
<h1>Two-step Login <small>secure your account</small></h1>
|
||||
</section>
|
||||
<section class="content">
|
||||
<div class="box box-danger">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-warning"></i> Recovery Code <i class="fa fa-warning"></i></h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
The recovery code allows you to access your account in the event that you can no longer use your normal
|
||||
two-step login provider (ex. you lose your device). bitwarden support will not be able to assist you if you lose
|
||||
access to your account. We recommend you write down or print the recovery code and keep it in a safe place.
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="viewRecover()">View Recovery Code</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Providers</h3>
|
||||
</div>
|
||||
<div class="box-body no-padding">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover table-vmiddle">
|
||||
<tbody>
|
||||
<tr ng-repeat="provider in providers | orderBy: 'displayOrder'">
|
||||
<td style="width: 120px; height: 75px;" align="center">
|
||||
<a href="#" stop-click ng-click="edit(provider)">
|
||||
<img alt="{{::provider.name}}" ng-src="{{'images/two-factor/' + provider.image}}" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" stop-click ng-click="edit(provider)">
|
||||
{{::provider.name}}
|
||||
<span class="label label-info" ng-if="!premium && !provider.free"
|
||||
style="margin-left: 5px;">PREMIUM</span>
|
||||
</a>
|
||||
<div class="text-muted text-sm">{{::provider.description}}</div>
|
||||
</td>
|
||||
<td style="width: 100px;" class="text-right">
|
||||
<span class="label label-full"
|
||||
ng-class="{ 'label-success': provider.enabled, 'label-default': !provider.enabled }">
|
||||
{{provider.enabled ? 'Enabled' : 'Disabled'}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
116
app/settings/views/settingsTwoStepAuthenticator.html
Normal file
@@ -0,0 +1,116 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">
|
||||
<i class="fa fa-key"></i> Two-step Login <small>authenticator app</small>
|
||||
</h4>
|
||||
</div>
|
||||
<form name="authTwoStepForm" ng-submit="authTwoStepForm.$valid && auth(authModel)" api-form="authPromise"
|
||||
ng-if="!model">
|
||||
<div class="modal-body">
|
||||
<p>Enter your master password to modify two-step login settings.</p>
|
||||
<div class="callout callout-danger validation-errors" ng-show="authTwoStepForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in authTwoStepForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="masterPassword">Master Password</label>
|
||||
<input type="password" id="masterPassword" name="MasterPasswordHash" ng-model="authModel.masterPassword"
|
||||
class="form-control" required api-field />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="authTwoStepForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="authTwoStepForm.$loading"></i>Continue
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
<form name="submitTwoStepForm" ng-submit="submitTwoStepForm.$valid && submit(updateModel)" api-form="submitPromise"
|
||||
ng-if="model">
|
||||
<div class="modal-body">
|
||||
<div ng-if="enabled">
|
||||
<div class="callout callout-success">
|
||||
<h4><i class="fa fa-check-circle"></i> Enabled</h4>
|
||||
<p>
|
||||
Two-step login via authenticator app is enabled on your account.
|
||||
</p>
|
||||
<p>
|
||||
In case you need to add it to another device, below is the QR code (or key) required by your
|
||||
authenticator app.
|
||||
</p>
|
||||
</div>
|
||||
<p>Need a two-step authenticator app? Download one of the following:</p>
|
||||
</div>
|
||||
<div ng-if="!enabled">
|
||||
<p>Setting up two-step login with an authenticator app is easy, just follow these steps:</p>
|
||||
<h4>1. Download a two-step authenticator app</h4>
|
||||
</div>
|
||||
<ul class="fa-ul">
|
||||
<li>
|
||||
<i class="fa-li fa fa-apple fa-lg"></i>
|
||||
iOS devices:
|
||||
<a href="https://itunes.apple.com/us/app/authy/id494168017?mt=8" target="_blank">
|
||||
Authy for iOS
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-android fa-lg"></i>
|
||||
Android devices:
|
||||
<a href="https://play.google.com/store/apps/details?id=com.authy.authy" target="_blank">
|
||||
Authy for Android
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-windows fa-lg"></i>
|
||||
Windows devices:
|
||||
<a href="https://www.microsoft.com/en-us/store/apps/authenticator/9wzdncrfj3rj" target="_blank">
|
||||
Microsoft Authenticator
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p>These apps are recommended, however, other authenticator apps will also work.</p>
|
||||
<hr ng-if="enabled" />
|
||||
<h4 ng-if="!enabled" style="margin-top: 30px;">2. Scan this QR code with your authenticator app</h4>
|
||||
<div class="row">
|
||||
<div class="col-md-4 text-center">
|
||||
<p><img ng-src="{{model.qr}}" alt="QR" class="img-thumbnail" /></p>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<p>
|
||||
<strong>Can't scan the code?</strong> You can add the code to your application manually using the
|
||||
following details:
|
||||
</p>
|
||||
<ul class="list-unstyled">
|
||||
<li><strong>Key:</strong> <code>{{model.key}}</code></li>
|
||||
<li><strong>Account:</strong> {{account}}</li>
|
||||
<li><strong>Time based:</strong> Yes</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="!enabled">
|
||||
<h4 style="margin-top: 30px;">
|
||||
3. Enter the resulting 6 digit verification code from the app
|
||||
</h4>
|
||||
<div class="callout callout-danger validation-errors" ng-show="submitTwoStepForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in submitTwoStepForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="token" class="sr-only">Verification Code</label>
|
||||
<input type="text" id="token" name="Token" placeholder="Verification Code" ng-model="updateModel.token"
|
||||
class="form-control" required api-field />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="submitTwoStepForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="submitTwoStepForm.$loading"></i>
|
||||
{{enabled ? 'Disable' : 'Enable'}}
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
76
app/settings/views/settingsTwoStepDuo.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">
|
||||
<i class="fa fa-key"></i> Two-step Login <small>duo</small>
|
||||
</h4>
|
||||
</div>
|
||||
<form name="authTwoStepForm" ng-submit="authTwoStepForm.$valid && auth(authModel)" api-form="authPromise"
|
||||
ng-if="!authed">
|
||||
<div class="modal-body">
|
||||
<p>Enter your master password to modify two-step login settings.</p>
|
||||
<div class="callout callout-danger validation-errors" ng-show="authTwoStepForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in authTwoStepForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="masterPassword">Master Password</label>
|
||||
<input type="password" id="masterPassword" name="MasterPasswordHash" ng-model="authModel.masterPassword"
|
||||
class="form-control" required api-field />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="authTwoStepForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="authTwoStepForm.$loading"></i>Continue
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
<form name="submitTwoStepForm" ng-submit="submitTwoStepForm.$valid && submit(updateModel)" api-form="submitPromise"
|
||||
ng-if="authed">
|
||||
<div class="modal-body">
|
||||
<div ng-if="enabled">
|
||||
<div class="callout callout-success">
|
||||
<h4><i class="fa fa-check-circle"></i> Enabled</h4>
|
||||
<p>Two-step log via Duo is enabled on your account.</p>
|
||||
</div>
|
||||
<ul class="list-unstyled">
|
||||
<li><strong>Integration Key:</strong> {{updateModel.ikey}}</li>
|
||||
<li><strong>Secret Key:</strong> ************</li>
|
||||
<li><strong>API Hostname:</strong> {{updateModel.host}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div ng-if="!enabled">
|
||||
<div class="callout callout-danger validation-errors" ng-show="submitTwoStepForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in submitTwoStepForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>Enter the bitwarden application information from your Duo Admin panel:</p>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="ikey">Integration Key</label>
|
||||
<input type="text" id="ikey" name="IntegrationKey" ng-model="updateModel.ikey" class="form-control"
|
||||
required api-field />
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="skey">Secret Key</label>
|
||||
<input type="password" id="skey" name="SecretKey" ng-model="updateModel.skey" class="form-control"
|
||||
required api-field />
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="host">API Hostname</label>
|
||||
<input type="text" id="host" name="Host" placeholder="ex. api-xxxxxxxx.duosecurity.com"
|
||||
ng-model="updateModel.host" class="form-control" required api-field />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="submitTwoStepForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="submitTwoStepForm.$loading"></i>
|
||||
{{enabled ? 'Disable' : 'Enable'}}
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
77
app/settings/views/settingsTwoStepEmail.html
Normal file
@@ -0,0 +1,77 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">
|
||||
<i class="fa fa-key"></i> Two-step Login <small>email</small>
|
||||
</h4>
|
||||
</div>
|
||||
<form name="authTwoStepForm" ng-submit="authTwoStepForm.$valid && auth(authModel)" api-form="authPromise"
|
||||
ng-if="!authed">
|
||||
<div class="modal-body">
|
||||
<p>Enter your master password to modify two-step login settings.</p>
|
||||
<div class="callout callout-danger validation-errors" ng-show="authTwoStepForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in authTwoStepForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="masterPassword">Master Password</label>
|
||||
<input type="password" id="masterPassword" name="MasterPasswordHash" ng-model="authModel.masterPassword"
|
||||
class="form-control" required api-field />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="authTwoStepForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="authTwoStepForm.$loading"></i>Continue
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
<form name="submitTwoStepForm" ng-submit="submitTwoStepForm.$valid && submit(updateModel)" api-form="submitPromise"
|
||||
ng-if="authed">
|
||||
<div class="modal-body">
|
||||
<div ng-if="enabled">
|
||||
<div class="callout callout-success">
|
||||
<h4><i class="fa fa-check-circle"></i> Enabled</h4>
|
||||
<p>Two-step log via email is enabled on your account.</p>
|
||||
</div>
|
||||
Email: <strong>{{updateModel.email}}</strong>
|
||||
</div>
|
||||
<div ng-if="!enabled">
|
||||
<div class="callout callout-danger validation-errors" ng-show="submitTwoStepForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in submitTwoStepForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>Setting up two-step login with email is easy, just follow these steps:</p>
|
||||
<h4>1. Enter the email that you wish to receive verification codes</h4>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="token" class="sr-only">Email</label>
|
||||
<input type="text" id="email" name="Email" placeholder="Email" ng-model="updateModel.email"
|
||||
class="form-control" required api-field />
|
||||
</div>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="sendEmail(updateModel)" ng-disabled="emailLoading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="emailLoading"></i>
|
||||
Send Email
|
||||
</button>
|
||||
<span class="text-green" ng-if="emailSuccess">Verification code email was sent.</span>
|
||||
<span class="text-red" ng-if="emailError">An error occurred when trying to send the email.</span>
|
||||
<h4 style="margin-top: 30px;">
|
||||
2. Enter the resulting 6 digit verification code from the email
|
||||
</h4>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="token" class="sr-only">Verification Code</label>
|
||||
<input type="text" id="token" name="Token" placeholder="Verification Code" ng-model="updateModel.token"
|
||||
class="form-control" required api-field />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="submitTwoStepForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="submitTwoStepForm.$loading"></i>
|
||||
{{enabled ? 'Disable' : 'Enable'}}
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
48
app/settings/views/settingsTwoStepRecover.html
Normal file
@@ -0,0 +1,48 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">
|
||||
<i class="fa fa-key"></i> Two-step Login <small>recovery code</small>
|
||||
</h4>
|
||||
</div>
|
||||
<form name="authTwoStepForm" ng-submit="authTwoStepForm.$valid && auth(authModel)" api-form="authPromise"
|
||||
ng-if="!authed">
|
||||
<div class="modal-body">
|
||||
<p>Enter your master password to view your recovery code.</p>
|
||||
<div class="callout callout-danger validation-errors" ng-show="authTwoStepForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in authTwoStepForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="masterPassword">Master Password</label>
|
||||
<input type="password" id="masterPassword" name="MasterPasswordHash" ng-model="authModel.masterPassword"
|
||||
class="form-control" required api-field />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="authTwoStepForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="authTwoStepForm.$loading"></i>Continue
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
<div ng-if="authed">
|
||||
<div class="modal-body text-center">
|
||||
<div ng-if="code">
|
||||
<p>Your two-step login recovery code:</p>
|
||||
<p class="lead"><code class="text-lg">{{code}}</code></p>
|
||||
</div>
|
||||
<div ng-if="!code">
|
||||
You have not enabled any two-step login providers yet. After you have enabled a two-step login provider you can
|
||||
check back here for your recovery code.
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-if="code" ng-click="print()">
|
||||
<i class="fa fa-print"></i>
|
||||
Print Code
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
93
app/settings/views/settingsTwoStepU2f.html
Normal file
@@ -0,0 +1,93 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">
|
||||
<i class="fa fa-key"></i> Two-step Login <small>fido u2f</small>
|
||||
</h4>
|
||||
</div>
|
||||
<form name="authTwoStepForm" ng-submit="authTwoStepForm.$valid && auth(authModel)" api-form="authPromise"
|
||||
ng-if="!authed">
|
||||
<div class="modal-body">
|
||||
<p>Enter your master password to modify two-step login settings.</p>
|
||||
<div class="callout callout-danger validation-errors" ng-show="authTwoStepForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in authTwoStepForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="masterPassword">Master Password</label>
|
||||
<input type="password" id="masterPassword" name="MasterPasswordHash" ng-model="authModel.masterPassword"
|
||||
class="form-control" required api-field />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="authTwoStepForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="authTwoStepForm.$loading"></i>Continue
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
<form name="submitTwoStepForm" ng-submit="submitTwoStepForm.$valid && submit()" api-form="submitPromise"
|
||||
ng-if="authed">
|
||||
<div class="modal-body">
|
||||
<div class="callout callout-warning">
|
||||
<h4><i class="fa fa-warning"></i> Warning <i class="fa fa-warning"></i></h4>
|
||||
<p>
|
||||
Due to platform limitations, FIDO U2F cannot be used on all bitwarden applications. You should enable
|
||||
another two-step login provider so that you can access your account when FIDO U2F cannot be used.
|
||||
</p>
|
||||
<p>Supported platforms:</p>
|
||||
<ul>
|
||||
<li>
|
||||
Web vault on a desktop/laptop with a U2F enabled browser (Chrome, Opera, Vivaldi, Brave, or Firefox with addon).
|
||||
</li>
|
||||
<li>Browser extensions on Chrome, Opera, Vivaldi, or Brave.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div ng-if="enabled">
|
||||
<div class="callout callout-success">
|
||||
<h4><i class="fa fa-check-circle"></i> Enabled</h4>
|
||||
<p>Two-step log via FIDO U2F is enabled on your account.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="!enabled">
|
||||
<div class="callout callout-danger validation-errors" ng-show="submitTwoStepForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in submitTwoStepForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>To add a new FIDO U2F Security Key to your account:</p>
|
||||
<ol>
|
||||
<li>Plug the security key into your computer's USB port.</li>
|
||||
<li>If the security key has a button, touch it.</li>
|
||||
</ol>
|
||||
<hr />
|
||||
<div class="text-center">
|
||||
<div ng-show="deviceListening">
|
||||
<p><i class="fa fa-spin fa-spinner fa-2x"></i></p>
|
||||
<p>Waiting for you to touch the button on your security key...</p>
|
||||
</div>
|
||||
<div class="text-green" ng-show="deviceResponse">
|
||||
<p><i class="fa fa-check-circle fa-2x"></i></p>
|
||||
<p>Success!</p>
|
||||
Click the "Enable" button below to enable this security key for two-step login.
|
||||
</div>
|
||||
<div class="text-red" ng-show="deviceError">
|
||||
<p><i class="fa fa-warning fa-2x"></i></p>
|
||||
<p>Error!</p>
|
||||
<p>There was a problem reading the security key.</p>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="readDevice()">Try again</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat"
|
||||
ng-disabled="(!enabled && !deviceResponse) || submitTwoStepForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="submitTwoStepForm.$loading"></i>
|
||||
{{enabled ? 'Disable' : 'Enable'}}
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
127
app/settings/views/settingsTwoStepYubi.html
Normal file
@@ -0,0 +1,127 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">
|
||||
<i class="fa fa-key"></i> Two-step Login <small>yubikey</small>
|
||||
</h4>
|
||||
</div>
|
||||
<form name="authTwoStepForm" ng-submit="authTwoStepForm.$valid && auth(authModel)" api-form="authPromise"
|
||||
ng-if="!authed">
|
||||
<div class="modal-body">
|
||||
<p>Enter your master password to modify two-step login settings.</p>
|
||||
<div class="callout callout-danger validation-errors" ng-show="authTwoStepForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in authTwoStepForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="masterPassword">Master Password</label>
|
||||
<input type="password" id="masterPassword" name="MasterPasswordHash" ng-model="authModel.masterPassword"
|
||||
class="form-control" required api-field />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="authTwoStepForm.$loading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="authTwoStepForm.$loading"></i>Continue
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
<form name="submitTwoStepForm" ng-submit="submitTwoStepForm.$valid && submit(updateModel)" api-form="submitPromise"
|
||||
ng-if="authed">
|
||||
<div class="modal-body">
|
||||
<div class="callout callout-warning">
|
||||
<h4><i class="fa fa-warning"></i> Warning <i class="fa fa-warning"></i></h4>
|
||||
<p>
|
||||
Due to platform limitations, YubiKeys cannot be used on all bitwarden applications. You should enable
|
||||
another two-step login provider so that you can access your account when YubiKeys cannot be used.
|
||||
</p>
|
||||
<p>Supported platforms:</p>
|
||||
<ul>
|
||||
<li>Web vault on a device with a USB port that can accept your YubiKey.</li>
|
||||
<li>Browser extensions.</li>
|
||||
<li>
|
||||
Android on a device with
|
||||
<a href="https://en.wikipedia.org/wiki/List_of_NFC-enabled_mobile_devices" target="_blank">
|
||||
NFC capabilities
|
||||
</a>. Read more <a href="https://forum.yubico.com/viewtopic.php?f=26&t=1302" target="_blank">here</a>.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div ng-if="enabled">
|
||||
<div class="callout callout-success">
|
||||
<h4><i class="fa fa-check-circle"></i> Enabled</h4>
|
||||
<p>Two-step log via YubiKey is enabled on your account.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="callout callout-danger validation-errors" ng-show="submitTwoStepForm.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in submitTwoStepForm.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>To add a new YubiKey to your account:</p>
|
||||
<ol>
|
||||
<li>Plug the YubiKey (NEO or 4 series) into your computer's USB port.</li>
|
||||
<li>Select in the first empty <b>Key</b> field below.</li>
|
||||
<li>Touch the YubiKey's button.</li>
|
||||
<li>Save the form.</li>
|
||||
</ol>
|
||||
<hr />
|
||||
<div class="form-group" show-errors>
|
||||
<label for="key1">YubiKey #1</label>
|
||||
<span ng-if="updateModel.key1.existingKey">
|
||||
<a href="#" class="btn btn-link btn-xs" stop-click ng-click="remove(updateModel.key1)">[remove]</a>
|
||||
</span>
|
||||
<div ng-if="updateModel.key1.existingKey" class="monospaced">
|
||||
{{updateModel.key1.existingKey}}
|
||||
</div>
|
||||
<input type="password" id="key1" name="Key1" ng-model="updateModel.key1.key" class="form-control" api-field
|
||||
ng-show="!updateModel.key1.existingKey" />
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="key2">YubiKey #2</label>
|
||||
<span ng-if="updateModel.key2.existingKey">
|
||||
<a href="#" class="btn btn-link btn-xs" stop-click ng-click="remove(updateModel.key2)">[remove]</a>
|
||||
</span>
|
||||
<div ng-if="updateModel.key2.existingKey" class="monospaced">
|
||||
{{updateModel.key2.existingKey}}
|
||||
</div>
|
||||
<input type="password" id="key2" name="Key2" ng-model="updateModel.key2.key" class="form-control" api-field
|
||||
ng-show="!updateModel.key2.existingKey" />
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="key3">YubiKey #3</label>
|
||||
<span ng-if="updateModel.key3.existingKey">
|
||||
<a href="#" class="btn btn-link btn-xs" stop-click ng-click="remove(updateModel.key3)">[remove]</a>
|
||||
</span>
|
||||
<div ng-if="updateModel.key3.existingKey" class="monospaced">
|
||||
{{updateModel.key3.existingKey}}
|
||||
</div>
|
||||
<input type="password" id="key3" name="Key3" ng-model="updateModel.key3.key" class="form-control" api-field
|
||||
ng-show="!updateModel.key3.existingKey" />
|
||||
</div>
|
||||
<strong>NFC Support</strong>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="Nfc" id="nfc" ng-model="updateModel.nfc" /> One of my keys supports NFC.
|
||||
</label>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
If one of your YubiKeys supports NFC (such as a YubiKey NEO), you will be prompted on mobile devices whenever NFC
|
||||
availability is detected.
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="submitTwoStepForm.$loading || disableLoading">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="submitTwoStepForm.$loading"></i>
|
||||
Save
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="disable()" ng-disabled="disableLoading"
|
||||
ng-if="enabled">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="disableLoading"></i>
|
||||
Disable All Keys
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
54
app/settings/views/settingsUpdateKey.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title"><i class="fa fa-key"></i> Update Encryption Key</h4>
|
||||
</div>
|
||||
<form name="form" ng-submit="form.$valid && save(form)" api-form="savePromise" ng-show="!processing">
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
This is <b>NOT</b> a security notification indicating that anything is wrong or has been compromised on your
|
||||
account. If interested, you can
|
||||
<a href="https://help.bitwarden.com/article/update-encryption-key/" target="_blank">read more details here</a>.
|
||||
</p>
|
||||
<hr />
|
||||
<p>
|
||||
You are currently using an outdated encryption scheme. We've moved to larger encryption keys
|
||||
that provide better security and access to newer features.
|
||||
</p>
|
||||
<p>
|
||||
Updating your encryption key is quick and easy. Just type your master password below and you're done!
|
||||
This update will eventually become mandatory.
|
||||
</p>
|
||||
<hr />
|
||||
<div class="callout callout-warning">
|
||||
<h4><i class="fa fa-warning"></i> Warning</h4>
|
||||
After updating your encryption key, you are required to log out and back in to all bitwarden applications that you
|
||||
are currently using (such as the mobile app or browser extensions). Failure to log out and back
|
||||
in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out
|
||||
automatically, however it may be delayed.
|
||||
</div>
|
||||
<div class="callout callout-danger validation-errors" ng-show="form.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in form.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="masterPassword">Master Password</label>
|
||||
<input type="password" id="masterPassword" name="MasterPasswordHash" ng-model="masterPassword" class="form-control"
|
||||
required api-field />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat">
|
||||
Update Key
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
<div ng-show="processing" class="modal-body text-center">
|
||||
<p><i class="fa fa-cog fa-spin fa-3x"></i></p>
|
||||
<p>
|
||||
Please wait. We are now generating a new encryption key and reencrypting all of your data. Do not close this window.
|
||||
You will be automatically logged out when this process has finished.
|
||||
</p>
|
||||
</div>
|
||||
@@ -18,8 +18,8 @@
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
<label for="masterPassword">Master Password</label>
|
||||
<input type="password" id="masterPassword" name="MasterPasswordHash" ng-model="model.masterPassword" class="form-control"
|
||||
master-password required api-field ng-model-options="{ 'updateOn': 'blur'}" />
|
||||
<input type="password" id="masterPassword" name="MasterPasswordHash" ng-model="model.masterPassword"
|
||||
class="form-control" master-password required api-field ng-model-options="{ 'updateOn': 'blur'}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
@@ -86,6 +86,11 @@
|
||||
<i class="fa fa-fw fa-pencil"></i> Edit
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" stop-click ng-click="attachments(login)">
|
||||
<i class="fa fa-fw fa-paperclip"></i> Attachments
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="!login.organizationId">
|
||||
<a href="#" stop-click ng-click="share(login)">
|
||||
<i class="fa fa-fw fa-share-alt"></i> Share
|
||||
@@ -115,7 +120,9 @@
|
||||
</td>
|
||||
<td ng-click="select($event)">
|
||||
<a href="#" stop-click ng-click="editLogin(login)" stop-prop>{{login.name}}</a>
|
||||
<i class="fa fa-share-alt text-muted" title="Shared" ng-show="login.organizationId"
|
||||
<i class="fa fa-share-alt text-muted" title="Shared" ng-if="login.organizationId"
|
||||
stop-prop></i>
|
||||
<i class="fa fa-paperclip text-muted" title="Attachments" ng-if="login.hasAttachments"
|
||||
stop-prop></i><br />
|
||||
<span class="text-sm text-muted" stop-prop>{{login.username}}</span>
|
||||
</td>
|
||||
@@ -189,6 +196,11 @@
|
||||
<i class="fa fa-fw fa-pencil"></i> Edit
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" stop-click ng-click="attachments(login)">
|
||||
<i class="fa fa-fw fa-paperclip"></i> Attachments
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="!login.organizationId">
|
||||
<a href="#" stop-click ng-click="share(login)">
|
||||
<i class="fa fa-fw fa-share-alt"></i> Share
|
||||
@@ -219,7 +231,10 @@
|
||||
<td ng-click="select($event)">
|
||||
<a href="#" stop-click ng-click="editLogin(login)" stop-prop>{{login.name}}</a>
|
||||
<i class="fa fa-star text-muted" title="Favorite" ng-show="login.favorite" stop-prop></i>
|
||||
<i class="fa fa-share-alt text-muted" title="Shared" ng-show="login.organizationId" stop-prop></i>
|
||||
<i class="fa fa-share-alt text-muted" title="Shared" ng-show="login.organizationId"
|
||||
stop-prop></i>
|
||||
<i class="fa fa-paperclip text-muted" title="Attachments" ng-if="login.hasAttachments"
|
||||
stop-prop></i>
|
||||
<br />
|
||||
<span class="text-sm text-muted" stop-prop>{{login.username}}</span>
|
||||
</td>
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group" show-errors>
|
||||
<div class="form-group" show-errors style="margin-bottom: 5px;">
|
||||
<div class="pull-right password-options">
|
||||
<i class="fa fa-lg fa-refresh" uib-tooltip="Generate Password" tooltip-placement="left" ng-click="generatePassword()"></i>
|
||||
<i class="fa fa-lg fa-eye" uib-tooltip="Toggle Password" tooltip-placement="left" password-viewer="#password"></i>
|
||||
@@ -75,7 +75,24 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin: -10px 0 15px 0;" password-meter="login.password" password-meter-username="login.username" outer-class="xs"></div>
|
||||
<div password-meter="login.password" password-meter-username="login.username"
|
||||
outer-class="xs" class="password-meter"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group" show-errors>
|
||||
<label for="totp">Authenticator Key (TOTP)</label>
|
||||
<input type="text" id="totp" name="Totp" ng-model="login.totp" class="form-control"
|
||||
ng-readonly="readOnly" api-field />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 totp-col">
|
||||
<div totp="login.totp" id="verification-code" ng-if="useTotp"></div>
|
||||
<div ng-if="!useTotp">
|
||||
<a href="#" stop-click ng-click="showUpgrade()"><img src="images/totp-countdown.png" alt="" /></a>
|
||||
<span class="label label-info clickable" ng-click="showUpgrade()">{{fromOrg ? 'UPGRADE' : 'PREMIUM'}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
|
||||
75
app/vault/views/vaultAttachments.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">
|
||||
<i class="fa fa-paperclip"></i> Secure Attachments <small>{{login.name}}</small>
|
||||
</h4>
|
||||
</div>
|
||||
<form name="form" ng-submit="form.$valid && save(form)" api-form="savePromise">
|
||||
<div class="modal-body">
|
||||
<div ng-if="loading">
|
||||
Loading...
|
||||
</div>
|
||||
<div ng-if="!loading && !login.attachments.length">
|
||||
There are no attachments for this login.
|
||||
</div>
|
||||
<div class="table-responsive" ng-if="login.attachments.length" style="margin: 0;">
|
||||
<table class="table table-striped table-hover table-vmiddle" style="margin: 0;">
|
||||
<tbody>
|
||||
<tr ng-repeat="attachment in login.attachments | orderBy: ['fileName']">
|
||||
<td style="width: 70px;">
|
||||
<div class="btn-group" data-append-to=".modal">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-cog"></i> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a href="#" stop-click ng-click="download(attachment)">
|
||||
<i class="fa fa-fw fa-download"></i> Download
|
||||
</a>
|
||||
<a href="#" stop-click ng-click="remove(attachment)" class="text-red"
|
||||
ng-show="!readOnly">
|
||||
<i class="fa fa-fw fa-trash"></i> Delete
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" stop-click ng-click="download(attachment)">{{attachment.fileName}}</a>
|
||||
<i class="fa fa-spinner fa-spin text-muted" ng-if="attachment.loading"></i>
|
||||
</td>
|
||||
<td style="width: 90px; min-width: 90px;">
|
||||
{{attachment.size}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div ng-if="!readOnly">
|
||||
<hr />
|
||||
<h4>Add New Attachment</h4>
|
||||
<div class="callout callout-warning" ng-if="!canUseAttachments">
|
||||
<h4>Premium Membership Required</h4>
|
||||
<p>Premium membership is required to use this feature.</p>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="showUpgrade()">Learn More</button>
|
||||
</div>
|
||||
<div class="callout callout-danger validation-errors" ng-show="form.$errors">
|
||||
<h4>Errors have occurred</h4>
|
||||
<ul>
|
||||
<li ng-repeat="e in form.$errors">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-group" show-error ng-if="canUseAttachments">
|
||||
<label for="file" class="sr-only">File</label>
|
||||
<input type="file" id="file" name="file" />
|
||||
<p class="help-block">Maximum size per file is 100 MB.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="form.$loading" ng-if="!readOnly && canUseAttachments">
|
||||
<i class="fa fa-refresh fa-spin loading-icon" ng-show="form.$loading"></i>Save
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -67,7 +67,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group" show-errors>
|
||||
<div class="form-group" show-errors style="margin-bottom: 5px;">
|
||||
<div class="pull-right password-options">
|
||||
<i class="fa fa-lg fa-refresh" uib-tooltip="Generate Password" tooltip-placement="left"
|
||||
ng-click="generatePassword()" ng-show="!readOnly"></i>
|
||||
@@ -87,8 +87,24 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin: -10px 0 15px 0;" password-meter="login.password" password-meter-username="login.username"
|
||||
outer-class="xs"></div>
|
||||
<div password-meter="login.password" password-meter-username="login.username"
|
||||
outer-class="xs" class="password-meter"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group" show-errors>
|
||||
<label for="totp">Authenticator Key (TOTP)</label>
|
||||
<input type="text" id="totp" name="Totp" ng-model="login.totp" class="form-control"
|
||||
ng-readonly="readOnly" api-field />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 totp-col">
|
||||
<div totp="login.totp" id="verification-code" ng-if="useTotp"></div>
|
||||
<div ng-if="!useTotp">
|
||||
<a href="#" stop-click ng-click="showUpgrade()"><img src="images/totp-countdown.png" alt="" /></a>
|
||||
<span class="label label-info clickable" ng-click="showUpgrade()">{{fromOrg ? 'UPGRADE' : 'PREMIUM'}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" show-errors>
|
||||
|
||||
@@ -62,6 +62,11 @@
|
||||
<i class="fa fa-fw fa-pencil"></i> Edit
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" stop-click ng-click="attachments(login)">
|
||||
<i class="fa fa-fw fa-paperclip"></i> Attachments
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="login.edit">
|
||||
<a href="#" stop-click ng-click="editCollections(login)">
|
||||
<i class="fa fa-fw fa-cubes"></i> Collections
|
||||
@@ -85,6 +90,8 @@
|
||||
<td>
|
||||
<a href="#" stop-click ng-click="editLogin(login)">{{login.name}}</a>
|
||||
<i class="fa fa-star text-muted" title="Favorite" ng-show="login.favorite"></i>
|
||||
<i class="fa fa-paperclip text-muted" title="Attachments" ng-if="login.hasAttachments"
|
||||
stop-prop></i><br />
|
||||
<div class="text-sm text-muted">{{login.username}}</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
13
app/views/paidOrgRequired.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title"><i class="fa fa-star"></i> Paid Plan Required</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
This feature is not available for free organizations. Switch to a paid plan to unlock more features.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-click="go()" ng-if="admin">
|
||||
Upgrade Organization
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
36
app/views/premiumRequired.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title"><i class="fa fa-star"></i> Premium <span class="hidden-xs">Membership</span> Required</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This features requires a premium membership. Sign up for premium and get:</p>
|
||||
<ul class="fa-ul">
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-green"></i>
|
||||
1 GB of encrypted file storage.
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-green"></i>
|
||||
Additional two-step login options such as YubiKey, FIDO U2F, and Duo.
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-green"></i>
|
||||
TOTP verification code (2FA) generator for logins in your vault.
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-green"></i>
|
||||
Priority customer support.
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-green"></i>
|
||||
All future premium features. More coming soon!
|
||||
</li>
|
||||
</ul>
|
||||
All for just <b>{{10 | currency:"$":0}}</b> /year.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-flat" ng-click="go()">
|
||||
Get Premium
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||
</div>
|
||||
@@ -64,10 +64,10 @@
|
||||
<i class="fa fa-share-alt fa-fw"></i> <span>Shared</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="treeview" ng-class="{active: $state.is('backend.user.tools') ||
|
||||
<li class="treeview" ng-class="{active: $state.is('backend.user.tools') ||
|
||||
$state.is('backend.user.reportsBreach')}">
|
||||
<a ui-sref="backend.user.tools"><i class="fa fa-wrench fa-fw"></i> <span>Tools</span></a>
|
||||
<ul class="treeview-menu" ng-class="{'menu-open': $state.is('backend.user.tools') ||
|
||||
<ul class="treeview-menu" ng-class="{'menu-open': $state.is('backend.user.tools') ||
|
||||
$state.is('backend.user.reportsBreach')}">
|
||||
<li ng-class="{active: $state.is('backend.user.reportsBreach')}">
|
||||
<a ui-sref="backend.user.reportsBreach">
|
||||
@@ -78,15 +78,31 @@
|
||||
</li>
|
||||
<li class="treeview"
|
||||
ng-class="{active: $state.is('backend.user.settings') || $state.is('backend.user.settingsDomains') ||
|
||||
$state.is('backend.user.settingsCreateOrg')}">
|
||||
$state.is('backend.user.settingsCreateOrg') || $state.is('backend.user.settingsTwoStep') ||
|
||||
$state.is('backend.user.settingsPremium') || $state.is('backend.user.settingsBilling')}">
|
||||
<a ui-sref="backend.user.settings"><i class="fa fa-cogs fa-fw"></i> <span>Settings</span></a>
|
||||
<ul class="treeview-menu" ng-class="{'menu-open': $state.is('backend.user.settings') ||
|
||||
$state.is('backend.user.settingsDomains') || $state.is('backend.user.settingsCreateOrg')}">
|
||||
<li ng-class="{active: $state.is('backend.user.settingsPremium')}">
|
||||
<a ui-sref="backend.user.settingsPremium" ng-if="!main.userProfile || !main.userProfile.premium">
|
||||
<i class="fa fa-star fa-fw"></i> Go Premium!
|
||||
</a>
|
||||
</li>
|
||||
<li ng-class="{active: $state.is('backend.user.settingsCreateOrg')}">
|
||||
<a ui-sref="backend.user.settingsCreateOrg">
|
||||
<i class="fa fa-plus-circle fa-fw"></i> New Organization
|
||||
</a>
|
||||
</li>
|
||||
<li ng-class="{active: $state.is('backend.user.settingsBilling')}">
|
||||
<a ui-sref="backend.user.settingsBilling" ng-if="main.userProfile && main.userProfile.premium">
|
||||
<i class="fa fa-circle-o fa-fw"></i> Billing
|
||||
</a>
|
||||
</li>
|
||||
<li ng-class="{active: $state.is('backend.user.settingsTwoStep')}">
|
||||
<a ui-sref="backend.user.settingsTwoStep">
|
||||
<i class="fa fa-fw fa-circle-o"></i> Two-step Login
|
||||
</a>
|
||||
</li>
|
||||
<li ng-class="{active: $state.is('backend.user.settingsDomains')}">
|
||||
<a ui-sref="backend.user.settingsDomains">
|
||||
<i class="fa fa-fw fa-circle-o"></i> Domain Rules
|
||||
@@ -130,5 +146,30 @@
|
||||
</section>
|
||||
</aside>
|
||||
|
||||
<div class="content-wrapper" ui-view>
|
||||
</div>
|
||||
<div class="content-wrapper">
|
||||
<div class="alert alert-danger alert-notification" ng-if="main.outdatedBrowser" ng-click="updateBrowser()">
|
||||
<h4><i class="fa fa-warning fa-fw"></i> Update Your Browser</h4>
|
||||
You are using an unsupported web browser. The web vault may not function properly.
|
||||
<u>Update your browser now</u>.
|
||||
</div>
|
||||
<div class="alert alert-warning alert-notification" ng-click="updateKey()" ng-if="!main.usingEncKey">
|
||||
<h4><i class="fa fa-key fa-fw"></i> Update Your Encryption Key</h4>
|
||||
You are currently using an outdated encryption scheme.
|
||||
<a href="#" stop-click>Learn more and update now</a>.
|
||||
</div>
|
||||
<div class="alert alert-warning alert-notification" ng-click="verifyEmail()"
|
||||
ng-if="main.usingEncKey && main.userProfile && !main.userProfile.emailVerified">
|
||||
<h4><i class="fa fa-envelope fa-fw"></i> Verify Your Email</h4>
|
||||
<div ng-if="!verifyEmailSent">
|
||||
Verify your account's email address to unlock access to all features.
|
||||
<a href="#" stop-click>Send verification email now</a>.
|
||||
<i class="fa fa-spin fa-refresh" ng-if="sendingVerify"></i>
|
||||
</div>
|
||||
<div ng-if="verifyEmailSent">
|
||||
Check your email inbox for a verification link.
|
||||
<a href="#" stop-click>Send verification email again</a>.
|
||||
<i class="fa fa-spin fa-refresh" ng-if="sendingVerify"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div ui-view></div>
|
||||
</div>
|
||||
|
||||
2
css/vault.min.css
vendored
72
duo-connector.html
Normal file
@@ -0,0 +1,72 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<title>Duo Two-step Login</title>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #efeff4 url('images/loading.svg') 0 0 no-repeat;
|
||||
}
|
||||
|
||||
iframe {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script src="js/duo.js"></script>
|
||||
<script>
|
||||
!(function () {
|
||||
var frameElement = document.createElement('iframe');
|
||||
frameElement.setAttribute('id', 'duo_iframe');
|
||||
setFrameHeight();
|
||||
document.body.appendChild(frameElement);
|
||||
|
||||
var hostParam = getQsParam('host');
|
||||
var requestParam = getQsParam('request');
|
||||
Duo.init({
|
||||
host: hostParam,
|
||||
sig_request: requestParam,
|
||||
submit_callback: function (form) {
|
||||
invokeCSCode(form.elements.sig_response.value);
|
||||
}
|
||||
});
|
||||
|
||||
window.onresize = setFrameHeight;
|
||||
function setFrameHeight() {
|
||||
frameElement.style.height = window.innerHeight + 'px';
|
||||
}
|
||||
})();
|
||||
|
||||
function getQsParam(name) {
|
||||
var url = window.location.href;
|
||||
name = name.replace(/[\[\]]/g, '\\$&');
|
||||
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
|
||||
results = regex.exec(url);
|
||||
if (!results) return null;
|
||||
if (!results[2]) return '';
|
||||
return decodeURIComponent(results[2].replace(/\+/g, ' '));
|
||||
}
|
||||
|
||||
function invokeCSCode(data) {
|
||||
try {
|
||||
invokeCSharpAction(data);
|
||||
}
|
||||
catch (err) {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
6
images/loading.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 100% 100%">
|
||||
<text fill="%23333333" x="50%" y="50%" font-family="\'Open Sans\', \'Helvetica Neue\', Helvetica, Arial, sans-serif"
|
||||
font-size="18" text-anchor="middle">
|
||||
Loading...
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 288 B |
BIN
images/totp-countdown.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
images/two-factor/authapp.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
images/two-factor/duo.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
images/two-factor/fido.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
images/two-factor/gmail.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
images/two-factor/u2fkey.jpg
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
images/two-factor/yubico.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
images/two-factor/yubikey.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
18
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
|
||||
*;">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
@@ -46,9 +48,9 @@
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
|
||||
integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
|
||||
<meta name="x-stylesheet-test" content="" class="fa invisible" />
|
||||
<script src="js/fallback-styles.min.js?v=3w1aal9pb9"></script>
|
||||
<script src="js/fallback-styles.min.js?v=yfk9wkit3xr"></script>
|
||||
|
||||
<link rel="stylesheet" href="css/vault.min.css?v=3w1aal9pb9" />
|
||||
<link rel="stylesheet" href="css/vault.min.css?v=yfk9wkit3xr" />
|
||||
</head>
|
||||
<body ng-controller="mainController as main" class="layout-boxed skin-blue sidebar-mini {{main.bodyClass}}"
|
||||
ng-class="{'using-control-sidebar': main.usingControlSidebar,
|
||||
@@ -61,10 +63,10 @@
|
||||
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"
|
||||
integrity="sha384-AH/e+s4V4kUifvnNED2x1XZqArO5qTFU4YKRzUXbz4IgPG1H0Xmz6fP1XUmO4vT/" crossorigin="anonymous"></script>
|
||||
<script src="js/fallback-scripts.min.js?v=3w1aal9pb9"></script>
|
||||
<script src="js/fallback-scripts.min.js?v=yfk9wkit3xr"></script>
|
||||
|
||||
<script src="js/lib.min.js?v=3w1aal9pb9"></script>
|
||||
<script src="js/bw.min.js?v=3w1aal9pb9"></script>
|
||||
<script src="js/app.min.js?v=3w1aal9pb9"></script>
|
||||
<script src="js/lib.min.js?v=yfk9wkit3xr"></script>
|
||||
<script src="js/bw.min.js?v=yfk9wkit3xr"></script>
|
||||
<script src="js/app.min.js?v=yfk9wkit3xr"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
12
js/app.min.js
vendored
2
js/bw.min.js
vendored
430
js/duo.js
Normal file
@@ -0,0 +1,430 @@
|
||||
/**
|
||||
* Duo Web SDK v2
|
||||
* Copyright 2017, Duo Security
|
||||
*/
|
||||
|
||||
(function (root, factory) {
|
||||
/*eslint-disable */
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define([], factory);
|
||||
/*eslint-enable */
|
||||
} else if (typeof module === 'object' && module.exports) {
|
||||
// Node. Does not work with strict CommonJS, but
|
||||
// only CommonJS-like environments that support module.exports,
|
||||
// like Node.
|
||||
module.exports = factory();
|
||||
} else {
|
||||
// Browser globals (root is window)
|
||||
var Duo = factory();
|
||||
// If the Javascript was loaded via a script tag, attempt to autoload
|
||||
// the frame.
|
||||
Duo._onReady(Duo.init);
|
||||
|
||||
// Attach Duo to the `window` object
|
||||
root.Duo = Duo;
|
||||
}
|
||||
}(this, function() {
|
||||
var DUO_MESSAGE_FORMAT = /^(?:AUTH|ENROLL)+\|[A-Za-z0-9\+\/=]+\|[A-Za-z0-9\+\/=]+$/;
|
||||
var DUO_ERROR_FORMAT = /^ERR\|[\w\s\.\(\)]+$/;
|
||||
var DUO_OPEN_WINDOW_FORMAT = /^DUO_OPEN_WINDOW\|/;
|
||||
var VALID_OPEN_WINDOW_DOMAINS = [
|
||||
'duo.com',
|
||||
'duosecurity.com',
|
||||
'duomobile.s3-us-west-1.amazonaws.com'
|
||||
];
|
||||
|
||||
var iframeId = 'duo_iframe',
|
||||
postAction = '',
|
||||
postArgument = 'sig_response',
|
||||
host,
|
||||
sigRequest,
|
||||
duoSig,
|
||||
appSig,
|
||||
iframe,
|
||||
submitCallback;
|
||||
|
||||
function throwError(message, url) {
|
||||
throw new Error(
|
||||
'Duo Web SDK error: ' + message +
|
||||
(url ? ('\n' + 'See ' + url + ' for more information') : '')
|
||||
);
|
||||
}
|
||||
|
||||
function hyphenize(str) {
|
||||
return str.replace(/([a-z])([A-Z])/, '$1-$2').toLowerCase();
|
||||
}
|
||||
|
||||
// cross-browser data attributes
|
||||
function getDataAttribute(element, name) {
|
||||
if ('dataset' in element) {
|
||||
return element.dataset[name];
|
||||
} else {
|
||||
return element.getAttribute('data-' + hyphenize(name));
|
||||
}
|
||||
}
|
||||
|
||||
// cross-browser event binding/unbinding
|
||||
function on(context, event, fallbackEvent, callback) {
|
||||
if ('addEventListener' in window) {
|
||||
context.addEventListener(event, callback, false);
|
||||
} else {
|
||||
context.attachEvent(fallbackEvent, callback);
|
||||
}
|
||||
}
|
||||
|
||||
function off(context, event, fallbackEvent, callback) {
|
||||
if ('removeEventListener' in window) {
|
||||
context.removeEventListener(event, callback, false);
|
||||
} else {
|
||||
context.detachEvent(fallbackEvent, callback);
|
||||
}
|
||||
}
|
||||
|
||||
function onReady(callback) {
|
||||
on(document, 'DOMContentLoaded', 'onreadystatechange', callback);
|
||||
}
|
||||
|
||||
function offReady(callback) {
|
||||
off(document, 'DOMContentLoaded', 'onreadystatechange', callback);
|
||||
}
|
||||
|
||||
function onMessage(callback) {
|
||||
on(window, 'message', 'onmessage', callback);
|
||||
}
|
||||
|
||||
function offMessage(callback) {
|
||||
off(window, 'message', 'onmessage', callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the sig_request parameter, throwing errors if the token contains
|
||||
* a server error or if the token is invalid.
|
||||
*
|
||||
* @param {String} sig Request token
|
||||
*/
|
||||
function parseSigRequest(sig) {
|
||||
if (!sig) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
// see if the token contains an error, throwing it if it does
|
||||
if (sig.indexOf('ERR|') === 0) {
|
||||
throwError(sig.split('|')[1]);
|
||||
}
|
||||
|
||||
// validate the token
|
||||
if (sig.indexOf(':') === -1 || sig.split(':').length !== 2) {
|
||||
throwError(
|
||||
'Duo was given a bad token. This might indicate a configuration ' +
|
||||
'problem with one of Duo\'s client libraries.',
|
||||
'https://www.duosecurity.com/docs/duoweb#first-steps'
|
||||
);
|
||||
}
|
||||
|
||||
var sigParts = sig.split(':');
|
||||
|
||||
// hang on to the token, and the parsed duo and app sigs
|
||||
sigRequest = sig;
|
||||
duoSig = sigParts[0];
|
||||
appSig = sigParts[1];
|
||||
|
||||
return {
|
||||
sigRequest: sig,
|
||||
duoSig: sigParts[0],
|
||||
appSig: sigParts[1]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is set up to run when the DOM is ready, if the iframe was
|
||||
* not available during `init`.
|
||||
*/
|
||||
function onDOMReady() {
|
||||
iframe = document.getElementById(iframeId);
|
||||
|
||||
if (!iframe) {
|
||||
throw new Error(
|
||||
'This page does not contain an iframe for Duo to use.' +
|
||||
'Add an element like <iframe id="duo_iframe"></iframe> ' +
|
||||
'to this page. ' +
|
||||
'See https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe ' +
|
||||
'for more information.'
|
||||
);
|
||||
}
|
||||
|
||||
// we've got an iframe, away we go!
|
||||
ready();
|
||||
|
||||
// always clean up after yourself
|
||||
offReady(onDOMReady);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that a MessageEvent came from the Duo service, and that it
|
||||
* is a properly formatted payload.
|
||||
*
|
||||
* The Google Chrome sign-in page injects some JS into pages that also
|
||||
* make use of postMessage, so we need to do additional validation above
|
||||
* and beyond the origin.
|
||||
*
|
||||
* @param {MessageEvent} event Message received via postMessage
|
||||
*/
|
||||
function isDuoMessage(event) {
|
||||
return Boolean(
|
||||
event.origin === ('https://' + host) &&
|
||||
typeof event.data === 'string' &&
|
||||
(
|
||||
event.data.match(DUO_MESSAGE_FORMAT) ||
|
||||
event.data.match(DUO_ERROR_FORMAT) ||
|
||||
event.data.match(DUO_OPEN_WINDOW_FORMAT)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the request token and prepare for the iframe to become ready.
|
||||
*
|
||||
* All options below can be passed into an options hash to `Duo.init`, or
|
||||
* specified on the iframe using `data-` attributes.
|
||||
*
|
||||
* Options specified using the options hash will take precedence over
|
||||
* `data-` attributes.
|
||||
*
|
||||
* Example using options hash:
|
||||
* ```javascript
|
||||
* Duo.init({
|
||||
* iframe: "some_other_id",
|
||||
* host: "api-main.duo.test",
|
||||
* sig_request: "...",
|
||||
* post_action: "/auth",
|
||||
* post_argument: "resp"
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Example using `data-` attributes:
|
||||
* ```
|
||||
* <iframe id="duo_iframe"
|
||||
* data-host="api-main.duo.test"
|
||||
* data-sig-request="..."
|
||||
* data-post-action="/auth"
|
||||
* data-post-argument="resp"
|
||||
* >
|
||||
* </iframe>
|
||||
* ```
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {String} options.iframe The iframe, or id of an iframe to set up
|
||||
* @param {String} options.host Hostname
|
||||
* @param {String} options.sig_request Request token
|
||||
* @param {String} [options.post_action=''] URL to POST back to after successful auth
|
||||
* @param {String} [options.post_argument='sig_response'] Parameter name to use for response token
|
||||
* @param {Function} [options.submit_callback] If provided, duo will not submit the form instead execute
|
||||
* the callback function with reference to the "duo_form" form object
|
||||
* submit_callback can be used to prevent the webpage from reloading.
|
||||
*/
|
||||
function init(options) {
|
||||
if (options) {
|
||||
if (options.host) {
|
||||
host = options.host;
|
||||
}
|
||||
|
||||
if (options.sig_request) {
|
||||
parseSigRequest(options.sig_request);
|
||||
}
|
||||
|
||||
if (options.post_action) {
|
||||
postAction = options.post_action;
|
||||
}
|
||||
|
||||
if (options.post_argument) {
|
||||
postArgument = options.post_argument;
|
||||
}
|
||||
|
||||
if (options.iframe) {
|
||||
if (options.iframe.tagName) {
|
||||
iframe = options.iframe;
|
||||
} else if (typeof options.iframe === 'string') {
|
||||
iframeId = options.iframe;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof options.submit_callback === 'function') {
|
||||
submitCallback = options.submit_callback;
|
||||
}
|
||||
}
|
||||
|
||||
// if we were given an iframe, no need to wait for the rest of the DOM
|
||||
if (false && iframe) {
|
||||
ready();
|
||||
} else {
|
||||
// try to find the iframe in the DOM
|
||||
iframe = document.getElementById(iframeId);
|
||||
|
||||
// iframe is in the DOM, away we go!
|
||||
if (iframe) {
|
||||
ready();
|
||||
} else {
|
||||
// wait until the DOM is ready, then try again
|
||||
onReady(onDOMReady);
|
||||
}
|
||||
}
|
||||
|
||||
// always clean up after yourself!
|
||||
offReady(init);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called when a message was received from another domain
|
||||
* using the `postMessage` API. Check that the event came from the Duo
|
||||
* service domain, and that the message is a properly formatted payload,
|
||||
* then perform the post back to the primary service.
|
||||
*
|
||||
* @param event Event object (contains origin and data)
|
||||
*/
|
||||
function onReceivedMessage(event) {
|
||||
if (isDuoMessage(event)) {
|
||||
if (event.data.match(DUO_OPEN_WINDOW_FORMAT)) {
|
||||
var url = event.data.substring("DUO_OPEN_WINDOW|".length);
|
||||
if (isValidUrlToOpen(url)) {
|
||||
// Open the URL that comes after the DUO_WINDOW_OPEN token.
|
||||
window.open(url, "_self");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// the event came from duo, do the post back
|
||||
doPostBack(event.data);
|
||||
|
||||
// always clean up after yourself!
|
||||
offMessage(onReceivedMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that this passed in URL is one that we will actually allow to
|
||||
* be opened.
|
||||
* @param url String URL that the message poster wants to open
|
||||
* @returns {boolean} true if we allow this url to be opened in the window
|
||||
*/
|
||||
function isValidUrlToOpen(url) {
|
||||
if (!url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var parser = document.createElement('a');
|
||||
parser.href = url;
|
||||
|
||||
if (parser.protocol === "duotrustedendpoints:") {
|
||||
return true;
|
||||
} else if (parser.protocol !== "https:") {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < VALID_OPEN_WINDOW_DOMAINS.length; i++) {
|
||||
if (parser.hostname.endsWith("." + VALID_OPEN_WINDOW_DOMAINS[i]) ||
|
||||
parser.hostname === VALID_OPEN_WINDOW_DOMAINS[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Point the iframe at Duo, then wait for it to postMessage back to us.
|
||||
*/
|
||||
function ready() {
|
||||
if (!host) {
|
||||
host = getDataAttribute(iframe, 'host');
|
||||
|
||||
if (!host) {
|
||||
throwError(
|
||||
'No API hostname is given for Duo to use. Be sure to pass ' +
|
||||
'a `host` parameter to Duo.init, or through the `data-host` ' +
|
||||
'attribute on the iframe element.',
|
||||
'https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!duoSig || !appSig) {
|
||||
parseSigRequest(getDataAttribute(iframe, 'sigRequest'));
|
||||
|
||||
if (!duoSig || !appSig) {
|
||||
throwError(
|
||||
'No valid signed request is given. Be sure to give the ' +
|
||||
'`sig_request` parameter to Duo.init, or use the ' +
|
||||
'`data-sig-request` attribute on the iframe element.',
|
||||
'https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// if postAction/Argument are defaults, see if they are specified
|
||||
// as data attributes on the iframe
|
||||
if (postAction === '') {
|
||||
postAction = getDataAttribute(iframe, 'postAction') || postAction;
|
||||
}
|
||||
|
||||
if (postArgument === 'sig_response') {
|
||||
postArgument = getDataAttribute(iframe, 'postArgument') || postArgument;
|
||||
}
|
||||
|
||||
// point the iframe at Duo
|
||||
iframe.src = [
|
||||
'https://', host, '/frame/web/v1/auth?tx=', duoSig,
|
||||
'&parent=', encodeURIComponent(document.location.href),
|
||||
'&v=2.6'
|
||||
].join('');
|
||||
|
||||
// listen for the 'message' event
|
||||
onMessage(onReceivedMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* We received a postMessage from Duo. POST back to the primary service
|
||||
* with the response token, and any additional user-supplied parameters
|
||||
* given in form#duo_form.
|
||||
*/
|
||||
function doPostBack(response) {
|
||||
// create a hidden input to contain the response token
|
||||
var input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = postArgument;
|
||||
input.value = response + ':' + appSig;
|
||||
|
||||
// user may supply their own form with additional inputs
|
||||
var form = document.getElementById('duo_form');
|
||||
|
||||
// if the form doesn't exist, create one
|
||||
if (!form) {
|
||||
form = document.createElement('form');
|
||||
|
||||
// insert the new form after the iframe
|
||||
iframe.parentElement.insertBefore(form, iframe.nextSibling);
|
||||
}
|
||||
|
||||
// make sure we are actually posting to the right place
|
||||
form.method = 'POST';
|
||||
form.action = postAction;
|
||||
|
||||
// add the response token input to the form
|
||||
form.appendChild(input);
|
||||
|
||||
// away we go!
|
||||
if (typeof submitCallback === "function") {
|
||||
submitCallback.call(null, form);
|
||||
} else {
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
_onReady: onReady,
|
||||
_parseSigRequest: parseSigRequest,
|
||||
_isDuoMessage: isDuoMessage,
|
||||
_doPostBack: doPostBack
|
||||
};
|
||||
}));
|
||||
2
js/fallback-styles.min.js
vendored
@@ -1 +1 @@
|
||||
function loadStylesheetIfMissing(e,t,s){var i=document.getElementsByTagName("SCRIPT"),l=i[i.length-1].previousElementSibling,n=document.defaultView&&document.defaultView.getComputedStyle?document.defaultView.getComputedStyle(l):l.currentStyle;if(n&&n[e]!==t)for(var o=0;o<s.length;o++)document.write('<link rel="stylesheet" href="'+s[o]+"?v="+cacheTag+'" />')}var cacheTag="3w1aal9pb9";loadStylesheetIfMissing("visibility","hidden",["lib/bootstrap/css/bootstrap.min.css"]),loadStylesheetIfMissing("fontFamily","FontAwesome",["lib/font-awesome/css/font-awesome.min.css"]);
|
||||
function loadStylesheetIfMissing(e,t,i){var s=document.getElementsByTagName("SCRIPT"),l=s[s.length-1].previousElementSibling,n=document.defaultView&&document.defaultView.getComputedStyle?document.defaultView.getComputedStyle(l):l.currentStyle;if(n&&n[e]!==t)for(var o=0;o<i.length;o++)document.write('<link rel="stylesheet" href="'+i[o]+"?v="+cacheTag+'" />')}var cacheTag="yfk9wkit3xr";loadStylesheetIfMissing("visibility","hidden",["lib/bootstrap/css/bootstrap.min.css"]),loadStylesheetIfMissing("fontFamily","FontAwesome",["lib/font-awesome/css/font-awesome.min.css"]);
|
||||
22
js/lib.min.js
vendored
1
js/settings.min.js
vendored
@@ -1 +0,0 @@
|
||||
angular.module("bit").constant("appSettings",{apiUri:"https://api.bitwarden.com",identityUri:"https://identity.bitwarden.com",stripeKey:"pk_live_bpN0P37nMxrMQkcaHXtAybJk",version:"1.13.0",environment:"Production"});
|
||||
1
js/u2f.min.js
vendored
Normal file
10
u2f-connector.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>U2F Connector</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="js/u2f.min.js?v=yfk9wkit3xr"></script>
|
||||
</body>
|
||||
</html>
|
||||