convert export to ts component

This commit is contained in:
Kyle Spearrin 2017-11-14 11:29:00 -05:00
parent b9139eec33
commit 00b8a3e7be
7 changed files with 176 additions and 172 deletions

View File

@ -58,6 +58,7 @@
"@types/chrome": "0.0.51",
"@types/jquery": "^3.2.16",
"@types/node-forge": "0.6.10",
"@types/papaparse": "4.1.31",
"@types/tldjs": "1.7.1",
"@types/webcrypto": "^0.0.28",
"@uirouter/angularjs": "^1.0.10"

View File

@ -1,6 +1,4 @@
window.Papa = require('papaparse');
require('clipboard');
require('angular');
require('angular-animate');
@ -131,7 +129,6 @@ require('./settings/settingsEditFolderController.js');
require('./settings/settingsPremiumController.js');
require('./settings/settingsEnvironmentController.js');
require('./tools/toolsPasswordGeneratorHistoryController.js');
require('./tools/toolsExportController.js');
// Bootstrap the angular application
angular.element(function () {

View File

@ -180,8 +180,7 @@ angular
})
.state('export', {
url: '/export',
template: require('./tools/views/toolsExport.html'),
controller: 'toolsExportController',
component: 'export',
data: { authorize: true },
params: { animation: null }
})

View File

@ -1,12 +1,12 @@
<form name="theForm" ng-submit="submit()" bit-form="submitPromise">
<form name="theForm" ng-submit="$ctrl.submit()" bit-form="submitPromise">
<div class="header">
<div class="left">
<a ui-sref="tabs.tools({animation: 'out-slide-down'})">{{i18n.close}}</a>
<a ui-sref="tabs.tools({animation: 'out-slide-down'})">{{$ctrl.i18n.close}}</a>
</div>
<div class="right">
<button type="submit" class="btn btn-link">{{i18n.submit}}</button>
<button type="submit" class="btn btn-link">{{$ctrl.i18n.submit}}</button>
</div>
<div class="title">{{i18n.exportVault}}</div>
<div class="title">{{$ctrl.i18n.exportVault}}</div>
</div>
<div class="content">
<div class="list">
@ -14,14 +14,14 @@
<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">
<label for="master-password" class="sr-only">{{$ctrl.i18n.masterPass}}</label>
<input id="master-password" type="password" name="MasterPassword"
placeholder="{{$ctrl.i18n.masterPass}}" ng-model="$ctrl.masterPassword">
</div>
</div>
<div class="list-section-footer">
<p>{{i18n.exportMasterPassword}}</p>
<b>{{i18n.warning}}</b>: {{i18n.exportWarning}}
<p>{{$ctrl.i18n.exportMasterPassword}}</p>
<b>{{$ctrl.i18n.warning}}</b>: {{$ctrl.i18n.exportWarning}}
</div>
</div>
</div>

View File

@ -0,0 +1,163 @@
import * as angular from 'angular';
import * as papa from 'papaparse';
import * as template from './export.component.html';
import { CipherType } from '../../../enums/cipherType.enum';
import { CryptoService } from '../../../services/abstractions/crypto.service';
import { UtilsService } from '../../../services/abstractions/utils.service';
export class ExportController {
i18n: any;
masterPassword: string;
constructor(private $scope: any, private $state: any, private cryptoService: CryptoService,
private toastr: any, private utilsService: UtilsService, private $analytics: any,
private i18nService: any, private folderService: any, private cipherService: any,
private $window: any, private userService: any) {
this.i18n = i18nService;
this.$scope.submitPromise = null;
}
$onInit() {
document.getElementById('master-password').focus();
}
submit() {
const self = this;
this.$scope.submitPromise = this.checkPassword().then(() => {
return self.getCsv();
}).then((csv) => {
self.$analytics.eventTrack('Exported Data');
self.downloadFile(csv);
}, () => {
this.toastr.error(self.i18n.invalidMasterPassword, self.i18n.errorsOccurred);
});
}
private async checkPassword() {
const email = await this.userService.getEmail();
const key = this.cryptoService.makeKey(this.masterPassword, email);
const keyHash = await this.cryptoService.hashPassword(this.masterPassword, key);
const storedKeyHash = await this.cryptoService.getKeyHash();
if (storedKeyHash == null || keyHash == null || storedKeyHash !== keyHash) {
throw new Error('Invalid password.');
}
}
private async getCsv(): Promise<string> {
let decFolders: any[] = [];
let decCiphers: any[] = [];
const promises = [];
promises.push(this.folderService.getAllDecrypted().then((folders: any[]) => {
decFolders = folders;
}));
promises.push(this.cipherService.getAllDecrypted().then((ciphers: any[]) => {
decCiphers = ciphers;
}));
await Promise.all(promises);
const foldersMap = new Map<string, any>();
for (const f of decFolders) {
foldersMap.set(f.id, f);
}
const exportCiphers = [];
for (const c of decCiphers) {
// only export logins and secure notes
if (c.type !== CipherType.Login && c.type !== CipherType.SecureNote) {
continue;
}
const cipher: any = {
folder: c.folderId && foldersMap.has(c.folderId) ? foldersMap.get(c.folderId).name : null,
favorite: c.favorite ? 1 : null,
type: null,
name: c.name,
notes: c.notes,
fields: null,
// Login props
login_uri: null,
login_username: null,
login_password: null,
login_totp: null,
};
if (c.fields) {
for (const f of c.fields) {
if (!cipher.fields) {
cipher.fields = '';
} else {
cipher.fields += '\n';
}
cipher.fields += ((f.name || '') + ': ' + f.value);
}
}
switch (c.type) {
case CipherType.Login:
cipher.type = 'login';
cipher.login_uri = c.login.uri;
cipher.login_username = c.login.username;
cipher.login_password = c.login.password;
cipher.login_totp = c.login.totp;
break;
case CipherType.SecureNote:
cipher.type = 'note';
break;
default:
continue;
}
exportCiphers.push(cipher);
}
const csv = papa.unparse(exportCiphers);
return csv;
}
private downloadFile(csv: string): void {
const csvBlob = new Blob([csv]);
const fileName = this.makeFileName();
if (this.$window.navigator.msSaveOrOpenBlob) {
// Currently bugged in Edge. See
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8178877/
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8477778/
this.$window.navigator.msSaveBlob(csvBlob, fileName);
} else {
const a = this.$window.document.createElement('a');
a.href = this.$window.URL.createObjectURL(csvBlob, { type: 'text/plain' });
a.download = fileName;
this.$window.document.body.appendChild(a);
a.click();
this.$window.document.body.removeChild(a);
}
}
private makeFileName(): string {
const now = new Date();
const dateString =
now.getFullYear() + '' + this.padNumber(now.getMonth() + 1, 2) + '' + this.padNumber(now.getDate(), 2) +
this.padNumber(now.getHours(), 2) + '' + this.padNumber(now.getMinutes(), 2) +
this.padNumber(now.getSeconds(), 2);
return 'bitwarden_export_' + dateString + '.csv';
}
private padNumber(num: number, width: number, padCharacter: string = '0'): string {
const numString = num.toString();
return numString.length >= width ? numString :
new Array(width - numString.length + 1).join(padCharacter) + numString;
}
}
export const ExportComponent = {
bindings: {},
controller: ExportController,
template,
};

View File

@ -1,4 +1,5 @@
import * as angular from 'angular';
import { ExportComponent } from './export.component';
import { PasswordGeneratorComponent } from './password-generator.component';
import { ToolsComponent } from './tools.component';
@ -7,5 +8,6 @@ export default angular
.component('tools', ToolsComponent)
.component('passwordGenerator', PasswordGeneratorComponent)
.component('export', ExportComponent)
.name;

View File

@ -1,158 +0,0 @@
angular
.module('bit.tools')
.controller('toolsExportController', function ($scope, $state, toastr, $q, $analytics,
i18nService, cryptoService, userService, folderService, cipherService, $window, constantsService) {
$scope.i18n = i18nService;
document.getElementById('master-password').focus();
$scope.submitPromise = null;
$scope.submit = function () {
$scope.submitPromise = checkPassword().then(function () {
return getCsv();
}).then(function (csv) {
$analytics.eventTrack('Exported Data');
downloadFile(csv);
}, function () {
toastr.error(i18nService.invalidMasterPassword, i18nService.errorsOccurred);
});
};
function checkPassword() {
var deferred = $q.defer();
userService.getEmail().then(function (email) {
var key = cryptoService.makeKey($scope.masterPassword, email);
var keyHash;
cryptoService.hashPassword($scope.masterPassword, key).then(function (theKeyHash) {
keyHash = theKeyHash;
return cryptoService.getKeyHash();
}).then(function (storedKeyHash) {
if (storedKeyHash && keyHash && storedKeyHash === keyHash) {
deferred.resolve();
}
else {
deferred.reject();
}
});
});
return deferred.promise;
}
function getCsv() {
var decFolders = [];
var decCiphers = [];
var promises = [];
var folderPromise = folderService.getAllDecrypted().then(function (folders) {
decFolders = folders;
});
promises.push(folderPromise);
var ciphersPromise = cipherService.getAllDecrypted().then(function (ciphers) {
decCiphers = ciphers;
});
promises.push(ciphersPromise);
return $q.all(promises).then(function () {
var foldersDict = {};
for (var i = 0; i < decFolders.length; i++) {
foldersDict[decFolders[i].id] = decFolders[i];
}
var exportCiphers = [];
for (i = 0; i < decCiphers.length; i++) {
// only export logins and secure notes
if (decCiphers[i].type !== constantsService.cipherType.login &&
decCiphers[i].type !== constantsService.cipherType.secureNote) {
continue;
}
var cipher = {
folder: decCiphers[i].folderId && (decCiphers[i].folderId in foldersDict) ?
foldersDict[decCiphers[i].folderId].name : null,
favorite: decCiphers[i].favorite ? 1 : null,
type: null,
name: decCiphers[i].name,
notes: decCiphers[i].notes,
fields: null,
// Login props
login_uri: null,
login_username: null,
login_password: null,
login_totp: null
};
if (decCiphers[i].fields) {
for (var j = 0; j < decCiphers[i].fields.length; j++) {
if (!cipher.fields) {
cipher.fields = '';
}
else {
cipher.fields += '\n';
}
cipher.fields += ((decCiphers[i].fields[j].name || '') + ': ' + decCiphers[i].fields[j].value);
}
}
switch (decCiphers[i].type) {
case constantsService.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;
break;
case constantsService.cipherType.secureNote:
cipher.type = 'note';
break;
default:
continue;
}
exportCiphers.push(cipher);
}
var csv = Papa.unparse(exportCiphers);
return csv;
});
}
function downloadFile(csvString) {
var csvBlob = new Blob([csvString]);
var fileName = makeFileName();
if ($window.navigator.msSaveOrOpenBlob) {
// Currently bugged in Edge. See
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8178877/
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8477778/
$window.navigator.msSaveBlob(csvBlob, fileName);
}
else {
var a = $window.document.createElement('a');
a.href = $window.URL.createObjectURL(csvBlob, { type: 'text/plain' });
a.download = fileName;
$window.document.body.appendChild(a);
a.click();
$window.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;
}
});