1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-27 12:36:14 +01:00

share login modal

This commit is contained in:
Kyle Spearrin 2017-02-28 00:18:11 -05:00
parent 63c136a1ff
commit 1ed86899bb
8 changed files with 190 additions and 80 deletions

View File

@ -29,6 +29,13 @@
del: { url: _apiUri + '/ciphers/:id/delete', method: 'POST', params: { id: '@id' } }
});
_service.shares = $resource(_apiUri + '/shares/:id', {}, {
get: { method: 'GET', params: { id: '@id' } },
listCipher: { url: _apiUri + '/shares/:cipherId', method: 'GET', params: { cipherId: '@cipherId' } },
post: { method: 'POST', params: {} },
del: { url: _apiUri + '/shares/:id/delete', method: 'POST', params: { id: '@id' } }
});
_service.accounts = $resource(_apiUri + '/accounts', {}, {
register: { url: _apiUri + '/accounts/register', method: 'POST', params: {} },
emailToken: { url: _apiUri + '/accounts/email-token', method: 'POST', params: {} },

View File

@ -18,16 +18,22 @@ angular
_service.decryptLogin = function (encryptedLogin) {
if (!encryptedLogin) throw "encryptedLogin is undefined or null";
var key = null;
if (encryptedLogin.Key) {
key = cryptoService.rsaDecrypt(encryptedLogin.Key);
}
var login = {
id: encryptedLogin.Id,
'type': 1,
folderId: encryptedLogin.FolderId,
favorite: encryptedLogin.Favorite,
name: cryptoService.decrypt(encryptedLogin.Name),
uri: encryptedLogin.Uri && encryptedLogin.Uri !== '' ? cryptoService.decrypt(encryptedLogin.Uri) : null,
username: encryptedLogin.Username && encryptedLogin.Username !== '' ? cryptoService.decrypt(encryptedLogin.Username) : null,
password: encryptedLogin.Password && encryptedLogin.Password !== '' ? cryptoService.decrypt(encryptedLogin.Password) : null,
notes: encryptedLogin.Notes && encryptedLogin.Notes !== '' ? cryptoService.decrypt(encryptedLogin.Notes) : null
name: cryptoService.decrypt(encryptedLogin.Name, key),
uri: encryptedLogin.Uri && encryptedLogin.Uri !== '' ? cryptoService.decrypt(encryptedLogin.Uri, key) : null,
username: encryptedLogin.Username && encryptedLogin.Username !== '' ? cryptoService.decrypt(encryptedLogin.Username, key) : null,
password: encryptedLogin.Password && encryptedLogin.Password !== '' ? cryptoService.decrypt(encryptedLogin.Password, key) : null,
notes: encryptedLogin.Notes && encryptedLogin.Notes !== '' ? cryptoService.decrypt(encryptedLogin.Notes, key) : null,
key: encryptedLogin.Key
};
if (encryptedLogin.Folder) {
@ -39,6 +45,40 @@ angular
return login;
};
_service.decryptLoginPreview = function (encryptedCipher) {
if (!encryptedCipher) throw "encryptedCipher is undefined or null";
var key = null;
if (encryptedCipher.Key) {
key = cryptoService.rsaDecrypt(encryptedCipher.Key);
}
var login = {
id: encryptedCipher.Id,
folderId: encryptedCipher.FolderId,
favorite: encryptedCipher.Favorite,
name: decryptProperty(encryptedCipher.Data.Name, key, false),
username: decryptProperty(encryptedCipher.Data.Username, key, true)
};
try {
login.name = cryptoService.decrypt(encryptedCipher.Data.Name, key);
}
catch (err) {
login.name = '[error: cannot decrypt]';
}
try {
login.username = encryptedCipher.Data.Username && encryptedCipher.Data.Username !== '' ?
cryptoService.decrypt(encryptedCipher.Data.Username, key) : null;
}
catch (err) {
login.username = '[error: cannot decrypt]';
}
return login;
};
_service.decryptFolders = function (encryptedFolders) {
if (!encryptedFolders) throw "encryptedFolders is undefined or null";
@ -60,6 +100,30 @@ angular
};
};
_service.decryptFolderPreview = function (encryptedFolder) {
if (!encryptedFolder) throw "encryptedFolder is undefined or null";
return {
id: encryptedFolder.Id,
name: decryptProperty(encryptedFolder.Data.Name, null, false)
};
};
function decryptProperty(property, key, checkEmpty) {
if (checkEmpty && (!property || property === '')) {
return null;
}
try {
property = cryptoService.decrypt(property, key);
}
catch (err) {
property = '[error: cannot decrypt]';
}
return property;
}
_service.encryptLogins = function (unencryptedLogins, key) {
if (!unencryptedLogins) throw "unencryptedLogins is undefined or null";

View File

@ -5,7 +5,8 @@ angular
var _service = {},
_key,
_b64Key,
_privateKey;
_privateKey,
_publicKey;
_service.setKey = function (key) {
_key = key;
@ -64,25 +65,41 @@ angular
}
if ($sessionStorage.privateKey) {
_privateKey = forge.util.decode64($sessionStorage.privateKey);
var privateKeyBytes = forge.util.decode64($sessionStorage.privateKey);
_privateKey = forge.pki.privateKeyFromAsn1(forge.asn1.fromDer(privateKeyBytes));
}
return _privateKey;
};
_service.getPublicKey = function () {
if (_publicKey) {
return _publicKey;
}
var privateKey = _service.getPrivateKey();
if (!privateKey) {
return null;
}
_publicKey = forge.pki.setRsaPublicKey(privateKey.n, privateKey.e);
return _publicKey;
};
_service.clearKey = function () {
_key = _b64Key = null;
delete $sessionStorage.key;
};
_service.clearPrivateKey = function () {
_service.clearKeyPair = function () {
_privateKey = null;
_publicKey = null;
delete $sessionStorage.privateKey;
};
_service.clearKeys = function () {
_service.clearKey();
_service.clearPrivateKey();
_service.clearKeyPair();
};
_service.makeKey = function (password, salt, b64) {
@ -114,6 +131,10 @@ angular
});
};
_service.makeShareKey = function () {
return forge.random.getBytesSync(32);
};
_service.hashPassword = function (password, key) {
if (!key) {
key = _service.getKey();
@ -163,6 +184,18 @@ angular
return cipherString;
};
_service.rsaEncrypt = function (plainValue, publicKey) {
publicKey = publicKey || _service.getPublicKey();
if (!publicKey) {
throw 'Public key unavailable.';
}
var encryptedBytes = publicKey.encrypt(plainValue, 'RSA-OAEP', {
md: forge.md.sha256.create()
});
return forge.util.encode64(encryptedBytes);
}
_service.decrypt = function (encValue, key, outputEncoding) {
if (!_service.getKey() && !key) {
throw 'Encryption key unavailable.';
@ -208,6 +241,20 @@ angular
}
};
_service.rsaDecrypt = function (encValue, privateKey) {
privateKey = privateKey || _service.getPrivateKey();
if (!privateKey) {
throw 'Private key unavailable.';
}
var ctBytes = forge.util.decode64(encValue);
var decBytes = privateKey.decrypt(ctBytes, 'RSA-OAEP', {
md: forge.md.sha256.create()
});
return decBytes;
}
function computeMac(ct, iv, macKey) {
var hmac = forge.hmac.create();
hmac.start('sha256', macKey || _service.getMacKey());

View File

@ -1,7 +1,8 @@
angular
.module('bit.vault')
.controller('vaultController', function ($scope, $uibModal, apiService, $filter, cryptoService, authService, toastr, cipherService) {
.controller('vaultController', function ($scope, $uibModal, apiService, $filter, cryptoService, authService, toastr,
cipherService) {
$scope.logins = [];
$scope.folders = [];
@ -17,30 +18,11 @@
for (var i = 0; i < ciphers.Data.length; i++) {
if (ciphers.Data[i].Type === 0) {
var decFolder = {
id: ciphers.Data[i].Id
};
try { decFolder.name = cryptoService.decrypt(ciphers.Data[i].Data.Name); }
catch (err) { decFolder.name = '[error: cannot decrypt]'; }
var decFolder = cipherService.decryptFolderPreview(ciphers.Data[i]);
decFolders.push(decFolder);
}
else {
var decLogin = {
id: ciphers.Data[i].Id,
folderId: ciphers.Data[i].FolderId,
favorite: ciphers.Data[i].Favorite
};
try { decLogin.name = cryptoService.decrypt(ciphers.Data[i].Data.Name); }
catch (err) { decLogin.name = '[error: cannot decrypt]'; }
if (ciphers.Data[i].Data.Username) {
try { decLogin.username = cryptoService.decrypt(ciphers.Data[i].Data.Username); }
catch (err) { decLogin.username = '[error: cannot decrypt]'; }
}
var decLogin = cipherService.decryptLoginPreview(ciphers.Data[i]);
decLogins.push(decLogin);
}
}
@ -183,28 +165,18 @@
};
$scope.shareLogin = function (login) {
share(login.id, login.name, false);
};
$scope.shareFolder = function (folder) {
share(folder.id, folder.name, true);
};
function share(id, name, isFolder) {
var shareModel = $uibModal.open({
animation: true,
templateUrl: 'app/vault/views/vaultShare.html',
controller: 'vaultShareController',
templateUrl: 'app/vault/views/vaultShareLogin.html',
controller: 'vaultShareLoginController',
size: 'lg',
resolve: {
id: function () { return id; },
name: function () { return name; },
isFolder: function () { return isFolder; }
id: function () { return login.id; }
}
});
shareModel.result.then(function (result) {
});
}
};
});

View File

@ -1,21 +0,0 @@
angular
.module('bit.vault')
.controller('vaultShareController', function ($scope, apiService, $uibModalInstance, cryptoService, cipherService,
id, name, isFolder, $analytics) {
$analytics.eventTrack('vaultShareController', { category: 'Modal' });
$scope.cipher = {
id: id,
name: name,
isFolder: isFolder
};
$scope.sharePromise = null;
$scope.share = function () {
$uibModalInstance.close({});
};
$scope.close = function () {
$uibModalInstance.dismiss('cancel');
};
});

View File

@ -0,0 +1,33 @@
angular
.module('bit.vault')
.controller('vaultShareLoginController', function ($scope, apiService, $uibModalInstance, cryptoService, cipherService,
id, $analytics) {
$analytics.eventTrack('vaultShareLoginController', { category: 'Modal' });
apiService.logins.get({
id: id
}, function (login) {
$scope.login = cipherService.decryptLogin(login);
});
$scope.enablePromise = null;
$scope.enable = function () {
var shareKey = cryptoService.makeShareKey();
var encLogin = cipherService.encryptLogin($scope.login, shareKey);
encLogin.key = cryptoService.rsaEncrypt(shareKey);
$scope.enablePromise = apiService.logins.put({ id: $scope.login.id }, encLogin, function (login) {
$scope.login = cipherService.decryptLogin(login);
}).$promise;
};
$scope.sharePromise = null;
$scope.share = function () {
$uibModalInstance.close({});
};
$scope.close = function () {
$uibModalInstance.dismiss('cancel');
};
});

View File

@ -1,13 +1,27 @@
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="editFolderModelLabel">
<i class="fa fa-user-plus"></i>
<span ng-if="!cipher.isFolder">Share Login</span>
<span ng-if="cipher.isFolder">Share Folder</span>
<small>{{cipher.name}}</small>
<i class="fa fa-user-plus"></i> Share Login <small>{{login.name}}</small>
</h4>
</div>
<form name="shareForm" ng-submit="shareForm.$valid && share()" api-form="savePromise">
<form name="enableForm" ng-submit="enableForm.$valid && enable()" api-form="savePromise" ng-if="!login.key">
<div class="modal-body">
<div class="callout callout-danger validation-errors" ng-show="enableForm.$errors">
<h4>Errors have occured</h4>
<ul>
<li ng-repeat="e in enableForm.$errors">{{e}}</li>
</ul>
</div>
Sharing is not enabled.
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="enableForm.$loading">
<i class="fa fa-refresh fa-spin loading-icon" ng-show="enableForm.$loading"></i>Enable Sharing
</button>
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
</div>
</form>
<form name="shareForm" ng-submit="shareForm.$valid && share()" api-form="savePromise" ng-if="login.key">
<div class="modal-body">
<div class="callout callout-danger validation-errors" ng-show="shareForm.$errors">
<h4>Errors have occured</h4>
@ -19,21 +33,15 @@
<div class="col-md-6">
<div class="form-group" show-errors>
<label for="email">Recipient Email address</label> <span>*</span>
<input type="email" id="email" name="Email" ng-model="cipher.email" class="form-control"
<input type="email" id="email" name="Email" ng-model="share.email" class="form-control"
placeholder="bob@example.com" required api-field />
</div>
<div class="checkbox">
<label>
<input type="checkbox" ng-model="cipher.readonly" name="Edit" />
<input type="checkbox" ng-model="share.readonly" name="Edit" />
Recipient can edit?
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" ng-model="cipher.reshare" name="Reshare" />
Recipient can re-share with others?
</label>
</div>
</div>
<div class="col-md-6">

View File

@ -115,7 +115,7 @@
<script src="app/vault/vaultAddLoginController.js"></script>
<script src="app/vault/vaultEditFolderController.js"></script>
<script src="app/vault/vaultAddFolderController.js"></script>
<script src="app/vault/vaultShareController.js"></script>
<script src="app/vault/vaultShareLoginController.js"></script>
<script src="app/shared/sharedModule.js"></script>
<script src="app/shared/sharedController.js"></script>