Added export vault function to tools.

This commit is contained in:
Kyle Spearrin 2017-04-12 08:45:31 -04:00
parent 4f70911f58
commit 9e6c0f79ef
10 changed files with 187 additions and 2 deletions

View File

@ -123,6 +123,10 @@ gulp.task('lib', ['clean:lib'], function () {
{
src: paths.npmDir + 'ng-infinite-scroll/build/ng-infinite-scroll.js',
dest: paths.libDir + 'ng-infinite-scroll'
},
{
src: paths.npmDir + 'papaparse/papaparse*.js',
dest: paths.libDir + 'papaparse'
}
];

View File

@ -35,6 +35,7 @@
"ng-infinite-scroll": "1.3.0",
"node-forge": "0.7.0",
"webpack-stream": "3.2.0",
"gulp-json-editor": "2.2.1"
"gulp-json-editor": "2.2.1",
"papaparse": "4.2.0"
}
}

View File

@ -654,5 +654,25 @@
"disableContextMenuItemDesc": {
"message": "Context menu options provide quick access to password generation and logins for the website in your current tab.",
"desription": "Context menu options provide quick access to password generation and logins for the website in your current tab."
},
"exportVault": {
"message": "Export Vault",
"desription": "Export Vault"
},
"warning": {
"message": "WARNING",
"desription": "WARNING (should stay in capitalized letters if the language permits)"
},
"exportWarning": {
"message": "This export contains your unencrypted data in .csv format. You should not store or send it over unsecure channels (such as email). Delete it immediately after your are done using it.",
"desription": "This export contains your unencrypted data in .csv format. You should not store or send it over unsecure channels (such as email). Delete it immediately after your are done using it."
},
"exportMasterPassword": {
"message": "Enter your master password to export your vault data.",
"desription": "Enter your master password to export your vault data."
},
"exportVaultInfo": {
"message": "Export your vault data in .csv format so that you can easily modify it or move it elsewhere.",
"desription": "Export your vault data in .csv format so that you can easily modify it or move it elsewhere."
}
}

View File

@ -652,7 +652,7 @@
"description": "Disable Context Menu Options"
},
"disableContextMenuItemDesc": {
"message": "Les options de menu contextuelles permettent un accès rapide à la génération de mots de passe et d\'identifiants pour le site web de l\'onglet actuel",
"message": "Les options de menu contextuelles permettent un accès rapide à la génération de mots de passe et d'identifiants pour le site web de l'onglet actuel",
"desription": "Context menu options provide quick access to password generation and logins for the website in your current tab."
}
}

View File

@ -141,6 +141,13 @@
data: { authorize: true },
params: { animation: null, addState: null, editState: null }
})
.state('export', {
url: '/export',
templateUrl: 'app/tools/views/toolsExport.html',
controller: 'toolsExportController',
data: { authorize: true },
params: { animation: null }
})
.state('about', {
url: '/about',

View File

@ -0,0 +1,116 @@
angular
.module('bit.tools')
.controller('toolsExportController', function ($scope, $state, toastr, $q, $analytics,
i18nService, cryptoService, userService, folderService, loginService) {
$scope.i18n = i18nService;
$scope.submitPromise = null;
$scope.submit = function () {
$scope.submitPromise = checkPassword().then(function () {
return getCsv();
}).then(function (csv) {
downloadFile(csv);
}, function () {
toastr.error(i18nService.invalidMasterPassword, i18nService.errorsOccurred);
});
};
function checkPassword() {
var deferred = $q.defer();
userService.getEmail(function (email) {
var key = cryptoService.makeKey($scope.masterPassword, email);
cryptoService.hashPassword($scope.masterPassword, key, function (keyHash) {
cryptoService.getKeyHash(true, function (storedKeyHash) {
if (storedKeyHash && keyHash && storedKeyHash === keyHash) {
deferred.resolve();
}
else {
deferred.reject();
}
});
});
});
return deferred.promise;
}
function getCsv() {
var deferred = $q.defer();
var decFolders = [];
var decLogins = [];
var promises = [];
var folderPromise = $q.when(folderService.getAllDecrypted());
folderPromise.then(function (folders) {
decFolders = folders;
});
promises.push(folderPromise);
var loginPromise = $q.when(loginService.getAllDecrypted());
loginPromise.then(function (logins) {
decLogins = logins;
});
promises.push(loginPromise);
$q.all(promises).then(function () {
var exportLogins = [];
for (var i = 0; i < decLogins.length; i++) {
var login = {
name: decLogins[i].name,
uri: decLogins[i].uri,
username: decLogins[i].username,
password: decLogins[i].password,
notes: decLogins[i].notes,
folder: null
};
for (var j = 0; j < decFolders.length; j++) {
if (decFolders[j].id === decLogins[i].folderId && decFolders[j].name !== i18nService.noneFolder) {
login.folder = decFolders[j].name;
break;
}
}
exportLogins.push(login);
}
var csv = Papa.unparse(exportLogins);
deferred.resolve(csv);
});
return deferred.promise;
}
function downloadFile(csvString) {
var csvBlob = new Blob([csvString]);
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(csvBlob, makeFileName());
}
else {
var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(csvBlob, { type: 'text/plain' });
a.download = makeFileName();
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
}
function makeFileName() {
var now = new Date();
var dateString =
now.getFullYear() + '' + padNumber(now.getMonth() + 1, 2) + '' + padNumber(now.getDate(), 2) +
padNumber(now.getHours(), 2) + '' + padNumber(now.getMinutes(), 2) +
padNumber(now.getSeconds(), 2);
return 'bitwarden_export_' + dateString + '.csv';
}
function padNumber(number, width, paddingCharacter) {
paddingCharacter = paddingCharacter || '0';
number = number + '';
return number.length >= width ? number : new Array(width - number.length + 1).join(paddingCharacter) + number;
}
});

View File

@ -30,6 +30,11 @@
<span class="text">{{i18n.importLogins}}</span>
<span class="detail">{{i18n.importLoginsInfo}}</span>
</a>
<a class="list-section-item wrap" href="" ui-sref="export({animation: 'in-slide-up'})">
<span class="leading-icon" style="color: #ff6f6f;"><i class="fa fa-cloud-download fa-fw"></i></span>
<span class="text">{{i18n.exportVault}}</span>
<span class="detail">{{i18n.exportVaultInfo}}</span>
</a>
</div>
</div>
</div>

View File

@ -0,0 +1,29 @@
<form name="theForm" ng-submit="submit()" bit-form="submitPromise">
<div class="header">
<div class="left">
<a ui-sref="tabs.tools({animation: 'out-slide-down'})">{{i18n.close}}</a>
</div>
<div class="right">
<button type="submit" class="btn btn-link">{{i18n.submit}}</button>
</div>
<div class="title">{{i18n.exportVault}}</div>
</div>
<div class="content">
<div class="list">
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-icon-input">
<i class="fa fa-lock fa-lg fa-fw"></i>
<label for="master-password" class="sr-only">{{i18n.masterPass}}</label>
<input id="master-password" type="password" name="MasterPassword" placeholder="{{i18n.masterPass}}"
ng-model="masterPassword">
</div>
</div>
<div class="list-section-footer">
<p>{{i18n.exportMasterPassword}}</p>
<b>{{i18n.warning}}</b>: {{i18n.exportWarning}}
</div>
</div>
</div>
</div>
</form>

View File

@ -14,6 +14,7 @@
<script src="../lib/jquery/jquery.js"></script>
<script src="../lib/bootstrap/js/bootstrap.js"></script>
<script src="../lib/papaparse/papaparse.js"></script>
<script src="../lib/clipboard/clipboard.js"></script>
<script src="../scripts/analytics.js"></script>
@ -82,6 +83,7 @@
<script src="app/tools/toolsModule.js"></script>
<script src="app/tools/toolsController.js"></script>
<script src="app/tools/toolsPasswordGeneratorController.js"></script>
<script src="app/tools/toolsExportController.js"></script>
<script src="app/lock/lockModule.js"></script>
<script src="app/lock/lockController.js"></script>

View File

@ -131,6 +131,7 @@ function initCryptoService() {
if (b64 && b64 === true && _b64KeyHash) {
callback(_b64KeyHash);
return;
}
else if (!b64 && _keyHash) {
callback(_keyHash);