1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-12-31 17:57:43 +01:00

share login with attachments

This commit is contained in:
Kyle Spearrin 2017-07-10 14:30:33 -04:00
parent 204ee72926
commit 47cb20f01e
5 changed files with 174 additions and 147 deletions

View File

@ -22,78 +22,40 @@
return; return;
} }
var file = files[0];
if (file.size > 104857600) { // 100 MB
validationService.addError(form, 'file', 'Maximum file size is 100 MB.', true);
return;
}
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function (evt) {
$timeout(function () {
form.$loading = true;
});
var key = cryptoService.getOrgKey($scope.login.organizationId); var key = cryptoService.getOrgKey($scope.login.organizationId);
var encFilename = cryptoService.encrypt(file.name, key); $scope.savePromise = cipherService.encryptAttachmentFile(key, files[0]).then(function (encValue) {
$scope.savePromise = cryptoService.encryptToBytes(evt.target.result, key).then(function (encData) {
var fd = new FormData(); var fd = new FormData();
var blob = new Blob([encData], { type: 'application/octet-stream' }); var blob = new Blob([encValue.data], { type: 'application/octet-stream' });
fd.append('data', blob, encFilename); fd.append('data', blob, encValue.fileName);
return apiService.ciphers.postAttachment({ id: loginId }, fd).$promise; return apiService.ciphers.postAttachment({ id: loginId }, fd).$promise;
}).then(function (response) { }).then(function (response) {
$analytics.eventTrack('Added Organization Attachment'); $analytics.eventTrack('Added Attachment');
toastr.success('The attachment has been added.'); toastr.success('The attachment has been added.');
closing = true; closing = true;
$uibModalInstance.close(true); $uibModalInstance.close(true);
}, function (err) {
if (err) {
validationService.addError(form, 'file', err, true);
}
else {
validationService.addError(form, 'file', 'Something went wrong.', true);
}
}); });
};
reader.onerror = function (evt) {
validationService.addError(form, 'file', 'Error reading file.', true);
};
} }
$scope.download = function (attachment) { $scope.download = function (attachment) {
attachment.loading = true; attachment.loading = true;
var req = new XMLHttpRequest();
req.open('GET', attachment.url, true);
req.responseType = 'arraybuffer';
req.onload = function (evt) {
if (!req.response) {
$timeout(function () {
attachment.loading = false;
});
// error
return;
}
var key = cryptoService.getOrgKey($scope.login.organizationId); var key = cryptoService.getOrgKey($scope.login.organizationId);
cryptoService.decryptFromBytes(req.response, key).then(function (decBuf) { cipherService.downloadAndDecryptAttachment(key, attachment, true).then(function (res) {
var blob = new Blob([decBuf]); $timeout(function () {
attachment.loading = false;
// IE hack. ref http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx });
if (window.navigator.msSaveOrOpenBlob) { }, function () {
window.navigator.msSaveBlob(blob, attachment.fileName);
}
else {
var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(blob);
a.download = attachment.fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
$timeout(function () { $timeout(function () {
attachment.loading = false; attachment.loading = false;
}); });
}); });
}; };
req.send(null);
};
$scope.remove = function (attachment) { $scope.remove = function (attachment) {
if (!confirm('Are you sure you want to delete this attachment (' + attachment.fileName + ')?')) { if (!confirm('Are you sure you want to delete this attachment (' + attachment.fileName + ')?')) {

View File

@ -47,6 +47,12 @@
headers: { 'Content-Type': undefined }, headers: { 'Content-Type': undefined },
params: { id: '@id' } 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' } }
}); });

View File

@ -1,7 +1,7 @@
angular angular
.module('bit.services') .module('bit.services')
.factory('cipherService', function (cryptoService, apiService, $q) { .factory('cipherService', function (cryptoService, apiService, $q, $window) {
var _service = {}; var _service = {};
_service.decryptLogins = function (encryptedLogins) { _service.decryptLogins = function (encryptedLogins) {
@ -89,6 +89,43 @@ angular
}; };
}; };
_service.downloadAndDecryptAttachment = function (key, decryptedAttachment, openDownload) {
var deferred = $q.defer();
var req = new XMLHttpRequest();
req.open('GET', decryptedAttachment.url, true);
req.responseType = 'arraybuffer';
req.onload = function (evt) {
if (!req.response) {
deferred.reject('No response');
// error
return;
}
cryptoService.decryptFromBytes(req.response, key).then(function (decBuf) {
if (openDownload) {
var blob = new Blob([decBuf]);
// IE hack. ref http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx
if ($window.navigator.msSaveOrOpenBlob) {
$window.navigator.msSaveBlob(blob, decryptedAttachment.fileName);
}
else {
var a = $window.document.createElement('a');
a.href = $window.URL.createObjectURL(blob);
a.download = decryptedAttachment.fileName;
$window.document.body.appendChild(a);
a.click();
$window.document.body.removeChild(a);
}
}
deferred.resolve(new Uint8Array(decBuf));
});
};
req.send(null);
return deferred.promise;
};
_service.decryptFolders = function (encryptedFolders) { _service.decryptFolders = function (encryptedFolders) {
if (!encryptedFolders) throw "encryptedFolders is undefined or null"; if (!encryptedFolders) throw "encryptedFolders is undefined or null";
@ -169,14 +206,14 @@ angular
return encryptedLogins; return encryptedLogins;
}; };
_service.encryptLogin = function (unencryptedLogin, key) { _service.encryptLogin = function (unencryptedLogin, key, attachments) {
if (!unencryptedLogin) throw "unencryptedLogin is undefined or null"; if (!unencryptedLogin) throw "unencryptedLogin is undefined or null";
if (unencryptedLogin.organizationId) { if (unencryptedLogin.organizationId) {
key = key || cryptoService.getOrgKey(unencryptedLogin.organizationId); key = key || cryptoService.getOrgKey(unencryptedLogin.organizationId);
} }
return { var login = {
id: unencryptedLogin.id, id: unencryptedLogin.id,
'type': 1, 'type': 1,
organizationId: unencryptedLogin.organizationId || null, organizationId: unencryptedLogin.organizationId || null,
@ -189,6 +226,42 @@ angular
notes: !unencryptedLogin.notes || unencryptedLogin.notes === '' ? null : cryptoService.encrypt(unencryptedLogin.notes, key), notes: !unencryptedLogin.notes || unencryptedLogin.notes === '' ? null : cryptoService.encrypt(unencryptedLogin.notes, key),
totp: !unencryptedLogin.totp || unencryptedLogin.totp === '' ? null : cryptoService.encrypt(unencryptedLogin.totp, key) totp: !unencryptedLogin.totp || unencryptedLogin.totp === '' ? null : cryptoService.encrypt(unencryptedLogin.totp, key)
}; };
if (unencryptedLogin.attachments && attachments) {
login.attachments = {};
for (var i = 0; i < unencryptedLogin.attachments.length; i++) {
login.attachments[unencryptedLogin.attachments[i].id] =
cryptoService.encrypt(unencryptedLogin.attachments[i].fileName, key);
}
}
return login;
};
_service.encryptAttachmentFile = function (key, unencryptedFile) {
var deferred = $q.defer();
if (unencryptedFile.size > 104857600) { // 100 MB
deferred.reject('Maximum file size is 100 MB.');
return;
}
var reader = new FileReader();
reader.readAsArrayBuffer(unencryptedFile);
reader.onload = function (evt) {
cryptoService.encryptToBytes(evt.target.result, key).then(function (encData) {
deferred.resolve({
fileName: cryptoService.encrypt(unencryptedFile.name, key),
data: new Uint8Array(encData),
size: unencryptedFile.size
});
});
};
reader.onerror = function (evt) {
deferred.reject('Error reading file.');
};
return deferred.promise;
}; };
_service.encryptFolders = function (unencryptedFolders, key) { _service.encryptFolders = function (unencryptedFolders, key) {

View File

@ -24,79 +24,38 @@
return; return;
} }
var file = files[0]; $scope.savePromise = cipherService.encryptAttachmentFile(getKeyForLogin(), files[0]).then(function (encValue) {
if (file.size > 104857600) { // 100 MB
validationService.addError(form, 'file', 'Maximum file size is 100 MB.', true);
return;
}
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function (evt) {
$timeout(function () {
form.$loading = true;
});
var key = getKeyForLogin();
var encFilename = cryptoService.encrypt(file.name, key);
$scope.savePromise = cryptoService.encryptToBytes(evt.target.result, key).then(function (encData) {
var fd = new FormData(); var fd = new FormData();
var blob = new Blob([encData], { type: 'application/octet-stream' }); var blob = new Blob([encValue.data], { type: 'application/octet-stream' });
fd.append('data', blob, encFilename); fd.append('data', blob, encValue.fileName);
return apiService.ciphers.postAttachment({ id: loginId }, fd).$promise; return apiService.ciphers.postAttachment({ id: loginId }, fd).$promise;
}).then(function (response) { }).then(function (response) {
$analytics.eventTrack('Added Attachment'); $analytics.eventTrack('Added Attachment');
toastr.success('The attachment has been added.'); toastr.success('The attachment has been added.');
closing = true; closing = true;
$uibModalInstance.close(true); $uibModalInstance.close(true);
}, function (err) {
if (err) {
validationService.addError(form, 'file', err, true);
}
else {
validationService.addError(form, 'file', 'Something went wrong.', true);
}
}); });
};
reader.onerror = function (evt) {
validationService.addError(form, 'file', 'Error reading file.', true);
};
} }
$scope.download = function (attachment) { $scope.download = function (attachment) {
attachment.loading = true; attachment.loading = true;
var key = getKeyForLogin(); cipherService.downloadAndDecryptAttachment(getKeyForLogin(), attachment, true).then(function (res) {
var req = new XMLHttpRequest();
req.open('GET', attachment.url, true);
req.responseType = 'arraybuffer';
req.onload = function (evt) {
if (!req.response) {
$timeout(function () { $timeout(function () {
attachment.loading = false; attachment.loading = false;
}); });
}, function () {
// error
return;
}
cryptoService.decryptFromBytes(req.response, key).then(function (decBuf) {
var blob = new Blob([decBuf]);
// IE hack. ref http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, attachment.fileName);
}
else {
var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(blob);
a.download = attachment.fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
$timeout(function () { $timeout(function () {
attachment.loading = false; attachment.loading = false;
}); });
}); });
}; };
req.send(null);
};
function getKeyForLogin() { function getKeyForLogin() {
if ($scope.login.organizationId) { if ($scope.login.organizationId) {

View File

@ -2,7 +2,7 @@
.module('bit.vault') .module('bit.vault')
.controller('vaultShareLoginController', function ($scope, apiService, $uibModalInstance, authService, cipherService, .controller('vaultShareLoginController', function ($scope, apiService, $uibModalInstance, authService, cipherService,
loginId, $analytics, $state) { loginId, $analytics, $state, cryptoService, $q) {
$analytics.eventTrack('vaultShareLoginController', { category: 'Modal' }); $analytics.eventTrack('vaultShareLoginController', { category: 'Modal' });
$scope.model = {}; $scope.model = {};
$scope.login = {}; $scope.login = {};
@ -111,11 +111,37 @@
$scope.submitPromise = null; $scope.submitPromise = null;
$scope.submit = function (model) { $scope.submit = function (model) {
var orgKey = cryptoService.getOrgKey(model.organizationId);
var attachmentSharePromises = [];
if ($scope.login.attachments) {
for (var i = 0; i < $scope.login.attachments.length; i++) {
var attachment = $scope.login.attachments[i];
var promise = cipherService.downloadAndDecryptAttachment(null, attachment, false)
.then(function (decData) {
return cryptoService.encryptToBytes(decData.buffer, orgKey);
}).then(function (encData) {
var fd = new FormData();
var blob = new Blob([encData], { type: 'application/octet-stream' });
var encFilename = cryptoService.encrypt(attachment.fileName, orgKey);
fd.append('data', blob, encFilename);
return apiService.ciphers.postShareAttachment({
id: loginId,
attachmentId: attachment.id,
orgId: model.organizationId
}, fd).$promise;
});
attachmentSharePromises.push(promise);
}
}
$scope.submitPromise = $q.all(attachmentSharePromises).then(function () {
$scope.login.organizationId = model.organizationId; $scope.login.organizationId = model.organizationId;
var request = { var request = {
collectionIds: [], collectionIds: [],
cipher: cipherService.encryptLogin($scope.login) cipher: cipherService.encryptLogin($scope.login, null, true)
}; };
for (var id in $scope.selectedCollections) { for (var id in $scope.selectedCollections) {
@ -124,10 +150,11 @@
} }
} }
$scope.submitPromise = apiService.ciphers.putShare({ id: loginId }, request, function (response) { return apiService.ciphers.putShare({ id: loginId }, request).$promise;
}).then(function (response) {
$analytics.eventTrack('Shared Login'); $analytics.eventTrack('Shared Login');
$uibModalInstance.close(model.organizationId); $uibModalInstance.close(model.organizationId);
}).$promise; });
}; };
$scope.close = function () { $scope.close = function () {