1
0
mirror of https://github.com/bitwarden/web synced 2025-12-06 00:03:28 +00:00

Update 2017-07-26T17:32:55.910Z

This commit is contained in:
Kyle Spearrin
2017-07-26 13:33:00 -04:00
parent e5bc502e6e
commit b0fedd189d
53 changed files with 2371 additions and 241 deletions

15
app-id.json Normal file
View 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"
]
}
]
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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">

View File

@@ -0,0 +1,25 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</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>

View 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>

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View 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 ? '&times;' + 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>

View File

@@ -0,0 +1,46 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</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>

View File

@@ -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 &amp; 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 @@
&times;12 mo. =
{{((model.additionalSeats || 0) * plans[model.plan].annualSeatPrice) | currency:"$":2}} /year
</span>
<span>
Additional storage:
{{model.additionalStorageGb || 0}} GB &times; {{storageGb.price | currency:"$":2}}
&times;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 @@
&times;{{plans[model.plan].monthlySeatPrice | currency:"$":2}} =
{{((model.additionalSeats || 0) * plans[model.plan].monthlySeatPrice) | currency:"$":2}} /month
</span>
<span>
Additional storage:
{{model.additionalStorageGb || 0}} GB &times; {{storageGb.monthlyPrice | currency:"$":2}} =
{{(model.additionalStorageGb || 0) * storageGb.monthlyPrice | currency:"$":2}} /month
</span>
</label>
</div>
</div>

View 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 &times; {{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>

View File

@@ -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>

View File

@@ -1,135 +0,0 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</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>

View 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>

View File

@@ -0,0 +1,116 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</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>

View File

@@ -0,0 +1,76 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</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>

View File

@@ -0,0 +1,77 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</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>

View File

@@ -0,0 +1,48 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</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>

View File

@@ -0,0 +1,93 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</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>

View File

@@ -0,0 +1,127 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</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>

View File

@@ -0,0 +1,54 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</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>

View File

@@ -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">

View File

@@ -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>

View File

@@ -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>

View File

@@ -0,0 +1,75 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -0,0 +1,13 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</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>

View File

@@ -0,0 +1,36 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</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>

View File

@@ -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 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

File diff suppressed because one or more lines are too long

72
duo-connector.html Normal file
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
images/two-factor/duo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
images/two-factor/fido.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
images/two-factor/gmail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -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

File diff suppressed because one or more lines are too long

2
js/bw.min.js vendored

File diff suppressed because one or more lines are too long

430
js/duo.js Normal file
View 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
};
}));

View File

@@ -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

File diff suppressed because one or more lines are too long

1
js/settings.min.js vendored
View File

@@ -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

File diff suppressed because one or more lines are too long

10
u2f-connector.html Normal file
View 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>