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

Compare commits

..

11 Commits

Author SHA1 Message Date
Kyle Spearrin
1b169f368b version bump to 1.1.1 2016-10-13 20:06:55 -04:00
Kyle Spearrin
3375bda789 cache tag for local assets on preprocess build 2016-10-13 19:57:56 -04:00
Kyle Spearrin
6569fbe6aa adjust importers to not require Uri and Passwords 2016-10-13 19:05:53 -04:00
Kyle Spearrin
1d2b82a302 lint fixes for import service. made passwird not required for sites 2016-10-13 18:40:42 -04:00
Kyle Spearrin
004ddb1e75 handle lastpass import when users try to upload html file instead of csv 2016-10-13 00:05:18 -04:00
Kyle Spearrin
400826baf7 minor version bump to 1.1.0 2016-10-12 22:51:59 -04:00
Kyle Spearrin
96ae20d81d fix floating labels for firefox 2016-10-12 22:50:46 -04:00
Kyle Spearrin
ef5f4df30f lint errors in import service 2016-10-12 22:49:04 -04:00
Kyle Spearrin
2ce1b12f6e Handle empty password and uri fields when encrypting/decrypting 2016-10-12 22:26:28 -04:00
Kyle Spearrin
85efba92e6 added importer for keypass 2.x xml 2016-10-12 22:14:39 -04:00
Kyle Spearrin
d17211ff86 added SafeInCloud csv import 2016-10-12 19:02:36 -04:00
12 changed files with 261 additions and 76 deletions

View File

@@ -30,6 +30,8 @@ paths.lessDir = 'less/';
paths.cssDir = paths.webroot + 'css/';
paths.jsDir = paths.webroot + 'js/';
var randomString = Math.random().toString(36).substring(7);
gulp.task('lint', function () {
return gulp.src(paths.webroot + 'app/**/*.js')
.pipe(jshint())
@@ -261,7 +263,7 @@ gulp.task('dist:css', function () {
paths.cssDir + '**/*.css',
'!' + paths.cssDir + '**/*.min.css'
])
.pipe(preprocess({ context: settings }))
.pipe(preprocess({ context: { cacheTag: randomString } }))
.pipe(cssmin())
.pipe(rename({ suffix: '.min' }))
.pipe(gulp.dest(paths.dist + 'css'));
@@ -277,7 +279,7 @@ gulp.task('dist:js:app', function () {
]);
merge(mainStream, config())
.pipe(preprocess({ context: settings }))
.pipe(preprocess({ context: { cacheTag: randomString } }))
.pipe(concat(paths.dist + '/js/app.min.js'))
.pipe(ngAnnotate())
.pipe(uglify())
@@ -306,7 +308,7 @@ gulp.task('dist:preprocess', function () {
.src([
paths.dist + '/**/*.html'
], { base: '.' })
.pipe(preprocess({ context: settings }))
.pipe(preprocess({ context: { cacheTag: randomString }}))
.pipe(gulp.dest('.'));
});

View File

@@ -1,5 +1,5 @@
{
"version": "1.0.1",
"version": "1.1.1",
"environment": "Development",
"dependencies": {

View File

@@ -56,7 +56,9 @@
/// <reference path="lib/angular-md5/angular-md5.js" />
/// <reference path="lib/angular-messages/angular-messages.js" />
/// <reference path="lib/angular-resource/angular-resource.js" />
/// <reference path="lib/angular-toastr/angular-toastr.js" />
/// <reference path="lib/angulartics/angulartics.js" />
/// <reference path="lib/angulartics/angulartics-ga.js" />
/// <reference path="lib/angular-toastr/angular-toastr.min.js" />
/// <reference path="lib/angular-toastr/angular-toastr.tpls.js" />
/// <reference path="lib/angular-ui-router/angular-ui-router.js" />
/// <reference path="lib/bootstrap/js/bootstrap.min.js" />
@@ -67,4 +69,3 @@
/// <reference path="lib/papaparse/papaparse.js" />
/// <reference path="lib/sjcl/bitArray.js" />
/// <reference path="lib/sjcl/cbc.js" />
/// <reference path="lib/sjcl/sjcl.js" />

View File

@@ -24,9 +24,9 @@ angular
folderId: encryptedSite.FolderId,
favorite: encryptedSite.Favorite,
name: cryptoService.decrypt(encryptedSite.Name),
uri: cryptoService.decrypt(encryptedSite.Uri),
uri: encryptedSite.Uri && encryptedSite.Uri !== '' ? cryptoService.decrypt(encryptedSite.Uri) : null,
username: encryptedSite.Username && encryptedSite.Username !== '' ? cryptoService.decrypt(encryptedSite.Username) : null,
password: cryptoService.decrypt(encryptedSite.Password),
password: encryptedSite.Password && encryptedSite.Password !== '' ? cryptoService.decrypt(encryptedSite.Password) : null,
notes: encryptedSite.Notes && encryptedSite.Notes !== '' ? cryptoService.decrypt(encryptedSite.Notes) : null
};
@@ -79,10 +79,10 @@ angular
'type': 1,
folderId: unencryptedSite.folderId === '' ? null : unencryptedSite.folderId,
favorite: unencryptedSite.favorite !== null ? unencryptedSite.favorite : false,
uri: cryptoService.encrypt(unencryptedSite.uri, key),
uri: !unencryptedSite.uri || unencryptedSite.uri === '' ? null : cryptoService.encrypt(unencryptedSite.uri, key),
name: cryptoService.encrypt(unencryptedSite.name, key),
username: !unencryptedSite.username || unencryptedSite.username === '' ? null : cryptoService.encrypt(unencryptedSite.username, key),
password: cryptoService.encrypt(unencryptedSite.password, key),
password: !unencryptedSite.password || unencryptedSite.password === '' ? null : cryptoService.encrypt(unencryptedSite.password, key),
notes: !unencryptedSite.notes || unencryptedSite.notes === '' ? null : cryptoService.encrypt(unencryptedSite.notes, key)
};
};

View File

@@ -12,6 +12,12 @@
case 'lastpass':
importLastPass(file, success, error);
break;
case 'safeincloudcsv':
importSafeInCloudCsv(file, success, error);
break;
case 'keypassxml':
importKeyPassXml(file, success, error);
break;
default:
error();
break;
@@ -27,10 +33,6 @@
folderRelationships = [];
angular.forEach(results.data, function (value, key) {
if (!value.uri || value.uri === '') {
return;
}
var folderIndex = folders.length,
siteIndex = sites.length,
hasFolder = value.folder && value.folder !== '',
@@ -38,7 +40,7 @@
if (hasFolder) {
for (var i = 0; i < folders.length; i++) {
if (folders[i].name == value.folder) {
if (folders[i].name === value.folder) {
addFolder = false;
folderIndex = i;
break;
@@ -48,11 +50,11 @@
sites.push({
favorite: value.favorite !== null ? value.favorite : false,
uri: value.uri,
uri: value.uri && value.uri !== '' ? value.uri : null,
username: value.username && value.username !== '' ? value.username : null,
password: value.password,
password: value.password && value.password !== '' ? value.password : null,
notes: value.notes && value.notes !== '' ? value.notes : null,
name: value.name
name: value.name && value.name !== '' ? value.name : '--',
});
if (addFolder) {
@@ -76,26 +78,64 @@
}
function importLastPass(file, success, error) {
if (file.type === 'text/html') {
var reader = new FileReader();
reader.readAsText(file, 'utf-8');
reader.onload = function (evt) {
var doc = $(evt.target.result);
var pre = doc.find('pre');
var csv, results;
if (pre.length === 1) {
csv = pre.text().trim();
results = Papa.parse(csv, { header: true });
parseData(results.data);
}
else {
var foundPre = false;
for (var i = 0; i < doc.length; i++) {
if (doc[i].tagName === 'PRE') {
foundPre = true;
csv = doc[i].outerText.trim();
results = Papa.parse(csv, { header: true });
parseData(results.data);
break;
}
}
if (!foundPre) {
error();
}
}
};
reader.onerror = function (evt) {
error();
};
}
else {
Papa.parse(file, {
header: true,
complete: function (results) {
parseData(results.data);
}
});
}
function parseData(data) {
var folders = [],
sites = [],
siteRelationships = [];
angular.forEach(results.data, function (value, key) {
if (!value.url || value.url === '') {
return;
}
angular.forEach(data, function (value, key) {
var folderIndex = folders.length,
siteIndex = sites.length,
hasFolder = value.grouping && value.grouping !== '' && value.grouping != '(none)',
hasFolder = value.grouping && value.grouping !== '' && value.grouping !== '(none)',
addFolder = hasFolder;
if (hasFolder) {
for (var i = 0; i < folders.length; i++) {
if (folders[i].name == value.grouping) {
if (folders[i].name === value.grouping) {
addFolder = false;
folderIndex = i;
break;
@@ -104,12 +144,12 @@
}
sites.push({
favorite: value.fav == '1',
uri: value.url,
favorite: value.fav === '1',
uri: value.url && value.url !== '' ? value.url : null,
username: value.username && value.username !== '' ? value.username : null,
password: value.password,
password: value.password && value.password !== '' ? value.password : null,
notes: value.extra && value.extra !== '' ? value.extra : null,
name: value.name
name: value.name && value.name !== '' ? value.name : '--',
});
if (addFolder) {
@@ -129,7 +169,145 @@
success(folders, sites, siteRelationships);
}
}
function importSafeInCloudCsv(file, success, error) {
Papa.parse(file, {
header: true,
complete: function (results) {
var folders = [],
sites = [],
siteRelationships = [];
angular.forEach(results.data, function (value, key) {
sites.push({
favorite: false,
uri: value.URL && value.URL !== '' ? value.URL : null,
username: value.Login && value.Login !== '' ? value.Login : null,
password: value.Password && value.Password !== '' ? value.Password : null,
notes: value.Notes && value.Notes !== '' ? value.Notes : null,
name: value.Title && value.Title !== '' ? value.Title : '--',
});
});
success(folders, sites, siteRelationships);
}
});
}
function importKeyPassXml(file, success, error) {
var folders = [],
sites = [],
siteRelationships = [];
var reader = new FileReader();
reader.readAsText(file, 'utf-8');
reader.onload = function (evt) {
var xmlDoc = $.parseXML(evt.target.result),
xml = $(xmlDoc);
var root = xml.find('Root');
if (root.length) {
var group = root.find('> Group');
if (group.length) {
traverse($(group[0]), true, '');
success(folders, sites, siteRelationships);
}
}
else {
error();
}
};
reader.onerror = function (evt) {
error();
};
function traverse(node, isRootNode, groupNamePrefix) {
var nodeEntries = [];
var folderIndex = folders.length;
var groupName = groupNamePrefix;
if (!isRootNode) {
if (groupName !== '') {
groupName += ' > ';
}
groupName += node.find('> Name').text();
folders.push({
name: groupName
});
}
var entries = node.find('> Entry');
if (entries.length) {
for (var i = 0; i < entries.length; i++) {
var entry = $(entries[i]);
var siteIndex = sites.length;
var site = {
favorite: false,
uri: null,
username: null,
password: null,
notes: null,
name: null
};
var entryStrings = entry.find('> String');
for (var j = 0; j < entryStrings.length; j++) {
var entryString = $(entryStrings[j]);
var key = entryString.find('> Key').text();
var value = entryString.find('> Value').text();
if (value === '') {
continue;
}
switch (key) {
case 'URL':
site.uri = value;
break;
case 'UserName':
site.username = value;
break;
case 'Password':
site.password = value;
break;
case 'Title':
site.name = value;
break;
case 'Notes':
site.notes = site.notes === null ? value + '\n' : site.notes + value + '\n';
break;
default:
// other custom fields
site.notes = site.notes === null ? key + ': ' + value + '\n'
: site.notes + key + ': ' + value + '\n';
break;
}
}
if (site.name === null) {
site.name = '--';
}
sites.push(site);
if (!isRootNode) {
siteRelationships.push({
key: siteIndex,
value: folderIndex
});
}
}
}
var groups = node.find('> Group');
if (groups.length) {
for (var k = 0; k < groups.length; k++) {
traverse($(groups[k]), false, groupName);
}
}
}
}
return _service;

View File

@@ -1,2 +1,2 @@
angular.module("bit")
.constant("appSettings", {"rememberedEmailCookieName":"bit.rememberedEmail","version":"1.0.1","environment":"Development","apiUri":"http://localhost:4000"});
.constant("appSettings", {"rememberedEmailCookieName":"bit.rememberedEmail","version":"1.1.1","environment":"Development","apiUri":"http://localhost:4000"});

View File

@@ -19,7 +19,7 @@
}, function () {
$uibModalInstance.dismiss('cancel');
$state.go('backend.vault').then(function () {
$analytics.eventTrack('Imported Data', { label: model.source });
$analytics.eventTrack('Imported Data', { label: $scope.model.source });
toastr.success('Data has been successfully imported into your vault.', 'Import Success');
});
}, importError);

View File

@@ -7,8 +7,10 @@
<div class="form-group">
<label for="source">Source</label>
<select id="source" name="source" class="form-control" ng-model="model.source">
<option value="local">bitwarden</option>
<option value="lastpass">LastPass</option>
<option value="local">bitwarden (csv)</option>
<option value="lastpass">LastPass (csv)</option>
<option value="safeincloudcsv">SafeInCloud (csv)</option>
<option value="keypassxml">KeyPass (xml)</option>
</select>
</div>
<div class="form-group">

View File

@@ -64,7 +64,7 @@
<label for="password">Password</label>
<div class="input-group">
<input tabindex="-1" type="text" id="password-text" value="{{site.password}}" style="margin-left: -9999px;" />
<input type="password" id="password" name="Password" ng-model="site.password" class="form-control" required api-field />
<input type="password" id="password" name="Password" ng-model="site.password" class="form-control" api-field />
<span class="input-group-btn" uib-tooltip="Copy Password" tooltip-placement="left">
<button tabindex="-1" class="btn btn-default btn-flat" type="button" ngclipboard
ngclipboard-success="clipboardSuccess(e)"

View File

@@ -67,7 +67,7 @@
<label for="password">Password</label>
<div class="input-group">
<input type="text" id="password-text" value="{{site.password}}" style="margin-left: -9999px;" />
<input type="password" id="password" name="Password" ng-model="site.password" class="form-control" required api-field />
<input type="password" id="password" name="Password" ng-model="site.password" class="form-control" api-field />
<span class="input-group-btn" uib-tooltip="Copy Password" tooltip-placement="left">
<button tabindex="-1" class="btn btn-default btn-flat" type="button" ngclipboard
ngclipboard-success="clipboardSuccess(e)"

View File

@@ -113,7 +113,8 @@
</a>
</li>
<li class="header">
MOBILE APPS<small class="label pull-right bg-green">FREE</small>
<small class="label pull-right bg-green">FREE</small>
MOBILE APPS
</li>
<li>
<a href="https://itunes.apple.com/us/app/bitwarden-free-password-manager/id1137397744?mt=8"
@@ -128,7 +129,8 @@
</a>
</li>
<li class="header">
BROWSER EXTENSIONS <small class="label pull-right bg-green">FREE</small>
<small class="label pull-right bg-green">FREE</small>
BROWSER EXTENSIONS
</li>
<li>
<a href="https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb"
@@ -139,22 +141,22 @@
<li>
<a href="javascript:void(0)"
target="_blank" analytics-on="click" analytics-event="Clicked Firefox">
<i class="fa fa-firefox"></i> <span>Firefox</span>
<small class="label pull-right bg-gray">coming very soon</small>
<i class="fa fa-firefox"></i> <span>Firefox</span>
</a>
</li>
<li>
<a href="javascript:void(0)"
target="_blank" analytics-on="click" analytics-event="Clicked Opera">
<i class="fa fa-opera"></i> <span>Opera</span>
<small class="label pull-right bg-gray">coming very soon</small>
<i class="fa fa-opera"></i> <span>Opera</span>
</a>
</li>
<li>
<a href="javascript:void(0)"
target="_blank" analytics-on="click" analytics-event="Clicked Edge">
<i class="fa fa-edge"></i> <span>Edge</span>
<small class="label pull-right bg-gray">coming soon</small>
<i class="fa fa-edge"></i> <span>Edge</span>
</a>
</li>
</ul>

View File

@@ -10,13 +10,13 @@
<!-- @if true !>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" />
<meta name="x-stylesheet-test-bs" content="" class="invisible" />
<script>!function(a,b,c){var d,e=document,f=e.getElementsByTagName('SCRIPT'),g=f[f.length-1].previousElementSibling,h=e.defaultView&&e.defaultView.getComputedStyle?e.defaultView.getComputedStyle(g):g.currentStyle;if(h&&h[a]!==b)for(d=0;d<c.length;d++)e.write('<link rel="stylesheet" href="'+c[d]+'"/>')}('visibility','hidden',['lib\/bootstrap\/css\/bootstrap.min.css']);</script>
<script>!function(a,b,c){var d,e=document,f=e.getElementsByTagName('SCRIPT'),g=f[f.length-1].previousElementSibling,h=e.defaultView&&e.defaultView.getComputedStyle?e.defaultView.getComputedStyle(g):g.currentStyle;if(h&&h[a]!==b)for(d=0;d<c.length;d++)e.write('<link rel="stylesheet" href="'+c[d]+'"/>')}('visibility','hidden',['lib\/bootstrap\/css\/bootstrap.min.css?v=<!-- @echo cacheTag !>']);</script>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" />
<meta name="x-stylesheet-test-fa" content="" class="fa" />
<script>!function(a,b,c){var d,e=document,f=e.getElementsByTagName('SCRIPT'),g=f[f.length-1].previousElementSibling,h=e.defaultView&&e.defaultView.getComputedStyle?e.defaultView.getComputedStyle(g):g.currentStyle;if(h&&h[a]!==b)for(d=0;d<c.length;d++)e.write('<link rel="stylesheet" href="'+c[d]+'"/>')}('font-family','FontAwesome',['lib\/font-awesome\/css\/font-awesome.min.css']);</script>
<script>!function(a,b,c){var d,e=document,f=e.getElementsByTagName('SCRIPT'),g=f[f.length-1].previousElementSibling,h=e.defaultView&&e.defaultView.getComputedStyle?e.defaultView.getComputedStyle(g):g.currentStyle;if(h&&h[a]!==b)for(d=0;d<c.length;d++)e.write('<link rel="stylesheet" href="'+c[d]+'"/>')}('font-family','FontAwesome',['lib\/font-awesome\/css\/font-awesome.min.css?v=<!-- @echo cacheTag !>']);</script>
<link rel="stylesheet" href="css/vault.min.css" />
<link rel="stylesheet" href="css/vault.min.css?v=<!-- @echo cacheTag !>" />
<!-- @endif -->
<!-- @exclude -->
<link rel="stylesheet" href="lib/bootstrap/css/bootstrap.css" />
@@ -30,16 +30,16 @@
<!-- @if true !>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script>(window.jQuery||document.write('<script src="lib\/jquery\/jquery.min.js"><\/script>'));</script>
<script>(window.jQuery||document.write('<script src="lib\/jquery\/jquery.min.js?v=<!-- @echo cacheTag !>"><\/script>'));</script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script>((window.jQuery&&window.jQuery.fn&&window.jQuery.fn.modal)||document.write('<script src="lib\/bootstrap\/js\/bootstrap.min.js"><\/script>'));</script>
<script>((window.jQuery&&window.jQuery.fn&&window.jQuery.fn.modal)||document.write('<script src="lib\/bootstrap\/js\/bootstrap.min.js?v=<!-- @echo cacheTag !>"><\/script>'));</script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script>(window.angular||document.write('<script src="lib\/angular\/angular.min.js"><\/script>'));</script>
<script>(window.angular||document.write('<script src="lib\/angular\/angular.min.js?v=<!-- @echo cacheTag !>"><\/script>'));</script>
<script src="js/lib.min.js"></script>
<script src="js/app.min.js"></script>
<script src="js/lib.min.js?v=<!-- @echo cacheTag !>"></script>
<script src="js/app.min.js?v=<!-- @echo cacheTag !>"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){