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

Compare commits

..

34 Commits

Author SHA1 Message Date
Kyle Spearrin
bbb69bba26 Update ISSUE_TEMPLATE.md 2018-03-10 16:36:53 -05:00
Kyle Spearrin
c1838b48ff Create ISSUE_TEMPLATE.md 2018-03-10 09:48:22 -05:00
Kyle Spearrin
d53f40002c totp-col breaks at sm, not md 2018-03-09 23:07:43 -05:00
Kyle Spearrin
866954b180 fix lint issues 2018-03-09 16:42:10 -05:00
Kyle Spearrin
befa9cbf08 version bump 2018-03-09 16:39:17 -05:00
Kyle Spearrin
859f44db43 only perpend http if there is no protocol 2018-03-05 22:15:22 -05:00
Kyle Spearrin
cca9c3c561 get rid of apps page and link to bitwarden.com 2018-03-02 22:42:32 -05:00
Kyle Spearrin
27e68e4c75 multi uri support for import/export 2018-03-02 22:13:53 -05:00
Kyle Spearrin
5c92350ed2 refactor for cipher response. add login uris. 2018-03-02 21:12:26 -05:00
Kyle Spearrin
b94c62d1e5 upadte security md 2018-02-27 23:00:10 -05:00
Kyle Spearrin
de888d8a37 remove pwnedtest 2018-02-27 22:42:39 -05:00
Kyle Spearrin
f8d6816101 Uppercase Bitwarden 2018-02-27 22:41:27 -05:00
Kyle Spearrin
119c6d5817 big-textarea not important 2018-02-27 08:21:26 -05:00
Kyle Spearrin
aaa21daa29 only intercept with headers when api is at start 2018-02-26 23:18:03 -05:00
Kyle Spearrin
10f41bf288 pwned test 2018-02-26 22:52:56 -05:00
Kyle Spearrin
91582691d8 whiteListedDomains for jwt 2018-02-26 13:48:26 -05:00
Kyle Spearrin
463efc2254 use new admin apis for attachments 2018-02-24 14:36:13 -05:00
Kyle Spearrin
0333354271 version bump 2018-02-20 23:34:10 -05:00
Kyle Spearrin
b85f56c681 restore collection ids on edit. resolves #174 2018-02-09 10:39:18 -05:00
Kyle Spearrin
be491be2cd Update organizationBilling.html 2018-02-04 16:00:00 -05:00
Kyle Spearrin
4be4a8115d Update settingsBilling.html 2018-02-04 15:58:41 -05:00
Kyle Spearrin
c0eb499f4d value.type should not be case sensitive 2018-01-26 11:55:57 -05:00
Kyle Spearrin
1b43f3facd check for empty name on SIC importer 2018-01-25 21:22:17 -05:00
Chuck
26d41d3cb9 Change npm to use https for gulp-gh-pages restore. (#168)
When using VS 2017 node.js integration, npm fails because a host key cannot be validated. Switching to https, provides security and no additional configuration to restore the package.
2018-01-23 11:43:51 -05:00
Kyle Spearrin
179765f6e4 use random bytes for each HMAC comparison 2018-01-18 12:07:32 -05:00
Kyle Spearrin
df2e332134 macBuf must exist if key has macKey 2018-01-18 09:03:51 -05:00
Kyle Spearrin
2952f9d158 manifest.json included with dist 2018-01-02 23:54:10 -05:00
Kyle Spearrin
3c9face597 disable autocomplete on duo and yubi setup 2018-01-02 23:38:54 -05:00
Kyle Spearrin
25f2e9c1b7 autocomplete="new-password" to disable autofilling 2018-01-02 22:49:05 -05:00
Kyle Spearrin
a6f8e1b9a3 duo connector moved to its own js file 2018-01-02 13:20:58 -05:00
Kyle Spearrin
d832031cec update cdn libs 2017-12-29 09:45:44 -05:00
Kyle Spearrin
7a1a3ab64d revert uglify removal 2017-12-29 09:28:49 -05:00
Kyle Spearrin
19491a684e additional user/pw field names for roboform 2017-12-29 08:43:07 -05:00
Kyle Spearrin
757224287e disable uglify since it seems to be conflicting 2017-12-29 08:40:37 -05:00
50 changed files with 688 additions and 488 deletions

5
ISSUE_TEMPLATE.md Normal file
View File

@@ -0,0 +1,5 @@
<!--
Please do not submit feature requests. The [Community Forums][1] has a
section for submitting, voting for, and discussing product feature requests.
[1]: https://community.bitwarden.com
-->

View File

@@ -1,8 +1,8 @@
[![appveyor build](https://ci.appveyor.com/api/projects/status/github/bitwarden/web?branch=master&svg=true)](https://ci.appveyor.com/project/bitwarden/web) [![DockerHub](https://img.shields.io/docker/pulls/bitwarden/web.svg)](https://hub.docker.com/u/bitwarden/) [![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby)
# bitwarden Web
# Bitwarden Web Vault
The bitwarden Web project is an AngularJS application that powers the web vault (https://vault.bitwarden.com/).
The Bitwarden web project is an AngularJS application that powers the web vault (https://vault.bitwarden.com/).
<img src="https://i.imgur.com/rxrykeX.png" alt="" width="791" height="739" />

View File

@@ -1,4 +1,4 @@
bitwarden believes that working with security researchers across the globe is crucial to keeping our
Bitwarden believes that working with security researchers across the globe is crucial to keeping our
users safe. If you believe you've found a security issue in our product or service, we encourage you to
notify us. We welcome working with you to resolve the issue promptly. Thanks in advance!
@@ -16,7 +16,7 @@ notify us. We welcome working with you to resolve the issue promptly. Thanks in
# In-scope
- Security issues in any current release of bitwarden. This includes the web vault, browser extension,
- Security issues in any current release of Bitwarden. This includes the web vault, browser extension,
and mobile apps (iOS and Android). Product downloads are available at https://bitwarden.com. Source
code is available at https://github.com/bitwarden.
@@ -24,14 +24,14 @@ notify us. We welcome working with you to resolve the issue promptly. Thanks in
The following bug classes are out-of scope:
- Bugs that are already reported on any of bitwarden's issue trackers (https://github.com/bitwarden),
- Bugs that are already reported on any of Bitwarden's issue trackers (https://github.com/bitwarden),
or that we already know of. Note that some of our issue tracking is private.
- Issues in an upstream software dependency (ex: Xamarin, ASP.NET) which are already reported to the
upstream maintainer.
- Attacks requiring physical access to a user's device.
- Self-XSS
- Issues related to software or protocols not under bitwarden's control
- Vulnerabilities in outdated versions of bitwarden
- Issues related to software or protocols not under Bitwarden's control
- Vulnerabilities in outdated versions of Bitwarden
- Missing security best practices that do not directly lead to a vulnerability
- Issues that do not have any impact on the general public
@@ -39,7 +39,7 @@ While researching, we'd like to ask you to refrain from:
- Denial of service
- Spamming
- Social engineering (including phishing) of bitwarden staff or contractors
- Any physical attempts against bitwarden property or data centers
- Social engineering (including phishing) of Bitwarden staff or contractors
- Any physical attempts against Bitwarden property or data centers
Thank you for helping keep bitwarden and our users safe!
Thank you for helping keep Bitwarden and our users safe!

2
dist/.publish vendored

Submodule dist/.publish updated: 1990d717ea...66eacea870

View File

@@ -71,6 +71,7 @@ gulp.task('min:js', ['clean:js'], function () {
'!' + paths.minJs,
'!' + paths.jsDir + 'fallback*.js',
'!' + paths.jsDir + 'u2f-connector.js',
'!' + paths.jsDir + 'duo-connector.js',
'!' + paths.jsDir + 'duo.js',
'!' + paths.jsDir + 'settings.js'
], { base: '.' })
@@ -335,6 +336,10 @@ gulp.task('dist:move', function () {
src: paths.jsDir + 'duo.js',
dest: paths.dist + 'js'
},
{
src: paths.jsDir + 'duo-connector.js',
dest: paths.dist + 'js'
},
{
src: paths.jsDir + 'settings.js',
dest: paths.dist + 'js'
@@ -351,6 +356,7 @@ gulp.task('dist:move', function () {
paths.webroot + 'u2f-connector.html',
paths.webroot + 'duo-connector.html',
paths.webroot + 'favicon.ico',
paths.webroot + 'manifest.json',
paths.webroot + 'app-id.json'
],
dest: paths.dist

View File

@@ -1,6 +1,6 @@
{
"name": "bitwarden",
"version": "1.22.0",
"version": "1.23.0",
"env": "Production",
"devDependencies": {
"connect": "3.6.5",
@@ -11,7 +11,7 @@
"gulp-less": "3.3.2",
"gulp-rename": "1.2.2",
"gulp-uglify": "3.0.0",
"gulp-gh-pages": "git@github.com:tekd/gulp-gh-pages.git#update-dependency",
"gulp-gh-pages": "git+https://github.com/tekd/gulp-gh-pages.git#update-dependency",
"gulp-preprocess": "2.0.0",
"gulp-ng-annotate": "2.0.0",
"gulp-ng-config": "1.5.0",

View File

@@ -49,7 +49,7 @@
<p class="login-box-msg">
Complete logging in with YubiKey.
</p>
<form name="twoFactorForm" ng-submit="twoFactorForm.$valid && twoFactor(token)" api-form="twoFactorPromise"
<form name="twoFactorForm" ng-submit="twoFactorForm.$valid && twoFactor(token)" api-form="twoFactorPromise"
autocomplete="off">
<div class="callout callout-danger validation-errors" ng-show="twoFactorForm.$errors">
<h4>Errors have occurred</h4>
@@ -63,7 +63,8 @@
</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 />
<input type="password" id="code" name="Token" class="form-control" ng-model="token"
autocomplete="new-password" required api-field />
</div>
<div class="row">
<div class="col-xs-7">
@@ -86,7 +87,7 @@
<p class="login-box-msg">
Complete logging in with Duo.
</p>
<form name="twoFactorForm" ng-submit="twoFactorForm.$valid && twoFactor(token)" api-form="twoFactorPromise"
<form name="twoFactorForm" ng-submit="twoFactorForm.$valid && twoFactor(token)" api-form="twoFactorPromise"
autocomplete="off">
<div class="callout callout-danger validation-errors" ng-show="twoFactorForm.$errors">
<h4>Errors have occurred</h4>

View File

@@ -14,7 +14,7 @@
<p class="text-center"><strong>{{state.params.email}}</strong></p>
<p>
You've been invited to join the organization listed above.
To accept the invitation, you need to log in or create a new bitwarden account.
To accept the invitation, you need to log in or create a new Bitwarden account.
</p>
<hr />
<div class="row">

View File

@@ -3,7 +3,7 @@
<i class="fa fa-shield"></i> <b>bit</b>warden
</div>
<div class="login-box-body">
<p class="login-box-msg">Enter your email address below to recover &amp; delete your bitwarden account.</p>
<p class="login-box-msg">Enter your email address below to recover &amp; delete your Bitwarden account.</p>
<div ng-show="success" class="text-center">
<div class="callout callout-success">
If your account exists ({{model.email}}) we've sent you an email with further instructions.

View File

@@ -12,7 +12,7 @@
This will permanently delete your account. This cannot be undone.
</div>
<p>
You have requested to delete your bitwarden account (<b>{{email}}</b>).
You have requested to delete your Bitwarden account (<b>{{email}}</b>).
Click the button below to confirm and proceed.
</p>
<button ng-click="delete()" class="btn btn-danger btn-block btn-flat">Delete Account</button>

View File

@@ -4,7 +4,7 @@ angular
.factory('apiInterceptor', function ($injector, $q, toastr, appSettings, utilsService) {
return {
request: function (config) {
if (config.url.indexOf(appSettings.apiUri + '/') > -1) {
if (config.url.indexOf(appSettings.apiUri + '/') === 0) {
config.headers['Device-Type'] = utilsService.getDeviceType();
}

View File

@@ -14,15 +14,13 @@ angular
$qProvider.errorOnUnhandledRejections(false);
$locationProvider.hashPrefix('');
// @if false
jwtOptionsProvider.config({
whiteListedDomains: ['localhost', 'api.bitwarden.com', 'vault.bitwarden.com']
whiteListedDomains: ['localhost', 'api.bitwarden.com', 'vault.bitwarden.com', 'haveibeenpwned.com']
});
// @endif
var refreshPromise;
jwtInterceptorProvider.tokenGetter = /*@ngInject*/ function (options, tokenService, authService) {
if (options.url.indexOf(appSettings.apiUri + '/') === -1) {
if (options.url.indexOf(appSettings.apiUri + '/') !== 0) {
return;
}
@@ -154,12 +152,6 @@ angular
controller: 'reportsBreachController',
data: { pageTitle: 'Data Breach Report' }
})
.state('backend.user.apps', {
url: '^/apps',
templateUrl: 'app/views/apps.html',
controller: 'appsController',
data: { pageTitle: 'Get the Apps' }
})
.state('backend.org', {
templateUrl: 'app/views/organizationLayout.html',
abstract: true

View File

@@ -6,7 +6,7 @@ angular
link: function (scope, element) {
var listener = function (event, toState, toParams, fromState, fromParams) {
// Default title
var title = 'bitwarden Web Vault';
var title = 'Bitwarden Web Vault';
if (toState.data && toState.data.pageTitle) {
title = toState.data.pageTitle + ' - ' + title;
}

View File

@@ -1,6 +0,0 @@
angular
.module('bit.global')
.controller('appsController', function ($scope, $state) {
});

View File

@@ -26,8 +26,9 @@
return;
}
var i;
var collectionsDict = {};
for (var i = 0; i < decCollections.length; i++) {
for (i = 0; i < decCollections.length; i++) {
collectionsDict[decCollections[i].id] = decCollections[i];
}
@@ -78,10 +79,17 @@
switch (decCiphers[i].type) {
case constants.cipherType.login:
cipher.type = 'login';
cipher.login_uri = decCiphers[i].login.uri;
cipher.login_uri = null;
cipher.login_username = decCiphers[i].login.username;
cipher.login_password = decCiphers[i].login.password;
cipher.login_totp = decCiphers[i].login.totp;
if (decCiphers[i].login.uris && decCiphers[i].login.uris.length) {
cipher.login_uri = [];
for (j = 0; j < decCiphers[i].login.uris.length; j++) {
cipher.login_uri.push(decCiphers[i].login.uris[j].uri);
}
}
break;
case constants.cipherType.secureNote:
cipher.type = 'note';

View File

@@ -11,7 +11,7 @@
$scope.options = [
{
id: 'bitwardencsv',
name: 'bitwarden (csv)',
name: 'Bitwarden (csv)',
featured: true,
sort: 1,
instructions: $sce.trustAsHtml('Export using the web vault (vault.bitwarden.com). ' +

View File

@@ -8,7 +8,13 @@
$scope.selectedType = constants.cipherType.login.toString();
$scope.cipher = {
type: constants.cipherType.login,
login: {},
login: {
uris: [{
uri: null,
match: null,
matchValue: null
}]
},
identity: {},
card: {},
secureNote: {
@@ -44,6 +50,42 @@
}
};
$scope.addUri = function () {
if (!$scope.cipher.login) {
return;
}
if (!$scope.cipher.login.uris) {
$scope.cipher.login.uris = [];
}
$scope.cipher.login.uris.push({
uri: null,
match: null,
matchValue: null
});
};
$scope.removeUri = function (uri) {
if (!$scope.cipher.login || !$scope.cipher.login.uris) {
return;
}
var index = $scope.cipher.login.uris.indexOf(uri);
if (index > -1) {
$scope.cipher.login.uris.splice(index, 1);
}
};
$scope.uriMatchChanged = function (uri) {
if ((!uri.matchValue && uri.matchValue !== 0) || uri.matchValue === '') {
uri.match = null;
}
else {
uri.match = parseInt(uri.matchValue);
}
};
$scope.addField = function () {
if (!$scope.cipher.fields) {
$scope.cipher.fields = [];

View File

@@ -29,7 +29,7 @@
var fd = new FormData();
var blob = new Blob([encValue.data], { type: 'application/octet-stream' });
fd.append('data', blob, encValue.fileName);
return apiService.ciphers.postAttachment({ id: cipherId }, fd).$promise;
return apiService.ciphers.postAttachmentAdmin({ id: cipherId }, fd).$promise;
}).then(function (response) {
$analytics.eventTrack('Added Attachment');
toastr.success('The attachment has been added.');
@@ -61,7 +61,7 @@
}
attachment.loading = true;
apiService.ciphers.delAttachment({ id: cipherId, attachmentId: attachment.id }).$promise.then(function () {
apiService.ciphers.delAttachmentAdmin({ id: cipherId, attachmentId: attachment.id }).$promise.then(function () {
attachment.loading = false;
$analytics.eventTrack('Deleted Organization Attachment');
var index = $scope.cipher.attachments.indexOf(attachment);

View File

@@ -11,6 +11,7 @@
apiService.ciphers.getAdmin({ id: cipherId }, function (cipher) {
$scope.cipher = cipherService.decryptCipher(cipher);
$scope.useTotp = $scope.cipher.organizationUseTotp;
setUriMatchValues();
});
$scope.save = function (model) {
@@ -32,6 +33,42 @@
}
};
$scope.addUri = function () {
if (!$scope.cipher.login) {
return;
}
if (!$scope.cipher.login.uris) {
$scope.cipher.login.uris = [];
}
$scope.cipher.login.uris.push({
uri: null,
match: null,
matchValue: null
});
};
$scope.removeUri = function (uri) {
if (!$scope.cipher.login || !$scope.cipher.login.uris) {
return;
}
var index = $scope.cipher.login.uris.indexOf(uri);
if (index > -1) {
$scope.cipher.login.uris.splice(index, 1);
}
};
$scope.uriMatchChanged = function (uri) {
if ((!uri.matchValue && uri.matchValue !== 0) || uri.matchValue === '') {
uri.match = null;
}
else {
uri.match = parseInt(uri.matchValue);
}
};
$scope.addField = function () {
if (!$scope.cipher.login.fields) {
$scope.cipher.login.fields = [];
@@ -98,4 +135,14 @@
}
});
};
function setUriMatchValues() {
if ($scope.cipher.login && $scope.cipher.login.uris) {
for (var i = 0; i < $scope.cipher.login.uris.length; i++) {
$scope.cipher.login.uris[i].matchValue =
$scope.cipher.login.uris[i].match || $scope.cipher.login.uris[i].match === 0 ?
$scope.cipher.login.uris[i].match.toString() : '';
}
}
}
});

View File

@@ -116,7 +116,7 @@
Loading...
</div>
<div ng-show="!loading">
You plan currently has a total of <b>{{plan.seats}}</b> seats.
Your plan currently has a total of <b>{{plan.seats}}</b> seats.
</div>
</div>
<div class="box-footer" ng-if="!selfHosted && !noSubscription && canAdjustSeats">
@@ -134,7 +134,7 @@
</div>
<div class="box-body">
<p>
You plan has a total of {{storage.maxGb}} GB of encrypted file storage.
Your 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;">

View File

@@ -5,8 +5,8 @@
<form name="inviteForm" ng-submit="inviteForm.$valid && submit(model)" api-form="submitPromise" autocomplete="off">
<div class="modal-body">
<p>
Invite a new user to your organization by entering their bitwarden account email address below. If they do not have
a bitwarden account already, they will be prompted to create a new account.
Invite a new user to your organization by entering their Bitwarden account email address below. If they do not have
a Bitwarden account already, they will be prompted to create a new account.
</p>
<div class="callout callout-danger validation-errors" ng-show="inviteForm.$errors">
<h4>Errors have occurred</h4>

View File

@@ -42,13 +42,20 @@
headers: { 'Content-Type': undefined },
params: { id: '@id' }
},
postAttachmentAdmin: {
url: _apiUri + '/ciphers/:id/attachment-admin',
method: 'POST',
headers: { 'Content-Type': undefined },
params: { id: '@id' }
},
postShareAttachment: {
url: _apiUri + '/ciphers/:id/attachment/:attachmentId/share?organizationId=:orgId',
method: 'POST',
headers: { 'Content-Type': undefined },
params: { id: '@id', attachmentId: '@attachmentId', orgId: '@orgId' }
},
delAttachment: { url: _apiUri + '/ciphers/:id/attachment/:attachmentId/delete', method: 'POST', params: { id: '@id', attachmentId: '@attachmentId' } }
delAttachment: { url: _apiUri + '/ciphers/:id/attachment/:attachmentId/delete', method: 'POST', params: { id: '@id', attachmentId: '@attachmentId' } },
delAttachmentAdmin: { url: _apiUri + '/ciphers/:id/attachment/:attachmentId/delete-admin', method: 'POST', params: { id: '@id', attachmentId: '@attachmentId' } }
});
_service.organizations = $resource(_apiUri + '/organizations/:id', {}, {

View File

@@ -30,6 +30,9 @@ angular
organizationId: encryptedCipher.OrganizationId,
collectionIds: encryptedCipher.CollectionIds || [],
'type': encryptedCipher.Type,
name: cryptoService.decrypt(encryptedCipher.Name, key),
notes: _service.decryptProperty(encryptedCipher.Notes, key, true, false),
fields: _service.decryptFields(key, encryptedCipher.Fields),
folderId: encryptedCipher.FolderId,
favorite: encryptedCipher.Favorite,
edit: encryptedCipher.Edit,
@@ -38,62 +41,68 @@ angular
icon: null
};
var cipherData = encryptedCipher.Data;
if (cipherData) {
cipher.name = cryptoService.decrypt(cipherData.Name, key);
cipher.notes = _service.decryptProperty(cipherData.Notes, key, true, false);
cipher.fields = _service.decryptFields(key, cipherData.Fields);
var dataObj = {};
switch (cipher.type) {
case constants.cipherType.login:
dataObj.uri = _service.decryptProperty(cipherData.Uri, key, true, false);
dataObj.username = _service.decryptProperty(cipherData.Username, key, true, false);
dataObj.password = _service.decryptProperty(cipherData.Password, key, true, false);
dataObj.totp = _service.decryptProperty(cipherData.Totp, key, true, false);
cipher.login = dataObj;
cipher.icon = 'fa-globe';
break;
case constants.cipherType.secureNote:
dataObj.type = cipherData.Type;
cipher.secureNote = dataObj;
cipher.icon = 'fa-sticky-note-o';
break;
case constants.cipherType.card:
dataObj.cardholderName = _service.decryptProperty(cipherData.CardholderName, key, true, false);
dataObj.number = _service.decryptProperty(cipherData.Number, key, true, false);
dataObj.brand = _service.decryptProperty(cipherData.Brand, key, true, false);
dataObj.expMonth = _service.decryptProperty(cipherData.ExpMonth, key, true, false);
dataObj.expYear = _service.decryptProperty(cipherData.ExpYear, key, true, false);
dataObj.code = _service.decryptProperty(cipherData.Code, key, true, false);
cipher.card = dataObj;
cipher.icon = 'fa-credit-card';
break;
case constants.cipherType.identity:
dataObj.title = _service.decryptProperty(cipherData.Title, key, true, false);
dataObj.firstName = _service.decryptProperty(cipherData.FirstName, key, true, false);
dataObj.middleName = _service.decryptProperty(cipherData.MiddleName, key, true, false);
dataObj.lastName = _service.decryptProperty(cipherData.LastName, key, true, false);
dataObj.address1 = _service.decryptProperty(cipherData.Address1, key, true, false);
dataObj.address2 = _service.decryptProperty(cipherData.Address2, key, true, false);
dataObj.address3 = _service.decryptProperty(cipherData.Address3, key, true, false);
dataObj.city = _service.decryptProperty(cipherData.City, key, true, false);
dataObj.state = _service.decryptProperty(cipherData.State, key, true, false);
dataObj.postalCode = _service.decryptProperty(cipherData.PostalCode, key, true, false);
dataObj.country = _service.decryptProperty(cipherData.Country, key, true, false);
dataObj.company = _service.decryptProperty(cipherData.Company, key, true, false);
dataObj.email = _service.decryptProperty(cipherData.Email, key, true, false);
dataObj.phone = _service.decryptProperty(cipherData.Phone, key, true, false);
dataObj.ssn = _service.decryptProperty(cipherData.SSN, key, true, false);
dataObj.username = _service.decryptProperty(cipherData.Username, key, true, false);
dataObj.passportNumber = _service.decryptProperty(cipherData.PassportNumber, key, true, false);
dataObj.licenseNumber = _service.decryptProperty(cipherData.LicenseNumber, key, true, false);
cipher.identity = dataObj;
cipher.icon = 'fa-id-card-o';
break;
default:
break;
}
var i;
switch (cipher.type) {
case constants.cipherType.login:
cipher.login = {
username: _service.decryptProperty(encryptedCipher.Login.Username, key, true, false),
password: _service.decryptProperty(encryptedCipher.Login.Password, key, true, false),
totp: _service.decryptProperty(encryptedCipher.Login.Totp, key, true, false),
uris: null
};
if (encryptedCipher.Login.Uris) {
cipher.login.uris = [];
for (i = 0; i < encryptedCipher.Login.Uris.length; i++) {
cipher.login.uris.push({
uri: _service.decryptProperty(encryptedCipher.Login.Uris[i].Uri, key, true, false),
match: encryptedCipher.Login.Uris[i].Match
});
}
}
cipher.icon = 'fa-globe';
break;
case constants.cipherType.secureNote:
cipher.secureNote = {
type: encryptedCipher.SecureNote.Type
};
cipher.icon = 'fa-sticky-note-o';
break;
case constants.cipherType.card:
cipher.card = {
cardholderName: _service.decryptProperty(encryptedCipher.Card.CardholderName, key, true, false),
number: _service.decryptProperty(encryptedCipher.Card.Number, key, true, false),
brand: _service.decryptProperty(encryptedCipher.Card.Brand, key, true, false),
expMonth: _service.decryptProperty(encryptedCipher.Card.ExpMonth, key, true, false),
expYear: _service.decryptProperty(encryptedCipher.Card.ExpYear, key, true, false),
code: _service.decryptProperty(encryptedCipher.Card.Code, key, true, false)
};
cipher.icon = 'fa-credit-card';
break;
case constants.cipherType.identity:
cipher.identity = {
title: _service.decryptProperty(encryptedCipher.Identity.Title, key, true, false),
firstName: _service.decryptProperty(encryptedCipher.Identity.FirstName, key, true, false),
middleName: _service.decryptProperty(encryptedCipher.Identity.MiddleName, key, true, false),
lastName: _service.decryptProperty(encryptedCipher.Identity.LastName, key, true, false),
address1: _service.decryptProperty(encryptedCipher.Identity.Address1, key, true, false),
address2: _service.decryptProperty(encryptedCipher.Identity.Address2, key, true, false),
address3: _service.decryptProperty(encryptedCipher.Identity.Address3, key, true, false),
city: _service.decryptProperty(encryptedCipher.Identity.City, key, true, false),
state: _service.decryptProperty(encryptedCipher.Identity.State, key, true, false),
postalCode: _service.decryptProperty(encryptedCipher.Identity.PostalCode, key, true, false),
country: _service.decryptProperty(encryptedCipher.Identity.Country, key, true, false),
company: _service.decryptProperty(encryptedCipher.Identity.Company, key, true, false),
email: _service.decryptProperty(encryptedCipher.Identity.Email, key, true, false),
phone: _service.decryptProperty(encryptedCipher.Identity.Phone, key, true, false),
ssn: _service.decryptProperty(encryptedCipher.Identity.SSN, key, true, false),
username: _service.decryptProperty(encryptedCipher.Identity.Username, key, true, false),
passportNumber: _service.decryptProperty(encryptedCipher.Identity.PassportNumber, key, true, false),
licenseNumber: _service.decryptProperty(encryptedCipher.Identity.LicenseNumber, key, true, false)
};
cipher.icon = 'fa-id-card-o';
break;
default:
break;
}
if (!encryptedCipher.Attachments) {
@@ -101,7 +110,7 @@ angular
}
cipher.attachments = [];
for (var i = 0; i < encryptedCipher.Attachments.length; i++) {
for (i = 0; i < encryptedCipher.Attachments.length; i++) {
cipher.attachments.push(_service.decryptAttachment(key, encryptedCipher.Attachments[i]));
}
@@ -121,6 +130,7 @@ angular
organizationId: encryptedCipher.OrganizationId,
collectionIds: encryptedCipher.CollectionIds || [],
'type': encryptedCipher.Type,
name: _service.decryptProperty(encryptedCipher.Name, key, false, true),
folderId: encryptedCipher.FolderId,
favorite: encryptedCipher.Favorite,
edit: encryptedCipher.Edit,
@@ -130,59 +140,56 @@ angular
icon: null
};
var cipherData = encryptedCipher.Data;
if (cipherData) {
cipher.name = _service.decryptProperty(cipherData.Name, key, false, true);
var dataObj = {};
switch (cipher.type) {
case constants.cipherType.login:
cipher.subTitle = _service.decryptProperty(cipherData.Username, key, true, true);
cipher.meta.password = _service.decryptProperty(cipherData.Password, key, true, true);
cipher.meta.uri = _service.decryptProperty(cipherData.Uri, key, true, true);
setLoginIcon(cipher, cipher.meta.uri, true);
break;
case constants.cipherType.secureNote:
cipher.subTitle = null;
cipher.icon = 'fa-sticky-note-o';
break;
case constants.cipherType.card:
cipher.subTitle = '';
cipher.meta.number = _service.decryptProperty(cipherData.Number, key, true, true);
var brand = _service.decryptProperty(cipherData.Brand, key, true, true);
if (brand) {
cipher.subTitle = brand;
}
if (cipher.meta.number && cipher.meta.number.length >= 4) {
if (cipher.subTitle !== '') {
cipher.subTitle += ', ';
}
cipher.subTitle += ('*' + cipher.meta.number.substr(cipher.meta.number.length - 4));
}
cipher.icon = 'fa-credit-card';
break;
case constants.cipherType.identity:
var firstName = _service.decryptProperty(cipherData.FirstName, key, true, true);
var lastName = _service.decryptProperty(cipherData.LastName, key, true, true);
cipher.subTitle = '';
if (firstName) {
cipher.subTitle = firstName;
}
if (lastName) {
if (cipher.subTitle !== '') {
cipher.subTitle += ' ';
}
cipher.subTitle += lastName;
}
cipher.icon = 'fa-id-card-o';
break;
default:
break;
}
if (cipher.subTitle === '') {
switch (cipher.type) {
case constants.cipherType.login:
cipher.subTitle = _service.decryptProperty(encryptedCipher.Login.Username, key, true, true);
cipher.meta.password = _service.decryptProperty(encryptedCipher.Login.Password, key, true, true);
cipher.meta.uri = null;
if (encryptedCipher.Login.Uris && encryptedCipher.Login.Uris.length) {
cipher.meta.uri = _service.decryptProperty(encryptedCipher.Login.Uris[0].Uri, key, true, true);
}
setLoginIcon(cipher, cipher.meta.uri, true);
break;
case constants.cipherType.secureNote:
cipher.subTitle = null;
}
cipher.icon = 'fa-sticky-note-o';
break;
case constants.cipherType.card:
cipher.subTitle = '';
cipher.meta.number = _service.decryptProperty(encryptedCipher.Card.Number, key, true, true);
var brand = _service.decryptProperty(encryptedCipher.Card.Brand, key, true, true);
if (brand) {
cipher.subTitle = brand;
}
if (cipher.meta.number && cipher.meta.number.length >= 4) {
if (cipher.subTitle !== '') {
cipher.subTitle += ', ';
}
cipher.subTitle += ('*' + cipher.meta.number.substr(cipher.meta.number.length - 4));
}
cipher.icon = 'fa-credit-card';
break;
case constants.cipherType.identity:
var firstName = _service.decryptProperty(encryptedCipher.Identity.FirstName, key, true, true);
var lastName = _service.decryptProperty(encryptedCipher.Identity.LastName, key, true, true);
cipher.subTitle = '';
if (firstName) {
cipher.subTitle = firstName;
}
if (lastName) {
if (cipher.subTitle !== '') {
cipher.subTitle += ' ';
}
cipher.subTitle += lastName;
}
cipher.icon = 'fa-id-card-o';
break;
default:
break;
}
if (cipher.subTitle === '') {
cipher.subTitle = null;
}
return cipher;
@@ -389,15 +396,24 @@ angular
fields: _service.encryptFields(unencryptedCipher.fields, key)
};
var i;
switch (cipher.type) {
case constants.cipherType.login:
var loginData = unencryptedCipher.login;
cipher.login = {
uri: encryptProperty(loginData.uri, key),
username: encryptProperty(loginData.username, key),
password: encryptProperty(loginData.password, key),
totp: encryptProperty(loginData.totp, key)
};
if (loginData.uris && loginData.uris.length) {
cipher.login.uris = [];
for (i = 0; i < loginData.uris.length; i++) {
cipher.login.uris.push({
uri: encryptProperty(loginData.uris[i].uri, key),
match: loginData.uris[i].match
});
}
}
break;
case constants.cipherType.secureNote:
cipher.secureNote = {
@@ -444,7 +460,7 @@ angular
if (unencryptedCipher.attachments && attachments) {
cipher.attachments = {};
for (var i = 0; i < unencryptedCipher.attachments.length; i++) {
for (i = 0; i < unencryptedCipher.attachments.length; i++) {
cipher.attachments[unencryptedCipher.attachments[i].id] =
cryptoService.encrypt(unencryptedCipher.attachments[i].fileName, key);
}

View File

@@ -540,7 +540,7 @@ angular
if (key.macKey && encPieces.length > 2) {
var macBytes = forge.util.decode64(encPieces[2]);
var computedMacBytes = computeMac(ivBytes + ctBytes, key.macKey, false);
if (!macsEqual(key.macKey, macBytes, computedMacBytes)) {
if (!macsEqual(macBytes, computedMacBytes)) {
console.error('MAC failed.');
return null;
}
@@ -623,6 +623,10 @@ angular
throw 'Encryption key unavailable.';
}
if (key.macKey && !macBuf) {
throw 'macBuf required for this type of key.';
}
if (encType !== key.encType) {
throw 'encType unavailable.';
}
@@ -646,7 +650,7 @@ angular
if (computedMacBuf === null) {
return null;
}
return macsEqualWC(keyBuf.macKey, macBuf, computedMacBuf);
return macsEqualWC(macBuf, computedMacBuf);
}).then(function (macsMatch) {
if (macsMatch === false) {
console.error('MAC failed.');
@@ -704,7 +708,7 @@ angular
if (key && key.macKey && encPieces.length > 1) {
var macBytes = forge.util.decode64(encPieces[1]);
var computedMacBytes = computeMac(ctBytes, key.macKey, false);
if (!macsEqual(key.macKey, macBytes, computedMacBytes)) {
if (!macsEqual(macBytes, computedMacBytes)) {
console.error('MAC failed.');
return null;
}
@@ -747,10 +751,11 @@ angular
// Safely compare two MACs in a way that protects against timing attacks (Double HMAC Verification).
// ref: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/
function macsEqual(macKey, mac1, mac2) {
// ref: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy
function macsEqual(mac1, mac2) {
var hmac = forge.hmac.create();
hmac.start('sha256', macKey);
hmac.start('sha256', getRandomBytes(32));
hmac.update(mac1);
mac1 = hmac.digest().getBytes();
@@ -761,11 +766,14 @@ angular
return mac1 === mac2;
}
function macsEqualWC(macKeyBuf, mac1Buf, mac2Buf) {
function macsEqualWC(mac1Buf, mac2Buf) {
var mac1,
macKey;
return window.crypto.subtle.importKey('raw', macKeyBuf, { name: 'HMAC', hash: { name: 'SHA-256' } }, false, ['sign'])
var compareKey = new Uint8Array(32);
_crypto.getRandomValues(compareKey);
return window.crypto.subtle.importKey('raw', compareKey.buffer, { name: 'HMAC', hash: { name: 'SHA-256' } }, false, ['sign'])
.then(function (key) {
macKey = key;
return window.crypto.subtle.sign({ name: 'HMAC', hash: { name: 'SHA-256' } }, macKey, mac1Buf);
@@ -932,5 +940,15 @@ angular
return new Uint8Array(result);
}
function getRandomBytes(byteLength) {
var bytes = new Uint32Array(byteLength / 4);
_crypto.getRandomValues(bytes);
var buffer = forge.util.createBuffer();
for (var i = 0; i < bytes.length; i++) {
buffer.putInt32(bytes[i]);
}
return buffer.getBytes();
}
return _service;
});

View File

@@ -133,7 +133,7 @@
var _passwordFieldNames = [
'password', 'pass word', 'passphrase', 'pass phrase',
'pass', 'code', 'code word', 'codeword',
'secret', 'secret word',
'secret', 'secret word', 'personpwd',
'key', 'keyword', 'key word', 'keyphrase', 'key phrase',
'form_pw', 'wppassword', 'pin', 'pwd', 'pw', 'pword', 'passwd',
'p', 'serial', 'serial#', 'license key', 'reg #',
@@ -146,7 +146,7 @@
'user', 'name', 'user name', 'username', 'login name',
'email', 'e-mail', 'id', 'userid', 'user id',
'login', 'form_loginname', 'wpname', 'mail',
'loginid', 'login id', 'log',
'loginid', 'login id', 'log', 'personlogin',
'first name', 'last name', 'card#', 'account #',
'member', 'member #',
@@ -191,14 +191,10 @@
function fixUri(uri) {
uri = uri.toLowerCase().trim();
if (!uri.startsWith('http') && uri.indexOf('.') >= 0) {
if (uri.indexOf('://') === -1 && uri.indexOf('.') >= 0) {
uri = 'http://' + uri;
}
return trimUri(uri);
}
function trimUri(uri) {
if (uri.length > 1000) {
return uri.substring(0, 1000);
}
@@ -206,6 +202,43 @@
return uri;
}
function makeUriArray(uri) {
if (!uri) {
return null;
}
if (typeof uri === 'string') {
return [{
uri: fixUri(uri),
match: null
}];
}
if (uri.length) {
var returnArr = [];
for (var i = 0; i < uri.length; i++) {
returnArr.push({
uri: fixUri(uri[i]),
match: null
});
}
return returnArr;
}
return null;
}
function parseSingleRowCsv(rowData) {
if (!rowData || rowData === '') {
return null;
}
var parsedRow = Papa.parse(rowData);
if (parsedRow && parsedRow.data && parsedRow.data.length && parsedRow.data[0].length) {
return parsedRow.data[0];
}
return null;
}
function parseCsvErrors(results) {
if (results.errors && results.errors.length) {
for (var i = 0; i < results.errors.length; i++) {
@@ -360,17 +393,20 @@
}
}
switch (value.type) {
case 'login': case null: case undefined:
var valueType = value.type ? value.type.toLowerCase() : null;
switch (valueType) {
case 'login':
case null:
case undefined:
cipher.type = constants.cipherType.login;
var totp = value.login_totp || value.totp;
var uri = value.login_uri || value.uri;
var uris = parseSingleRowCsv(value.login_uri || value.uri);
var username = value.login_username || value.username;
var password = value.login_password || value.password;
cipher.login = {
totp: totp && totp !== '' ? totp : null,
uri: uri && uri !== '' ? trimUri(uri) : null,
uris: makeUriArray(uris),
username: username && username !== '' ? username : null,
password: password && password !== '' ? password : null
};
@@ -487,17 +523,20 @@
}
}
switch (value.type) {
case 'login': case null: case undefined:
var valueType = value.type ? value.type.toLowerCase() : null;
switch (valueType) {
case 'login':
case null:
case undefined:
cipher.type = constants.cipherType.login;
var totp = value.login_totp || value.totp;
var uri = value.login_uri || value.uri;
var uris = parseSingleRowCsv(value.login_uri || value.uri);
var username = value.login_username || value.username;
var password = value.login_password || value.password;
cipher.login = {
totp: totp && totp !== '' ? totp : null,
uri: uri && uri !== '' ? trimUri(uri) : null,
uris: makeUriArray(uris),
username: username && username !== '' ? username : null,
password: password && password !== '' ? password : null
};
@@ -684,7 +723,7 @@
if (cipher.type === constants.cipherType.login) {
cipher.login = {
uri: value.url && value.url !== '' ? trimUri(value.url) : null,
uris: makeUriArray(value.url),
username: value.username && value.username !== '' ? value.username : null,
password: value.password && value.password !== '' ? value.password : null
};
@@ -844,6 +883,10 @@
fields: null
};
if (!cipher.name || cipher.name === '') {
cipher.name = '--';
}
if (card.attr('type') === 'note') {
cipher.type = constants.cipherType.secureNote;
cipher.secureNote = {
@@ -873,7 +916,7 @@
cipher.notes += (text + '\n');
}
else if (type === 'weblogin' || type === 'website') {
cipher.login.uri = trimUri(text);
cipher.login.uris = makeUriArray(text);
}
else if (text.length > 200) {
cipher.notes += (name + ': ' + text + '\n');
@@ -974,7 +1017,7 @@
notes: null,
name: value[0] && value[0] !== '' ? value[0] : '--',
login: {
uri: null,
uris: null,
username: value[2] && value[2] !== '' ? value[2] : null,
password: value[3] && value[3] !== '' ? value[3] : null
},
@@ -990,7 +1033,7 @@
var cfHeader = customFieldHeaders[j - 4];
if (cfHeader.toLowerCase() === 'url' || cfHeader.toLowerCase() === 'uri') {
cipher.login.uri = trimUri(cf);
cipher.login.uris = makeUriArray(cf);
}
else {
if (!cipher.fields) {
@@ -1076,7 +1119,7 @@
name: null,
type: constants.cipherType.login,
login: {
uri: null,
uris: null,
username: null,
password: null
},
@@ -1095,7 +1138,7 @@
switch (key) {
case 'URL':
cipher.login.uri = fixUri(value);
cipher.login.uris = makeUriArray(value);
break;
case 'UserName':
cipher.login.username = value;
@@ -1197,7 +1240,7 @@
notes: value.Notes && value.Notes !== '' ? value.Notes : null,
name: value.Title && value.Title !== '' ? value.Title : '--',
login: {
uri: value.URL && value.URL !== '' ? fixUri(value.URL) : null,
uris: makeUriArray(value.URL),
username: value.Username && value.Username !== '' ? value.Username : null,
password: value.Password && value.Password !== '' ? value.Password : null
}
@@ -1310,7 +1353,7 @@
else {
cipher.type = constants.cipherType.login;
cipher.login = {
uri: item.location && item.location !== '' ? fixUri(item.location) : null,
uris: makeUriArray(item.location),
username: null,
password: null,
totp: null
@@ -1365,7 +1408,7 @@
notes: value.notesPlain && value.notesPlain !== '' ? value.notesPlain : '',
name: value.title && value.title !== '' ? value.title : '--',
login: {
uri: null,
uris: null,
username: null,
password: null
}
@@ -1383,17 +1426,9 @@
else if (!cipher.login.username && property === 'username') {
cipher.login.username = value[property];
}
else if (!cipher.login.uri && property === 'urls') {
else if (!cipher.login.uris && property === 'urls') {
var urls = value[property].split(/(?:\r\n|\r|\n)/);
cipher.login.uri = fixUri(urls[0]);
for (var j = 1; j < urls.length; j++) {
if (cipher.notes !== '') {
cipher.notes += '\n';
}
cipher.notes += ('url ' + (j + 1) + ': ' + urls[j]);
}
cipher.login.uris = makeUriArray(urls);
}
else if (property !== 'ainfo' && property !== 'autosubmit' && property !== 'notesPlain' &&
property !== 'ps' && property !== 'scope' && property !== 'tags' && property !== 'title' &&
@@ -1437,7 +1472,7 @@
notes: null,
name: value.name && value.name !== '' ? value.name : '--',
login: {
uri: value.url && value.url !== '' ? trimUri(value.url) : null,
uris: makeUriArray(value.url),
username: value.username && value.username !== '' ? value.username : null,
password: value.password && value.password !== '' ? value.password : null
}
@@ -1491,7 +1526,7 @@
notes: null,
name: getNameFromHost(host),
login: {
uri: host && host !== '' ? trimUri(host) : null,
uris: makeUriArray(host),
username: user && user !== '' ? user : null,
password: password && password !== '' ? password : null,
}
@@ -1527,7 +1562,7 @@
notes: value[4] && value[4] !== '' ? value[4] : null,
name: value[0] && value[0] !== '' ? value[0] : '--',
login: {
uri: value[3] && value[3] !== '' ? trimUri(value[3]) : null,
uris: makeUriArray(value[3]),
username: value[1] && value[1] !== '' ? value[1] : null,
password: value[2] && value[2] !== '' ? value[2] : null
}
@@ -1574,7 +1609,7 @@
notes: value[5] && value[5] !== '' ? value[5] : null,
name: value[1] && value[1] !== '' ? value[1] : '--',
login: {
uri: value[4] && value[4] !== '' ? trimUri(value[4]) : null,
uris: makeUriArray(value[4]),
username: value[2] && value[2] !== '' ? value[2] : null,
password: value[3] && value[3] !== '' ? value[3] : null
},
@@ -1682,7 +1717,7 @@
notes: notes && notes.text() !== '' ? notes.text() : null,
name: accountName && accountName.text() !== '' ? accountName.text() : '--',
login: {
uri: url && url.text() !== '' ? trimUri(url.text()) : null,
uris: url ? makeUriArray(url.text()) : null,
username: userId && userId.text() !== '' ? userId.text() : null,
password: password && password.text() !== '' ? password.text() : null
},
@@ -1782,7 +1817,7 @@
notes: note && note !== '' ? note : null,
fields: null,
login: {
uri: null,
uris: null,
password: null,
username: null,
totp: null
@@ -1799,8 +1834,8 @@
var field = row[i + 1];
var fieldLower = field.toLowerCase();
if (fieldLower === 'url' && !cipher.login.uri) {
cipher.login.uri = trimUri(value);
if (fieldLower === 'url' && !cipher.login.uris) {
cipher.login.uris = makeUriArray(value);
}
else if ((fieldLower === 'username' || fieldLower === 'email') && !cipher.login.username) {
cipher.login.username = value;
@@ -1901,7 +1936,7 @@
notes: notes && notesText !== '' ? notesText : null,
name: title && title.text() !== '' ? title.text() : '--',
login: {
uri: url && url.text() !== '' ? trimUri(url.text()) : null,
uris: url ? makeUriArray(url.text()) : null,
username: username && username.text() !== '' ? username.text() : null,
password: password && password.text() !== '' ? password.text() : null
}
@@ -1963,17 +1998,17 @@
favorite: false,
notes: null,
login: {
uri: null,
uris: null,
password: null,
username: null
}
};
if (row.length === 2) {
cipher.login.uri = fixUri(row[1]);
cipher.login.uris = makeUriArray(row[1]);
}
else if (row.length === 3) {
cipher.login.uri = fixUri(row[1]);
cipher.login.uris = makeUriArray(row[1]);
cipher.login.username = row[2];
}
else if (row.length === 4) {
@@ -1987,7 +2022,7 @@
}
}
else if (row.length === 5) {
cipher.login.uri = fixUri(row[1]);
cipher.login.uris = makeUriArray(row[1]);
cipher.login.username = row[2];
cipher.login.password = row[3];
cipher.notes = row[4];
@@ -2004,7 +2039,7 @@
cipher.notes = row[4] + '\n' + row[5];
}
cipher.login.uri = fixUri(row[1]);
cipher.login.uris = makeUriArray(row[1]);
}
else if (row.length === 7) {
if (row[2] === '') {
@@ -2016,7 +2051,7 @@
cipher.notes = row[3] + '\n' + row[4] + '\n' + row[6];
}
cipher.login.uri = fixUri(row[1]);
cipher.login.uris = makeUriArray(row[1]);
cipher.login.password = row[5];
}
else {
@@ -2043,9 +2078,6 @@
if (cipher.notes === '') {
cipher.notes = null;
}
if (cipher.login.uri === '') {
cipher.login.uri = null;
}
ciphers.push(cipher);
}
@@ -2138,7 +2170,7 @@
notes: notesText && notesText !== '' ? notesText : null,
name: titleText && titleText !== '' ? titleText : '--',
login: {
uri: linkText && linkText !== '' ? trimUri(linkText) : null,
uris: makeUriArray(linkText),
username: usernameText && usernameText !== '' ? usernameText : null,
password: passwordText && passwordText !== '' ? passwordText : null
}
@@ -2204,14 +2236,14 @@
notes: '',
name: value[2] && value[2] !== '' ? value[2] : null,
login: {
uri: null,
uris: null,
username: null,
password: null
}
};
if (value[1] === 'Web Logins') {
cipher.login.uri = value[4] && value[4] !== '' ? trimUri(value[4]) : null;
cipher.login.uris = makeUriArray(value[4]);
cipher.login.username = value[5] && value[5] !== '' ? value[5] : null;
cipher.login.password = value[6] && value[6] !== '' ? value[6] : null;
cipher.notes = value[3] && value[3] !== '' ? value[3].split('\\n').join('\n') : null;
@@ -2289,7 +2321,7 @@
notes: value.memo && value.memo !== '' ? value.memo : null,
name: value.name && value.name !== '' ? value.name : '--',
login: {
uri: value.url && value.url !== '' ? trimUri(value.url) : null,
uris: makeUriArray(value.url),
username: value.login && value.login !== '' ? value.login : null,
password: value.password && value.password !== '' ? value.password : null
},
@@ -2361,7 +2393,7 @@
notes: '',
name: entry.label && entry.label !== '' ? entry.label.split(' ')[0] : '--',
login: {
uri: null,
uris: null,
username: null,
password: null
},
@@ -2389,7 +2421,7 @@
cipher.login.username = field.value;
break;
case 'url':
cipher.login.uri = trimUri(field.value);
cipher.login.uris = makeUriArray(field.value);
break;
default:
if (!cipher.login.username && isField(field.label, _usernameFieldNames)) {
@@ -2452,7 +2484,7 @@
notes: null,
name: account.label && account.label !== '' ? account.label : account.domain,
login: {
uri: account.domain && account.domain !== '' ? fixUri(account.domain) : null,
uris: makeUriArray(account.domain),
username: account.username && account.username !== '' ? account.username : null,
password: account.password && account.password !== '' ? account.password : null
}
@@ -2498,7 +2530,7 @@
notes: '',
name: outterTable.find('span.caption').text(),
login: {
uri: null,
uris: null,
username: null,
password: null
},
@@ -2507,7 +2539,7 @@
var url = outterTable.find('.subcaption').text();
if (url && url !== '') {
cipher.login.uri = fixUri(url);
cipher.login.uris = makeUriArray(url);
}
var fields = [];
@@ -2591,7 +2623,7 @@
notes: value.notes && value.notes !== '' ? value.notes : null,
name: value.url && value.url !== '' ? urlDomain(value.url) : '--',
login: {
uri: value.url && value.url !== '' ? trimUri(value.url) : null,
uris: makeUriArray(value.url),
username: value.username && value.username !== '' ? value.username : null,
password: value.password && value.password !== '' ? value.password : null
}
@@ -2625,7 +2657,7 @@
favorite: false,
notes: note && note !== '' ? note : null,
login: {
uri: null,
uris: null,
password: null,
username: null
},
@@ -2642,8 +2674,8 @@
var fieldLower = field.toLowerCase();
if (!cipher.login.uri && isField(field, _uriFieldNames)) {
cipher.login.uri = fixUri(value);
if (!cipher.login.uris && isField(field, _uriFieldNames)) {
cipher.login.uris = makeUriArray(value);
}
else if (!cipher.login.username && isField(field, _usernameFieldNames)) {
cipher.login.username = value;
@@ -2700,7 +2732,7 @@
notes: '',
name: item.name && item.name !== '' ? item.name : '--',
login: {
uri: item.login_url && item.login_url !== '' ? fixUri(item.login_url) : null,
uris: makeUriArray(item.login_url),
username: null,
password: null
},
@@ -2844,7 +2876,7 @@
notes: value.Notes && value.Notes !== '' ? value.Notes : '',
name: value['Secret Name'] && value['Secret Name'] !== '' ? value['Secret Name'] : '--',
login: {
uri: value['Secret URL'] && value['Secret URL'] !== '' ? fixUri(value['Secret URL']) : null,
uris: makeUriArray(value['Secret URL']),
username: null,
password: null
},
@@ -2944,14 +2976,14 @@
name: value[1] && value[1] !== '' ? value[1] : '--',
fields: null,
login: {
uri: null,
uris: null,
username: null,
password: null
}
};
if (type === 'Web Logins' || type === 'Servers' || type === 'Email Accounts') {
cipher.login.uri = value[4] && value[4] !== '' ? fixUri(value[4]) : null;
cipher.login.uris = makeUriArray(value[4]);
cipher.login.username = value[2] && value[2] !== '' ? value[2] : null;
cipher.login.password = value[3] && value[3] !== '' ? value[3] : null;
parseFieldsToNotes(5, value, cipher);
@@ -3009,7 +3041,7 @@
favorite: false,
notes: row.Notes && row.Notes !== '' ? row.Notes : null,
login: {
uri: row.Url && row.Url !== '' ? fixUri(row.Url) : null,
uris: makeUriArray(row.Url),
password: row.Password && row.Password !== '' ? row.Password : null,
username: row.UserName && row.UserName !== '' ? row.UserName : null
}
@@ -3066,7 +3098,7 @@
notes: !!getValue('description', value) ? getValue('description', value) : null,
name: !!getValue('title', value) ? getValue('title', value) : '--',
login: {
uri: !!getValue('site', value) ? fixUri(getValue('site', value)) : null,
uris: !!getValue('site', value) ? makeUriArray(getValue('site', value)) : null,
username: !!getValue('username', value) ? getValue('username', value) : null,
password: !!getValue('password', value) ? getValue('password', value) : null
}
@@ -3140,7 +3172,7 @@
notes: '',
name: item.display_name.replace('http://', '').replace('https://', ''),
login: {
uri: fixUri(item.display_name),
uris: makeUriArray(item.display_name),
username: item.attributes.username_value && item.attributes.username_value !== '' ?
item.attributes.username_value : null,
password: item.secret && item.secret !== '' ? item.secret : null

View File

@@ -1,2 +1,2 @@
angular.module("bit")
.constant("appSettings", {"apiUri":"/api","identityUri":"/identity","iconsUri":"https://icons.bitwarden.com","stripeKey":"pk_live_bpN0P37nMxrMQkcaHXtAybJk","braintreeKey":"production_qfbsv8kc_njj2zjtyngtjmbjd","selfHosted":false,"version":"1.22.0","environment":"Production"});
.constant("appSettings", {"apiUri":"/api","identityUri":"/identity","iconsUri":"https://icons.bitwarden.com","stripeKey":"pk_live_bpN0P37nMxrMQkcaHXtAybJk","braintreeKey":"production_qfbsv8kc_njj2zjtyngtjmbjd","selfHosted":false,"version":"1.22.1","environment":"Production"});

View File

@@ -4,7 +4,7 @@
.controller('settingsTwoStepAuthenticatorController', function ($scope, apiService, $uibModalInstance, cryptoService,
authService, $q, toastr, $analytics, constants, $timeout) {
$analytics.eventTrack('settingsTwoStepAuthenticatorController', { category: 'Modal' });
var _issuer = 'bitwarden',
var _issuer = 'Bitwarden',
_profile = null,
_masterPasswordHash,
_key = null;

View File

@@ -28,7 +28,7 @@
$analytics.eventTrack('Print Recovery Code');
var w = window.open();
w.document.write('<div style="font-size: 18px; text-align: center;"><p>bitwarden two-step login recovery code:</p>' +
w.document.write('<div style="font-size: 18px; text-align: center;"><p>Bitwarden two-step login recovery code:</p>' +
'<code style="font-family: Menlo, Monaco, Consolas, \'Courier New\', monospace;">' + $scope.code + '</code>' +
'</div><p style="text-align: center;">' + new Date() + '</p>');
w.print();

View File

@@ -23,7 +23,7 @@
}, function (e) {
throw e ? e : 'Error occurred.';
}).then(function () {
toastr.success('Please log back in. If you are using other bitwarden applications, ' +
toastr.success('Please log back in. If you are using other Bitwarden applications, ' +
'log out and back in to those as well.', 'Key Updated', { timeOut: 10000 });
});
};

View File

@@ -96,7 +96,7 @@
</div>
<div class="box-body">
<p>
You membership has a total of {{storage.maxGb}} GB of encrypted file storage.
Your 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;">

View File

@@ -4,7 +4,7 @@
<section class="content">
<p>
If you have the same login across multiple different website domains, you can mark the website as "equivalent".
"Global" domains are ones already created for you by bitwarden.
"Global" domains are ones already created for you by Bitwarden.
</p>
<form name="customForm" ng-submit="customForm.$valid && saveCustom()" api-form="customPromise" autocomplete="off">
<div class="box box-default">

View File

@@ -8,7 +8,7 @@
</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
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">

View File

@@ -48,7 +48,7 @@
<li ng-repeat="e in submitTwoStepForm.$errors">{{e}}</li>
</ul>
</div>
<p>Enter the bitwarden application information from your Duo Admin panel:</p>
<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"
@@ -57,7 +57,7 @@
<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 />
required api-field autocomplete="new-password" />
</div>
<div class="form-group" show-errors>
<label for="host">API Hostname</label>

View File

@@ -33,7 +33,7 @@
<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
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>

View File

@@ -33,7 +33,7 @@
<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
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>
@@ -77,7 +77,7 @@
{{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" />
ng-show="!updateModel.key1.existingKey" autocomplete="new-password" />
</div>
<div class="form-group" show-errors>
<label for="key2">YubiKey #2</label>
@@ -88,7 +88,7 @@
{{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" />
ng-show="!updateModel.key2.existingKey" autocomplete="new-password" />
</div>
<div class="form-group" show-errors>
<label for="key3">YubiKey #3</label>
@@ -99,7 +99,7 @@
{{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" />
ng-show="!updateModel.key3.existingKey" autocomplete="new-password" />
</div>
<strong>NFC Support</strong>
<div class="checkbox">

View File

@@ -21,7 +21,7 @@
<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
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.

View File

@@ -53,8 +53,9 @@
login_totp: null
};
var j;
if (decCiphers[i].fields) {
for (var j = 0; j < decCiphers[i].fields.length; j++) {
for (j = 0; j < decCiphers[i].fields.length; j++) {
if (!cipher.fields) {
cipher.fields = '';
}
@@ -69,10 +70,16 @@
switch (decCiphers[i].type) {
case constants.cipherType.login:
cipher.type = 'login';
cipher.login_uri = decCiphers[i].login.uri;
cipher.login_username = decCiphers[i].login.username;
cipher.login_password = decCiphers[i].login.password;
cipher.login_totp = decCiphers[i].login.totp;
if (decCiphers[i].login.uris && decCiphers[i].login.uris.length) {
cipher.login_uri = [];
for (j = 0; j < decCiphers[i].login.uris.length; j++) {
cipher.login_uri.push(decCiphers[i].login.uris[j].uri);
}
}
break;
case constants.cipherType.secureNote:
cipher.type = 'note';

View File

@@ -11,7 +11,7 @@
$scope.options = [
{
id: 'bitwardencsv',
name: 'bitwarden (csv)',
name: 'Bitwarden (csv)',
featured: true,
sort: 1,
instructions: $sce.trustAsHtml('Export using the web vault (vault.bitwarden.com). ' +
@@ -243,7 +243,7 @@
'python script by Luke Plant to your desktop as <code>pw_helper.py</code>. Open terminal and run ' +
'<code>chmod +rx Desktop/pw_helper.py</code> and then ' +
'<code>python Desktop/pw_helper.py export Desktop/my_passwords.json</code>. Then upload ' +
'the resulting <code>my_passwords.json</code> file here to bitwarden.')
'the resulting <code>my_passwords.json</code> file here to Bitwarden.')
}
];

View File

@@ -11,7 +11,13 @@
folderId: selectedFolder ? selectedFolder.id : null,
favorite: checkedFavorite === true,
type: constants.cipherType.login,
login: {},
login: {
uris: [{
uri: null,
match: null,
matchValue: null
}]
},
identity: {},
card: {},
secureNote: {
@@ -44,6 +50,42 @@
}
};
$scope.addUri = function () {
if (!$scope.cipher.login) {
return;
}
if (!$scope.cipher.login.uris) {
$scope.cipher.login.uris = [];
}
$scope.cipher.login.uris.push({
uri: null,
match: null,
matchValue: null
});
};
$scope.removeUri = function (uri) {
if (!$scope.cipher.login || !$scope.cipher.login.uris) {
return;
}
var index = $scope.cipher.login.uris.indexOf(uri);
if (index > -1) {
$scope.cipher.login.uris.splice(index, 1);
}
};
$scope.uriMatchChanged = function (uri) {
if ((!uri.matchValue && uri.matchValue !== 0) || uri.matchValue === '') {
uri.match = null;
}
else {
uri.match = parseInt(uri.matchValue);
}
};
$scope.addField = function () {
if (!$scope.cipher.fields) {
$scope.cipher.fields = [];

View File

@@ -205,6 +205,8 @@
if (returnVal.action === 'edit') {
var index = $scope.ciphers.indexOf(cipher);
if (index > -1) {
// restore collection ids since those cannot change on edit here.
returnVal.data.collectionIds = $rootScope.vaultCiphers[index].collectionIds;
$rootScope.vaultCiphers[index] = returnVal.data;
}
sortScopedCipherData();

View File

@@ -16,6 +16,7 @@
$scope.cipher = cipherService.decryptCipher(cipher);
$scope.readOnly = !$scope.cipher.edit;
$scope.useTotp = $scope.useTotp || $scope.cipher.organizationUseTotp;
setUriMatchValues();
});
$scope.save = function (model) {
@@ -55,6 +56,42 @@
}
};
$scope.addUri = function () {
if (!$scope.cipher.login) {
return;
}
if (!$scope.cipher.login.uris) {
$scope.cipher.login.uris = [];
}
$scope.cipher.login.uris.push({
uri: null,
match: null,
matchValue: null
});
};
$scope.removeUri = function (uri) {
if (!$scope.cipher.login || !$scope.cipher.login.uris) {
return;
}
var index = $scope.cipher.login.uris.indexOf(uri);
if (index > -1) {
$scope.cipher.login.uris.splice(index, 1);
}
};
$scope.uriMatchChanged = function (uri) {
if ((!uri.matchValue && uri.matchValue !== 0) || uri.matchValue === '') {
uri.match = null;
}
else {
uri.match = parseInt(uri.matchValue);
}
};
$scope.addField = function () {
if (!$scope.cipher.fields) {
$scope.cipher.fields = [];
@@ -130,4 +167,14 @@
controller: 'premiumRequiredController'
});
};
function setUriMatchValues() {
if ($scope.cipher.login && $scope.cipher.login.uris) {
for (var i = 0; i < $scope.cipher.login.uris.length; i++) {
$scope.cipher.login.uris[i].matchValue =
$scope.cipher.login.uris[i].match || $scope.cipher.login.uris[i].match === 0 ?
$scope.cipher.login.uris[i].match.toString() : '';
}
}
}
});

View File

@@ -39,26 +39,7 @@
</div>
</div>
</div>
<div ng-if="cipher.type === constants.cipherType.login">
<div class="form-group" show-errors>
<label for="uri">URI</label>
<div class="input-group">
<input type="text" id="uri" name="Login.Uri" ng-model="cipher.login.uri" class="form-control"
placeholder="http://..." ng-readonly="readOnly" api-field />
<span class="input-group-btn">
<button class="btn btn-default btn-flat" type="button" uib-tooltip="Copy URI"
tooltip-placement="left" ngclipboard ngclipboard-error="clipboardError(e)"
data-clipboard-target="#uri">
<i class="fa fa-clipboard"></i>
</button>
<a href="{{cipher.login.uri}}" target="_blank" class="btn btn-default btn-flat"
uib-tooltip="Go To Website" tooltip-placement="left">
<i class="fa fa-share"></i>
</a>
</span>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group" show-errors>
@@ -87,7 +68,8 @@
<label for="password">Password</label>
<div class="input-group">
<input type="password" id="password" name="Login.Password" ng-model="cipher.login.password"
class="form-control monospaced" ng-readonly="readOnly" api-field />
class="form-control monospaced" ng-readonly="readOnly" api-field
autocomplete="new-password" />
<span class="input-group-btn" uib-tooltip="Copy Password" tooltip-placement="left">
<button class="btn btn-default btn-flat" type="button" ngclipboard
ngclipboard-success="clipboardSuccess(e)" ngclipboard-error="clipboardError(e, true)"
@@ -119,6 +101,58 @@
</div>
</div>
</div>
<div ng-repeat="u in cipher.login.uris" ng-if="cipher.login.uris && cipher.login.uris.length">
<div class="row">
<div class="col-sm-7">
<div class="form-group" show-errors>
<label for="uri{{$index}}">URI {{$index + 1}}</label>
<div class="input-group">
<input type="text" id="uri{{$index}}" name="Login.Uris[{{$index}}].Uri"
ng-model="u.uri" class="form-control"
placeholder="http://..." ng-readonly="readOnly" api-field />
<span class="input-group-btn">
<button class="btn btn-default btn-flat" type="button" uib-tooltip="Copy URI"
tooltip-placement="left" ngclipboard ngclipboard-error="clipboardError(e)"
data-clipboard-target="#uri{{$index}}">
<i class="fa fa-clipboard"></i>
</button>
<a href="{{u.uri}}" target="_blank" class="btn btn-default btn-flat"
uib-tooltip="Go To Website" tooltip-placement="left">
<i class="fa fa-share"></i>
</a>
</span>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="form-group">
<label for="uri_match_{{$index}}">Auto-fill Detection</label>
<select id="uri_match_{{$index}}" name="Login.Uris[{{$index}}].Match"
class="form-control" ng-model="u.matchValue" ng-change="uriMatchChanged(u)">
<option value="">Default</option>
<option value="0">Base domain</option>
<option value="1">Host</option>
<option value="2">Starts with</option>
<option value="4">Regular Expression</option>
<option value="3">Exact</option>
<option value="5">Never</option>
</select>
</div>
</div>
<div class="col-sm-1">
<br class="hidden-xs" />
<a href="#" ng-click="removeUri(u)" stop-click>
<i class="fa fa-window-close-o fa-lg"></i>
<span class="visible-xs-inline">Remove URI</span>
</a>
</div>
</div>
<hr class="visible-xs-block" />
</div>
<a href="#" ng-click="addUri()" stop-click>
<i class="fa fa-plus-circle"></i> New URI
</a>
<br /><br />
</div>
<div ng-if="cipher.type === constants.cipherType.card">
<div class="row">
@@ -504,7 +538,6 @@
<div ng-if="cipher.type === constants.cipherType.secureNote">
<!-- Nothing for now -->
</div>
<div class="form-group" show-errors>
<label for="notes">Notes</label>
<textarea id="notes" name="Notes" class="form-control" ng-model="cipher.notes" api-field

View File

@@ -33,24 +33,6 @@
</div>
<div ng-if="cipher.type === constants.cipherType.login">
<div class="form-group" show-errors>
<label for="uri">URI</label>
<div class="input-group">
<input type="text" id="uri" name="Login.Uri" ng-model="cipher.login.uri" class="form-control"
placeholder="http://..." ng-readonly="readOnly" api-field />
<span class="input-group-btn">
<button class="btn btn-default btn-flat" type="button" uib-tooltip="Copy URI"
tooltip-placement="left" ngclipboard ngclipboard-error="clipboardError(e)"
data-clipboard-target="#uri">
<i class="fa fa-clipboard"></i>
</button>
<a href="{{cipher.login.uri}}" target="_blank" class="btn btn-default btn-flat"
uib-tooltip="Go To Website" tooltip-placement="left">
<i class="fa fa-share"></i>
</a>
</span>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group" show-errors>
@@ -79,7 +61,8 @@
<label for="password">Password</label>
<div class="input-group">
<input type="password" id="password" name="Login.Password" ng-model="cipher.login.password"
class="form-control monospaced" ng-readonly="readOnly" api-field />
class="form-control monospaced" ng-readonly="readOnly" api-field
autocomplete="new-password" />
<span class="input-group-btn" uib-tooltip="Copy Password" tooltip-placement="left">
<button class="btn btn-default btn-flat" type="button" ngclipboard
ngclipboard-success="clipboardSuccess(e)" ngclipboard-error="clipboardError(e, true)"
@@ -111,6 +94,60 @@
</div>
</div>
</div>
<div ng-repeat="u in cipher.login.uris" ng-if="cipher.login.uris && cipher.login.uris.length">
<div class="row">
<div class="col-sm-7">
<div class="form-group" show-errors>
<label for="uri{{$index}}">URI {{$index + 1}}</label>
<div class="input-group">
<input type="text" id="uri{{$index}}" name="Login.Uris[{{$index}}].Uri"
ng-model="u.uri" class="form-control"
placeholder="http://..." ng-readonly="readOnly" api-field />
<span class="input-group-btn">
<button class="btn btn-default btn-flat" type="button" uib-tooltip="Copy URI"
tooltip-placement="left" ngclipboard ngclipboard-error="clipboardError(e)"
data-clipboard-target="#uri{{$index}}">
<i class="fa fa-clipboard"></i>
</button>
<a href="{{u.uri}}" target="_blank" class="btn btn-default btn-flat"
uib-tooltip="Go To Website" tooltip-placement="left">
<i class="fa fa-share"></i>
</a>
</span>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="form-group">
<label for="uri_match_{{$index}}">Auto-fill Detection</label>
<select id="uri_match_{{$index}}" name="Login.Uris[{{$index}}].Match" ng-disabled="readOnly"
class="form-control" ng-model="u.matchValue" ng-change="uriMatchChanged(u)">
<option value="">Default</option>
<option value="0">Base domain</option>
<option value="1">Host</option>
<option value="2">Starts with</option>
<option value="4">Regular Expression</option>
<option value="3">Exact</option>
<option value="5">Never</option>
</select>
</div>
</div>
<div class="col-sm-1" ng-if="!readOnly">
<br class="hidden-xs" />
<a href="#" ng-click="removeUri(u)" stop-click>
<i class="fa fa-window-close-o fa-lg"></i>
<span class="visible-xs-inline">Remove URI</span>
</a>
</div>
</div>
<hr class="visible-xs-block" />
</div>
<div ng-if="!readOnly">
<a href="#" ng-click="addUri()" stop-click>
<i class="fa fa-plus-circle"></i> New URI
</a>
<br /><br />
</div>
</div>
<div ng-if="cipher.type === constants.cipherType.card">
<div class="row">
@@ -551,9 +588,9 @@
</div>
</div>
</div>
<div class="col-sm-1">
<div class="col-sm-1" ng-if="!readOnly">
<br class="hidden-xs" />
<a href="#" ng-click="removeField(field)" stop-click ng-if="!readOnly">
<a href="#" ng-click="removeField(field)" stop-click>
<i class="fa fa-window-close-o fa-lg"></i>
<span class="visible-xs-inline">Remove Custom Field</span>
</a>

View File

@@ -14,7 +14,7 @@
<p ng-show="loading">Loading...</p>
<div ng-show="!loading && !organizations.length" class="callout callout-default">
<h4><i class="fa fa-info-circle"></i> No Organizations</h4>
<p>You do not belong to any organizations. Organizations allow you to share items with other bitwarden users.</p>
<p>You do not belong to any organizations. Organizations allow you to share items with other Bitwarden users.</p>
<a ng-click="createOrg()" class="btn btn-default btn-flat">
Create an Organization
</a>

View File

@@ -1,135 +0,0 @@
<section class="content-header">
<h1>
Apps
<small>for all of your devices</small>
</h1>
</section>
<section class="content">
<div class="box box-default box-apps">
<div class="box-header with-border">
<h3 class="box-title">Desktop/Browser</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-sm-6">
<ul class="fa-ul">
<li>
<a href="https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb" target="_blank">
<i class="fa fa-chrome fa-lg fa-fw fa-li"></i> Google Chrome
</a>
</li>
<li>
<a href="https://addons.mozilla.org/firefox/addon/bitwarden-password-manager/" target="_blank">
<i class="fa fa-firefox fa-lg fa-fw fa-li"></i> Mozilla Firefox
</a>
</li>
<li>
<a href="https://addons.opera.com/extensions/details/bitwarden-free-password-manager/" target="_blank">
<i class="fa fa-opera fa-lg fa-fw fa-li"></i> Opera
</a>
</li>
<li>
<a href="https://www.microsoft.com/store/p/bitwarden-free-password-manager/9p6kxl0svnnl" target="_blank">
<i class="fa fa-edge fa-lg fa-fw fa-li"></i> Microsoft Edge
</a>
</li>
</ul>
</div>
<div class="col-sm-6">
Others:
<ul>
<li>
<a href="https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb" target="_blank">
Vivaldi
</a>
</li>
<li>
<a href="https://brave.com/" target="_blank">
Brave
</a>
</li>
<li>
<a href="https://addons.mozilla.org/firefox/addon/bitwarden-password-manager/" target="_blank">
Tor Browser
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="box box-default box-apps">
<div class="box-header with-border">
<h3 class="box-title">Mobile</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-sm-6">
<ul class="fa-ul">
<li>
<a href="https://itunes.apple.com/app/bitwarden-free-password-manager/id1137397744?mt=8" target="_blank">
<i class="fa fa-apple fa-lg fa-fw fa-li"></i> iOS
</a>
</li>
<li>
<a href="https://play.google.com/store/apps/details?id=com.x8bit.bitwarden" target="_blank">
<i class="fa fa-android fa-lg fa-fw fa-li"></i> Android
</a>
</li>
</ul>
</div>
<div class="col-sm-6">
<ul class="fa-ul">
<li>
<a href="#" stop-click>
<i class="fa fa-windows fa-lg fa-fw fa-li"></i> Windows
<small class="text-muted">(coming soon)</small>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="box box-default box-apps">
<div class="box-header with-border">
<h3 class="box-title">Other</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-sm-6">
<ul class="fa-ul">
<li>
<a href="#" stop-click>
<i class="fa fa-windows fa-lg fa-fw fa-li"></i> Desktop Windows
<small class="text-muted">(coming soon)</small>
</a>
</li>
<li>
<a href="#" stop-click>
<i class="fa fa-apple fa-lg fa-fw fa-li"></i> Desktop macOS
<small class="text-muted">(coming soon)</small>
</a>
</li>
</ul>
</div>
<div class="col-sm-6">
<ul class="fa-ul">
<li>
<a href="#" stop-click>
<i class="fa fa-linux fa-lg fa-fw fa-li"></i> Desktop Linux
<small class="text-muted">(coming soon)</small>
</a>
</li>
<li>
<a href="#" stop-click>
<i class="fa fa-terminal fa-lg fa-fw fa-li"></i> CLI
<small class="text-muted">(coming soon)</small>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</section>

View File

@@ -104,7 +104,7 @@
</ul>
</li>
<li ng-class="{active: $state.is('backend.user.apps')}">
<a ui-sref="backend.user.apps">
<a href="https://bitwarden.com/#download" target="_blank">
<small class="label pull-right bg-green">FREE</small>
<i class="fa fa-download fa-fw"></i> <span>Get the Apps</span>
</a>

View File

@@ -26,47 +26,6 @@
</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>
<script src="js/duo-connector.js"></script>
</body>
</html>

View File

@@ -31,7 +31,7 @@
<meta name="theme-color" content="#3c8dbc">
<base href="/" />
<title page-title>bitwarden Web Vault</title>
<title page-title>Bitwarden Web Vault</title>
<!-- @if !selfHosted -->
<script src="https://js.stripe.com/v2/"></script>
@@ -67,12 +67,12 @@
<div ui-view></div>
<!-- @if !selfHosted !>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"
integrity="sha384-rY/jv8mMhqDabXSo+UCggqKtdmBfd3qC2/KvyTDNQ6PcUJXaxK1tMepoQda4g5vB" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"
integrity="sha384-xBuQ/xzmlsLoJpyjoggmTEz8OWUFM0/RC5BsqQBDX2v5cMvDHcMakNTNrHIW2I5f" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
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="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular.min.js"
integrity="sha384-R6kAKgTgRiD5889XyzYD/aMryNA4Yr9EBnt6rIXuukLgVONifQDnHNaadrSNakQl" crossorigin="anonymous"></script>
<!-- @endif -->
<!-- @if true !>
<script src="js/fallback-scripts.min.js?v=<!-- @echo cacheTag !>"></script>

40
src/js/duo-connector.js Normal file
View File

@@ -0,0 +1,40 @@
!(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) {
}
}

View File

@@ -735,7 +735,7 @@ h1, h2, h3, h4, h5, h6 {
.totp-col {
margin: -10px 0 10px 0;
@media (min-width: @screen-md) {
@media (min-width: @screen-sm) {
padding-top: 26px;
margin: 0;
}
@@ -815,9 +815,9 @@ h1, h2, h3, h4, h5, h6 {
textarea {
&#notes {
height: 100px;
}
&.big-textarea {
height: 200px !important;
&.big-textarea {
height: 200px;
}
}
}