Merge branch 'master' into fix-docx-file-handling

This commit is contained in:
binsky 2023-05-02 20:18:27 +02:00
commit c28afd1b0b
13 changed files with 401 additions and 172 deletions

View file

@ -58,14 +58,17 @@ return [
['name' => 'share#getPendingRequests', 'url' => '/api/v2/sharing/pending', 'verb' => 'GET'],
['name' => 'share#deleteShareRequest', 'url' => '/api/v2/sharing/decline/{share_request_id}', 'verb' => 'DELETE'],
['name' => 'share#getVaultItems', 'url' => '/api/v2/sharing/vault/{vault_guid}/get', 'verb' => 'GET'],
['name' => 'share#getVaultAclEntries', 'url' => '/api/v2/sharing/vault/{vault_guid}/acl', 'verb' => 'GET'],
['name' => 'share#createPublicShare', 'url' => '/api/v2/sharing/public', 'verb' => 'POST'],
['name' => 'share#getPublicCredentialData', 'url' => '/api/v2/sharing/credential/{credential_guid}/public', 'verb' => 'GET'],
['name' => 'share#unshareCredential', 'url' => '/api/v2/sharing/credential/{item_guid}', 'verb' => 'DELETE'],
['name' => 'share#unshareCredentialFromUser', 'url' => '/api/v2/sharing/credential/{item_guid}/{user_id}', 'verb' => 'DELETE'],
['name' => 'share#getRevisions', 'url' => '/api/v2/sharing/credential/{item_guid}/revisions', 'verb' => 'GET'],
['name' => 'share#getItemAcl', 'url' => '/api/v2/sharing/credential/{item_guid}/acl', 'verb' => 'GET'],
['name' => 'share#uploadFile', 'url' => '/api/v2/sharing/credential/{item_guid}/file', 'verb' => 'POST'],
['name' => 'share#getFile', 'url' => '/api/v2/sharing/credential/{item_guid}/file/{file_guid}', 'verb' => 'GET'],
['name' => 'share#updateSharedCredentialACL', 'url' => '/api/v2/sharing/credential/{item_guid}/acl', 'verb' => 'PATCH'],
['name' => 'share#updateSharedCredentialACLSharedKey', 'url' => '/api/v2/sharing/credential/{item_guid}/acl/shared_key', 'verb' => 'PATCH'],
['name' => 'internal#getAppVersion', 'url' => '/api/v2/version', 'verb' => 'GET'],
//Settings

View file

@ -93,6 +93,10 @@ class FileController extends ApiController {
return new JSONResponse(array('ok' => empty($failed_file_ids), 'failed' => $failed_file_ids));
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*/
public function updateFile($file_id, $file_data, $filename) {
try {
$file = $this->fileService->getFile($file_id, $this->userId);

View file

@ -368,6 +368,20 @@ class ShareController extends ApiController {
}
}
/**
* Obtains the list of acl entries for credentials shared with this vault
*
* @NoAdminRequired
* @NoCSRFRequired
*/
public function getVaultAclEntries($vault_guid) {
try {
return new JSONResponse($this->shareService->getVaultAclList($this->userId->getUID(), $vault_guid));
} catch (\Exception $ex) {
return new NotFoundResponse();
}
}
/**
* @param $share_request_id
* @return JSONResponse
@ -476,13 +490,55 @@ class ShareController extends ApiController {
} catch (\Exception $e) {
return new NotFoundJSONResponse();
}
// $this->userId does not exist for anonymous share link downloads
$userId = ($this->userId) ? $this->userId->getUID() : null;
$acl = $this->shareService->getACL($userId, $credential->getGuid());
if (!$acl->hasPermission(SharingACL::FILES)) {
return new NotFoundJSONResponse();
} else {
return $this->fileService->getFileByGuid($file_guid);
if ($acl->hasPermission(SharingACL::FILES)) {
// get file by guid and check if it is owned by the owner of the shared credential
return $this->fileService->getFileByGuid($file_guid, $credential->getUserId());
}
return new NotFoundJSONResponse();
}
/**
* @param $item_guid
* @param $data
* @param $filename
* @param $mimetype
* @param $size
* @return DataResponse|NotFoundJSONResponse|JSONResponse
* @throws \Exception
* @NoAdminRequired
* @NoCSRFRequired
*/
public function uploadFile($item_guid, $data, $filename, $mimetype, $size) {
try {
$credential = $this->credentialService->getCredentialByGUID($item_guid);
} catch (\Exception $e) {
return new NotFoundJSONResponse();
}
// only check acl, if the uploading user is not the credential owner
if ($credential->getUserId() != $this->userId->getUID()) {
$acl = $this->shareService->getACL($this->userId->getUID(), $credential->getGuid());
if (!$acl->hasPermission(SharingACL::FILES)) {
return new DataResponse(['msg' => 'Not authorized'], Http::STATUS_UNAUTHORIZED);
}
}
$file = array(
'filename' => $filename,
'size' => $size,
'mimetype' => $mimetype,
'file_data' => $data,
'user_id' => $credential->getUserId()
);
// save the file with the id of the user that owns the credential
return new JSONResponse($this->fileService->createFile($file, $credential->getUserId()));
}
/**
@ -515,4 +571,18 @@ class ShareController extends ApiController {
}
}
/**
* @param $item_guid
* @param $shared_key
* @return JSONResponse
* @NoAdminRequired
* @NoCSRFRequired
*/
public function updateSharedCredentialACLSharedKey($item_guid, $shared_key) {
/** @var SharingACL $acl */
$acl = $this->shareService->getACL($this->userId->getUID(), $item_guid);
$acl->setSharedKey($shared_key);
return new JSONResponse($this->shareService->updateCredentialACL($acl)->jsonSerialize());
}
}

View file

@ -58,6 +58,7 @@ class TranslationController extends ApiController {
'credential.recovered' => $this->trans->t('Credential recovered'),
'credential.destroyed' => $this->trans->t('Credential destroyed'),
'error.loading.file.perm' => $this->trans->t('Error downloading file, you probably have insufficient permissions'),
'error.general' => $this->trans->t('An error occurred'),
// js/app/controllers/edit_credential.js
'invalid.qr' => $this->trans->t('Invalid QR code'),

View file

@ -72,20 +72,18 @@
try {
if (!_credential.shared_key) {
_credential = CredentialService.decryptCredential(angular.copy(_credential));
} else {
var enc_key = EncryptService.decryptString(_credential.shared_key);
_credential = ShareService.decryptSharedCredential(angular.copy(_credential), enc_key);
}
_credential.tags_raw = _credential.tags;
} catch (e) {
NotificationService.showNotification($translate.instant('error.decrypt'), 5000);
console.error(e);
//$rootScope.$broadcast('logout');
//SettingsService.setSetting('defaultVaultPass', null);
//.setSetting('defaultVault', null);
//$location.path('/')
}
_credentials[i] = _credential;
}
@ -181,10 +179,10 @@
private_key = ShareService.rsaPrivateKeyFromPEM(private_key);
/** global: forge */
crypted_shared_key = private_key.decrypt(forge.util.decode64(crypted_shared_key));
crypted_shared_key = EncryptService.encryptString(crypted_shared_key);
const decrypted_shared_key = private_key.decrypt(forge.util.decode64(crypted_shared_key));
const vault_key_encrypted_shared_key = EncryptService.encryptString(decrypted_shared_key);
ShareService.saveSharingRequest(share_request, crypted_shared_key).then(function () {
ShareService.saveSharingRequest(share_request, vault_key_encrypted_shared_key).then(function () {
var idx = $scope.incoming_share_requests.indexOf(share_request);
$scope.incoming_share_requests.splice(idx, 1);
var active_share_requests = false;
@ -261,54 +259,62 @@
};
var notification;
$scope.deleteCredential = function (credential) {
var _credential = angular.copy(credential);
try {
_credential = CredentialService.decryptCredential(_credential);
} catch (e) {
}
$scope.deleteCredential = function (decrypted_credential) {
let _credential = angular.copy(decrypted_credential);
_credential.delete_time = new Date().getTime() / 1000;
for (var i = 0; i < $scope.active_vault.credentials.length; i++) {
if ($scope.active_vault.credentials[i].credential_id === credential.credential_id) {
$scope.active_vault.credentials[i].delete_time = _credential.delete_time;
}
}
$scope.closeSelected();
if (notification) {
NotificationService.hideNotification(notification);
}
var key = CredentialService.getSharedKeyFromCredential(_credential);
CredentialService.updateCredential(_credential, false, key).then(function () {
const key = CredentialService.getSharedKeyFromCredential(_credential);
CredentialService.updateCredential(_credential, false, key).then(function (response) {
decrypted_credential.delete_time = _credential.delete_time;
for (let i = 0; i < $scope.active_vault.credentials.length; i++) {
if ($scope.active_vault.credentials[i].credential_id === _credential.credential_id) {
$scope.active_vault.credentials[i].delete_time = _credential.delete_time;
}
}
notification = NotificationService.showNotification($translate.instant('credential.deleted'), 5000);
}, function (error) {
if (error.data.msg) {
NotificationService.showNotification(error.data.msg, 5000);
} else {
NotificationService.showNotification($translate.instant('error.general'), 5000);
}
});
};
$scope.recoverCredential = function (credential) {
var _credential = angular.copy(credential);
try {
_credential = CredentialService.decryptCredential(_credential);
} catch (e) {
}
for (var i = 0; i < $scope.active_vault.credentials.length; i++) {
if ($scope.active_vault.credentials[i].credential_id === credential.credential_id) {
$scope.active_vault.credentials[i].delete_time = 0;
}
}
$scope.recoverCredential = function (decrypted_credential) {
let _credential = angular.copy(decrypted_credential);
_credential.delete_time = 0;
$scope.closeSelected();
if (notification) {
NotificationService.hideNotification(notification);
}
var key = CredentialService.getSharedKeyFromCredential(_credential);
CredentialService.updateCredential(_credential, false, key).then(function () {
const key = CredentialService.getSharedKeyFromCredential(_credential);
CredentialService.updateCredential(_credential, false, key).then(function (response) {
decrypted_credential.delete_time = 0;
for (let i = 0; i < $scope.active_vault.credentials.length; i++) {
if ($scope.active_vault.credentials[i].credential_id === _credential.credential_id) {
$scope.active_vault.credentials[i].delete_time = 0;
}
}
NotificationService.showNotification($translate.instant('credential.recovered'), 5000);
}, function (error) {
if (error.data.msg) {
NotificationService.showNotification(error.data.msg, 5000);
} else {
NotificationService.showNotification($translate.instant('error.general'), 5000);
}
});
};
$scope.destroyCredential = function (credential) {
var _credential = angular.copy(credential);
const _credential = angular.copy(credential);
CredentialService.destroyCredential(_credential.guid).then(function () {
for (var i = 0; i < $scope.active_vault.credentials.length; i++) {
if ($scope.active_vault.credentials[i].credential_id === credential.credential_id) {
@ -317,6 +323,12 @@
break;
}
}
}, function (error) {
if (error.data.msg) {
NotificationService.showNotification(error.data.msg, 5000);
} else {
NotificationService.showNotification($translate.instant('error.general'), 5000);
}
});
};

View file

@ -161,26 +161,22 @@
$scope.selected_field_type = 'text';
_field.secret = (_field.field_type === 'password');
if(_field.field_type === 'file'){
var key = false;
var _file = $scope.new_custom_field.value;
if (!$scope.storedCredential.hasOwnProperty('acl') && $scope.storedCredential.hasOwnProperty('shared_key')) {
const key = CredentialService.getSharedKeyFromCredential($scope.storedCredential);
const file = $scope.new_custom_field.value;
if ($scope.storedCredential.shared_key) {
key = EncryptService.decryptString(angular.copy($scope.storedCredential.shared_key));
}
}
if ($scope.storedCredential.hasOwnProperty('acl')) {
key = EncryptService.decryptString(angular.copy($scope.storedCredential.acl.shared_key));
}
FileService.uploadFile(_file, key).then(function (result) {
const callback = function (result) {
delete result.file_data;
result.filename = EncryptService.decryptString(result.filename, key);
_field.value = result;
$scope.storedCredential.custom_fields.push(_field);
$scope.new_custom_field = angular.copy(_customField);
});
};
if (key) {
ShareService.uploadSharedFile($scope.storedCredential, file, key).then(callback);
} else {
FileService.uploadFile(file).then(callback);
}
} else {
$scope.storedCredential.custom_fields.push(_field);
$scope.new_custom_field = angular.copy(_customField);
@ -229,11 +225,17 @@
data: file.data
};
FileService.uploadFile(_file, key).then(function (result) {
const callback = function (result) {
delete result.file_data;
result.filename = EncryptService.decryptString(result.filename, key);
$scope.storedCredential.files.push(result);
});
};
if (key) {
ShareService.uploadSharedFile($scope.storedCredential, _file, key).then(callback);
} else {
FileService.uploadFile(_file).then(callback);
}
$scope.$digest();
};
@ -329,31 +331,17 @@
$scope.updateExistingListWithCredential(new_cred);
});
} else {
var key, _credential;
if (!$scope.storedCredential.hasOwnProperty('acl') && $scope.storedCredential.hasOwnProperty('shared_key')) {
if ($scope.storedCredential.shared_key) {
key = EncryptService.decryptString(angular.copy($scope.storedCredential.shared_key));
}
}
if ($scope.storedCredential.hasOwnProperty('acl')) {
key = EncryptService.decryptString(angular.copy($scope.storedCredential.acl.shared_key));
}
let _credential = angular.copy($scope.storedCredential);
const key = CredentialService.getSharedKeyFromCredential($scope.storedCredential);
if (key) {
_credential = ShareService.encryptSharedCredential($scope.storedCredential, key);
} else {
_credential = angular.copy($scope.storedCredential);
}
delete _credential.shared_key;
var _useKey = (key != null);
var regex = /(<([^>]+)>)/ig;
const _useKey = (key != null);
const regex = /(<([^>]+)>)/ig;
if(_credential.description && _credential.description !== "") {
_credential.description = _credential.description.replace(regex, "");
}
@ -373,17 +361,17 @@
if (!credential.shared_key) {
credential = CredentialService.decryptCredential(credential);
} else {
var enc_key = EncryptService.decryptString(credential.shared_key);
const enc_key = CredentialService.getSharedKeyFromCredential($scope.storedCredential);
credential = ShareService.decryptSharedCredential(credential, enc_key);
}
credential.tags_raw = credential.tags;
var found=false;
var credList=$rootScope.vaultCache[$scope.active_vault.guid].credentials;
for (var i = 0; i < credList.length; i++) {
let found = false;
let credList = $rootScope.vaultCache[$scope.active_vault.guid].credentials;
for (let i = 0; i < credList.length; i++) {
if (credList[i].credential_id === credential.credential_id) {
$rootScope.vaultCache[$scope.active_vault.guid].credentials[i]=credential;
found=true;
$rootScope.vaultCache[$scope.active_vault.guid].credentials[i] = credential;
found = true;
}
}
@ -391,7 +379,6 @@
$rootScope.vaultCache[$scope.active_vault.guid].credentials.push(credential);
}
$rootScope.$broadcast('push_decrypted_credential_to_list', credential);
} catch (e) {
NotificationService.showNotification($translate.instant('error.decrypt'), 5000);
console.log(e);

View file

@ -73,7 +73,7 @@
}
});
var key_strengths = [
const key_strengths = [
'password.poor',
'password.poor',
'password.weak',
@ -86,7 +86,7 @@
$scope.required_score = {'strength': translation};
});
var btn_txt = $translate.instant('bookmarklet.text');
const btn_txt = $translate.instant('bookmarklet.text');
var http = location.protocol, slashes = http.concat("//"),
host = slashes.concat(window.location.hostname + ":" + window.location.port),
complete = host + location.pathname;
@ -94,7 +94,7 @@
$scope.saveVaultSettings = function () {
var _vault = $scope.active_vault;
let _vault = $scope.active_vault;
_vault.name = $scope.new_vault_name;
_vault.vault_settings = angular.copy($scope.vault_settings);
VaultService.updateVault(_vault).then(function () {
@ -236,19 +236,9 @@
done: 0,
total: _selected_credentials.length
};
var changeCredential = function (index, oldVaultPass, newVaultPass) {
var usedKey = oldVaultPass;
if (_selected_credentials[index].hasOwnProperty('shared_key')) {
if (_selected_credentials[index].shared_key) {
usedKey = EncryptService.decryptString(angular.copy(_selected_credentials[index].shared_key), oldVaultPass);
}
}
CredentialService.reencryptCredential(_selected_credentials[index].guid, usedKey, newVaultPass).progress(function (data) {
$scope.cur_state = data;
}).then(function () {
var percent = index / _selected_credentials.length * 100;
const changeCredential = function (index, oldVaultPass, newVaultPass) {
const next_credential_callback = function () {
const percent = index / _selected_credentials.length * 100;
$scope.change_pw = {
percent: percent,
done: index + 1,
@ -260,14 +250,31 @@
vault.private_sharing_key = EncryptService.decryptString(angular.copy(vault.private_sharing_key), oldVaultPass);
vault.private_sharing_key = EncryptService.encryptString(vault.private_sharing_key, newVaultPass);
VaultService.updateSharingKeys(vault).then(function () {
$rootScope.$broadcast('logout');
NotificationService.showNotification($translate.instant('login.new.pass'), 5000);
VaultService.reEncryptACLSharingKeys(vault, oldVaultPass, newVaultPass, EncryptService).then(function () {
$rootScope.$broadcast('logout');
NotificationService.showNotification($translate.instant('login.new.pass'), 5000);
});
});
}
});
};
if (_selected_credentials[index].shared_key) {
// only re-encrypt the shared key, if the credential is shared and not encrypted with the vault key like default credentials
CredentialService.getCredential(_selected_credentials[index].guid).then((function (credential) {
const decrypted_shared_key = EncryptService.decryptString(angular.copy(credential.shared_key), oldVaultPass);
let _credential = angular.copy(credential);
_credential.set_share_key = true;
_credential.skip_revision = true;
_credential.shared_key = EncryptService.encryptString(decrypted_shared_key, newVaultPass);
CredentialService.updateCredential(_credential, true).then(next_credential_callback);
}));
} else {
CredentialService.reencryptCredential(_selected_credentials[index].guid, oldVaultPass, newVaultPass).progress(function (data) {
$scope.cur_state = data;
}).then(next_credential_callback);
}
};
changeCredential(0, VaultService.getActiveVault().vaultKey, newVaultPass);
});
};
@ -276,18 +283,23 @@
$scope.delete_vault = function () {
if ($scope.confirm_vault_delete && $scope.delete_vault_password === VaultService.getActiveVault().vaultKey) {
getCurrentVaultCredentials(function (vault) {
var credentials = vault.credentials;
const credentials = vault.credentials;
$scope.remove_pw = {
percent: 0,
done: 0,
total: vault.credentials.length,
};
var file_ids = [];
const file_ids = [];
for (const credential of credentials) {
var decryptedFiles = JSON.parse(EncryptService.decryptString(angular.copy(credential.files), VaultService.getActiveVault().vaultKey));
for (const file of decryptedFiles) {
file_ids.push(file.file_id);
try {
const enc_key = CredentialService.getSharedKeyFromCredential(credential);
const decryptedFiles = JSON.parse(EncryptService.decryptString(angular.copy(credential.files), enc_key));
for (const file of decryptedFiles) {
file_ids.push(file.file_id);
}
} catch (e) {
console.error(e);
}
}

View file

@ -278,7 +278,7 @@
$scope.$digest();
})
.then(function (result) {
$scope.share_settings.cypher_progress.times.push({
$scope.share_settings.cypher_progress.times.push({
time: ((new Date().getTime() / 1000) - start),
user: data[0].user_id
});
@ -320,7 +320,7 @@
$scope.share_settings.upload_progress.total = 0;
//Credential is already shared
if ($scope.storedCredential.shared_key && $scope.storedCredential.shared_key !== '' && $scope.storedCredential.shared_key !== null) {
var enc_key = EncryptService.decryptString(angular.copy($scope.storedCredential.shared_key));
var enc_key = EncryptService.decryptString(angular.copy($scope.storedCredential.shared_key));
if ($scope.share_settings.linkSharing.enabled) {
var expire_time = new Date(angular.copy($scope.share_settings.linkSharing.settings.expire_time)).getTime() / 1000;
var shareObj = {
@ -355,28 +355,31 @@
$scope.sharing_complete = true;
} else {
ShareService.generateSharedKey(20).then(function (key) {
ShareService.generateSharedKey(20).then(function (generated_shared_key) {
// copy complete credential to encryptedSharedCredential where it can be safely re-encrypted
var encryptedSharedCredential = angular.copy($scope.storedCredential);
var old_key = VaultService.getActiveVault().vaultKey;
var vault_key = VaultService.getActiveVault().vaultKey;
CredentialService.reencryptCredential(encryptedSharedCredential.guid, old_key, key).progress(function () {
}).then(function (data) {
var _credential = data.cryptogram;
_credential.set_share_key = true;
_credential.skip_revision = true;
_credential.shared_key = EncryptService.encryptString(key);
CredentialService.updateCredential(_credential, true).then(function () {
$scope.storedCredential.shared_key = _credential.shared_key;
NotificationService.showNotification($translate.instant('credential.shared'), 4000);
$scope.sharing_complete = true;
});
});
CredentialService
.reencryptCredential(encryptedSharedCredential.guid, vault_key, generated_shared_key)
.progress(function () {})
.then(function (data) {
var _credential = data.cryptogram;
_credential.set_share_key = true;
_credential.skip_revision = true;
_credential.shared_key = EncryptService.encryptString(generated_shared_key);
CredentialService.updateCredential(_credential, true).then(function () {
$scope.storedCredential.shared_key = _credential.shared_key;
NotificationService.showNotification($translate.instant('credential.shared'), 4000);
$scope.sharing_complete = true;
});
});
var list = $scope.share_settings.credentialSharedWithUserAndGroup;
for (var i = 0; i < list.length; i++) {
if (list[i].type === "user") {
$scope.applyShareToUser(list[i], key);
$scope.applyShareToUser(list[i], generated_shared_key);
}
}
@ -390,7 +393,7 @@
expire_views: $scope.share_settings.linkSharing.settings.expire_views
};
ShareService.createPublicSharedCredential(shareObj).then(function () {
var hash = window.btoa($scope.storedCredential.guid + '<::>' + key);
var hash = window.btoa($scope.storedCredential.guid + '<::>' + generated_shared_key);
$scope.share_link = getShareLink(hash);
});
}

View file

@ -85,17 +85,13 @@
return _encryptedFields;
},
updateCredential: function (credential, skipEncryption, key) {
var _credential = angular.copy(credential);
let _credential = angular.copy(credential);
if (!skipEncryption) {
for (var i = 0; i < _encryptedFields.length; i++) {
var field = _encryptedFields[i];
var fieldValue = angular.copy(credential[field]);
_credential[field] = EncryptService.encryptString(JSON.stringify(fieldValue), key);
}
_credential = this.encryptCredential(credential, key);
}
_credential.expire_time = new Date(angular.copy(credential.expire_time)).getTime() / 1000;
var queryUrl = OC.generateUrl('apps/passman/api/v2/credentials/' + credential.guid);
const queryUrl = OC.generateUrl('apps/passman/api/v2/credentials/' + credential.guid);
return $http.patch(queryUrl, _credential).then(function (response) {
if (response.data) {
return response.data;
@ -145,13 +141,13 @@
}
} catch (e) {
console.error('Error decrypting credential:', credential);
console.error('Error decrypting credential field:', field);
throw e;
}
try {
credential[field] = JSON.parse(field_decrypted_value);
} catch (e) {
console.warn('Field' + field + ' in ' + credential.label + ' could not be parsed! Value:' + fieldValue);
}
}
@ -215,23 +211,39 @@
var promise_credential_update = function () {
service.getCredential(credential_guid).then((function (credential) {
this.parent.plain_credential = service.decryptCredential(credential, this.parent.old_password);
var tmp = angular.copy(this.parent.plain_credential);
var plain_credential = angular.copy(this.parent.plain_credential);
if (tmp.hasOwnProperty('shared_key') && tmp.shared_key !== null && tmp.shared_key !== '' && !skipSharingKey) {
var shared_key = EncryptService.decryptString(angular.copy(tmp.shared_key)).trim();
tmp.shared_key = EncryptService.encryptString(angular.copy(shared_key), this.parent.new_password);
tmp.set_share_key = true;
tmp.skip_revision = true;
this.parent.new_password = shared_key;
if (
plain_credential.hasOwnProperty('shared_key') &&
plain_credential.shared_key !== null &&
plain_credential.shared_key !== '' &&
!skipSharingKey
) {
// re-encrypt the credential.shared_key with the new password
// (e.g. re-encrypt from vault_key to generated_shared_key)
const decrypted_credential_shared_key = EncryptService.decryptString(angular.copy(plain_credential.shared_key)).trim();
plain_credential.shared_key = EncryptService.encryptString(
angular.copy(decrypted_credential_shared_key),
this.parent.new_password
);
plain_credential.set_share_key = true;
plain_credential.skip_revision = true;
// todo: temporary comment out this Brantje code line as is looks pointless to set the
// new encryption key to the decrypted_credential_shared_key
// this.parent.new_password = decrypted_credential_shared_key;
}
this.parent.new_credential_cryptogram = service.encryptCredential(tmp, this.parent.new_password);
// before: re-encryption with the now out-commented decrypted_credential_shared_key if the credential has one
// now: re-encryption with the original parent.new_password (e.g. generated_shared_key)
this.parent.new_credential_cryptogram = service.encryptCredential(plain_credential, this.parent.new_password);
this.call_progress(new progress_datatype(1, 2, 'credential'));
// Save data
this.parent.new_credential_cryptogram.skip_revision = true;
service.updateCredential(this.parent.new_credential_cryptogram, true).then((function () {
this.call_progress(new progress_datatype(2, 2, 'credential'));
// transfer plain and encrypted credential to the next promise in the complete re-encryption task
this.call_then({
plain_text: this.parent.plain_credential,
cryptogram: this.parent.new_credential_cryptogram
@ -241,36 +253,97 @@
};
var promise_files_update = function () {
// Add the double of the files so we take encryption phase and upload to the server into the math
this.total = this.parent.plain_credential.files.length * 2; // Binded on credential finish upload
this.current = 0;
for (var i = 0; i < this.parent.plain_credential.files.length; i++) {
var _file = this.parent.plain_credential.files[i];
/* jshint ignore:start */
FileService.getFile(_file).then((function (fileData) {
//Decrypt with old key
fileData.filename = EncryptService.decryptString(fileData.filename, this.parent.old_password);
fileData.file_data = EncryptService.decryptString(fileData.file_data, this.parent.old_password);
this.current++;
this.call_progress(new progress_datatype(this.current, this.total, 'files'));
FileService.updateFile(fileData, this.parent.new_password).then((function () {
this.current++;
this.call_progress(new progress_datatype(this.current, this.total, 'files'));
if (this.current === this.total) {
this.call_then('All files has been updated');
}
}).bind(this));
}).bind(this));
/* jshint ignore:end */
}
if (this.parent.plain_credential.files.length === 0) {
this.call_progress(new progress_datatype(0, 0, 'files'));
this.call_then("No files to update");
return;
}
this.total = this.parent.plain_credential.files.length;
this.current = 0;
const files_workload = function () {
const check_next_callback = function () {
this.current++;
this.call_progress(new progress_datatype(this.current, this.total, 'files'));
if (this.current === this.total) {
this.call_then('All files has been updated');
} else {
setTimeout(files_workload.bind(this), 1);
}
};
const _file = this.parent.plain_credential.files[this.current];
/* jshint ignore:start */
FileService.getFile(_file).then((function (fileData) {
try {
//Decrypt with old key
fileData.filename = EncryptService.decryptString(fileData.filename, this.parent.old_password);
fileData.file_data = EncryptService.decryptString(fileData.file_data, this.parent.old_password);
FileService.updateFile(fileData, this.parent.new_password).then((function () {
check_next_callback.bind(this)();
}).bind(this));
} catch (e) {
console.error(e);
console.error('Failed to re-encrypt file. It seems to be corrupt.', _file);
check_next_callback.bind(this)();
}
}).bind(this));
/* jshint ignore:end */
};
setTimeout(files_workload.bind(this), 1);
};
var promise_custom_field_files_update = function () {
if (this.parent.plain_credential.custom_fields.length === 0) {
this.call_progress(new progress_datatype(0, 0, 'custom_field_files'));
this.call_then("No custom field files to update");
return;
}
this.total = this.parent.plain_credential.custom_fields.length;
console.log("total custom_field_files_update = " + this.total);
this.current = 0;
const custom_field_workload = function () {
const check_next_callback = function () {
this.current++;
this.call_progress(new progress_datatype(this.current, this.total, 'custom_field_files'));
if (this.current === this.total) {
this.call_then('All custom field files has been updated');
} else {
setTimeout(custom_field_workload.bind(this), 1);
}
};
if (this.parent.plain_credential.custom_fields[this.current].field_type !== 'file') {
check_next_callback.bind(this)();
return;
}
const _file = this.parent.plain_credential.custom_fields[this.current].value;
/* jshint ignore:start */
FileService.getFile(_file).then((function (fileData) {
try {
//Decrypt with old key
fileData.filename = EncryptService.decryptString(fileData.filename, this.parent.old_password);
fileData.file_data = EncryptService.decryptString(fileData.file_data, this.parent.old_password);
FileService.updateFile(fileData, this.parent.new_password).then((function () {
check_next_callback.bind(this)();
}).bind(this));
} catch (e) {
console.error(e);
console.error('Failed to re-encrypt custom field file. It seems to be corrupt.', _file);
check_next_callback.bind(this)();
}
}).bind(this));
/* jshint ignore:end */
};
setTimeout(custom_field_workload.bind(this), 1);
};
var promise_revisions_update = function () {
@ -287,12 +360,13 @@
this.call_then("No history to update");
return;
}
var _revision = revisions[this.current];
//Decrypt!
_revision.credential_data = service.decryptCredential(_revision.credential_data, this.parent.old_password);
_revision.credential_data = service.encryptCredential(_revision.credential_data, this.parent.new_password);
this.current++;
var _revision = revisions[this.current];
const decrypted_revision_credential_data = service.decryptCredential(_revision.credential_data, this.parent.old_password);
_revision.credential_data = service.encryptCredential(decrypted_revision_credential_data, this.parent.new_password);
this.current++;
this.call_progress(new progress_datatype(this.current + this.upload, this.total, 'revisions'));
service.updateRevision(_revision).then((function () {
@ -344,6 +418,18 @@
}
});
master_promise.promises++;
/** global: C_Promise */
(new C_Promise(promise_custom_field_files_update, new password_data())).progress(function (data) {
master_promise.call_progress(data);
}).then(function () {
console.warn("End custom field files update");
master_promise.promises--;
if (master_promise.promises === 0) {
master_promise.call_then(master_promise.credential_data);
}
});
master_promise.promises++;
/** global: C_Promise */
(new C_Promise(promise_revisions_update, new password_data())).progress(function (data) {

View file

@ -160,13 +160,27 @@
});
},
downloadSharedFile: function (credential, file) {
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/credential/' + credential.guid + '/file/' + file.guid);
const queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/credential/' + credential.guid + '/file/' + file.guid);
return $http.get(queryUrl).then(function (response) {
if (response.data) {
return response.data;
}
});
},
uploadSharedFile: function (credential, file, key) {
const queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/credential/' + credential.guid + '/file');
let _file = angular.copy(file);
_file.filename = EncryptService.encryptString(_file.filename, key);
const data = EncryptService.encryptString(angular.copy(file.data), key);
_file.data = data;
return $http.post(queryUrl, _file).then(function (response) {
if (response.data) {
return response.data;
} else {
return response;
}
});
},
encryptSharedCredential: function (credential, sharedKey) {
var _credential = angular.copy(credential);
_credential.shared_key = EncryptService.encryptString(sharedKey);

View file

@ -122,6 +122,30 @@
}
});
},
reEncryptACLSharingKeys: function (vault, oldVaultPass, newVaultPass, EncryptService) {
const queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/vault/' + vault.guid + '/acl');
return $http.get(queryUrl).then(function (response) {
if (response.data) {
const updateACLSharingKey = function (index) {
let acl = response.data[index];
const decrypted_shared_key = EncryptService.decryptString(angular.copy(acl.shared_key), oldVaultPass);
acl.shared_key = EncryptService.encryptString(decrypted_shared_key, newVaultPass);
const patchUrl = OC.generateUrl('apps/passman/api/v2/sharing/credential/' + acl.item_guid + '/acl/shared_key');
$http.patch(patchUrl, {
shared_key: acl.shared_key
}).then(function () {
if (index < response.data.length - 1) {
return updateACLSharingKey(index + 1);
}
});
};
if (response.data[0]) {
return updateACLSharingKey(0);
}
}
});
},
deleteVault: function (vault, file_ids) {
var queryUrl = OC.generateUrl('apps/passman/api/v2/vaults/' + vault.guid);
var deleteFilesUrl = OC.generateUrl('apps/passman/api/v2/files/delete');

View file

@ -273,6 +273,17 @@ class ShareService {
return $this->sharingACL->getCredentialAclList($item_guid);
}
/**
* Get the access control list by vault guid
*
* @param string $user_id
* @param string $vault_guid
* @return Entity[]
*/
public function getVaultAclList(string $user_id, string $vault_guid) {
return $this->sharingACL->getVaultEntries($user_id, $vault_guid);
}
/**
* @param string $item_guid
* @return Entity[]

View file

@ -55,12 +55,13 @@ class Admin implements ISettings {
* @return TemplateResponse
*/
public function getForm(): TemplateResponse {
$hasInternetConnection = $this->config->getSystemValue('has_internet_connection', true);
$checkVersion = $this->config->getAppValue('passman', 'check_version', '1') === '1';
$localVersion = $this->appManager->getAppInfo('passman')["version"];
$githubVersion = $this->l->t('Unable to get version info');
$githubReleaseUrl = null;
if ($checkVersion) {
if ($checkVersion && $hasInternetConnection) {
// get latest GitHub release version
$url = 'https://api.github.com/repos/nextcloud/passman/releases/latest';
@ -90,6 +91,7 @@ class Admin implements ISettings {
'githubVersion' => $githubVersion,
'githubReleaseUrl' => $githubReleaseUrl,
'checkVersion' => $checkVersion,
'hasInternetConnection' => $hasInternetConnection,
], 'blank');
}