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:
parent
63c136a1ff
commit
1ed86899bb
@ -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: {} },
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -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');
|
||||
};
|
||||
});
|
33
src/app/vault/vaultShareLoginController.js
Normal file
33
src/app/vault/vaultShareLoginController.js
Normal 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');
|
||||
};
|
||||
});
|
@ -1,13 +1,27 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</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">
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user