Merge pull request #37 from nextcloud/vaultSettings

Vault implement settings
This commit is contained in:
Sander 2016-09-28 23:24:14 +02:00 committed by GitHub
commit b317fe4359
16 changed files with 308 additions and 123 deletions

View file

@ -36,7 +36,11 @@
<length>100</length>
<notnull>true</notnull>
</field>
<field>
<name>vault_settings</name>
<type>clob</type>
<notnull>false</notnull>
</field>
<field>
<name>created</name>
<type>integer</type>

View file

@ -5,7 +5,7 @@
<description>A password manager for Nextcloud</description>
<licence>AGPL</licence>
<author>Sander Brand</author>
<version>1.0.2.9</version>
<version>1.0.2.11</version>
<namespace>Passman</namespace>
<category>other</category>
<website>https://github.com/nextcloud/passman/</website>

View file

@ -56,15 +56,40 @@ class VaultController extends ApiController {
*/
public function get($vault_id) {
$credentials = $this->credentialService->getCredentialsByVaultId($vault_id, $this->userId);
$this->vaultService->setLastAccess($vault_id);
return new JSONResponse($credentials);
$vault = $this->vaultService->getById($vault_id, $this->userId);
$vault = $vault[0];
if($vault) {
$result = array(
'vault_id' => $vault->getId(),
'guid' => $vault->getGuid(),
'name' => $vault->getName(),
'created' => $vault->getCreated(),
'private_sharing_key' => $vault->getPrivateSharingKey(),
'public_sharing_key' => $vault->getPublicSharingKey(),
'sharing_keys_generated' => $vault->getSharingKeysGenerated(),
'vault_settings' => $vault->getVaultSettings(),
'last_access' => $vault->getlastAccess()
);
$result['credentials'] = $credentials;
$this->vaultService->setLastAccess($vault_id, $this->userId);
} else {
$result = array();
}
return new JSONResponse($result);
}
/**
* @NoAdminRequired
*/
public function update($vault_id) {
return;
public function update($vault_id, $name, $vault_settings) {
$vault = array_pop($this->vaultService->getById($vault_id, $this->userId));
if($name) {
$vault->setName($name);
}
if($vault_settings) {
$vault->setVaultSettings($vault_settings);
}
$this->vaultService->updateVault($vault);
}
/**

View file

@ -27,6 +27,42 @@ angular.module('passmanApp')
}
$scope.show_spinner = true;
var fetchCredentials = function () {
VaultService.getVault($scope.active_vault).then(function (vault) {
$scope.active_vault = angular.merge($scope.active_vault, vault);
var _credentials = [];
for (var i = 0; i < $scope.active_vault.credentials.length; i++) {
try {
$scope.active_vault.credentials[i] = CredentialService.decryptCredential(angular.copy(vault.credentials[i]));
$scope.active_vault.credentials[i].tags_raw = $scope.active_vault.credentials[i].tags;
} catch (e) {
NotificationService.showNotification('An error happend during decryption', 5000);
$rootScope.$broadcast('logout');
SettingsService.setSetting('defaultVaultPass', null);
SettingsService.setSetting('defaultVault', null);
$location.path('/')
}
if ($scope.active_vault.credentials[i]) {
TagService.addTags($scope.active_vault.credentials[i].tags);
}
}
$scope.show_spinner = false;
});
};
if ($scope.active_vault) {
$scope.$parent.selectedVault = true;
fetchCredentials();
}
$scope.addCredential = function () {
var new_credential = CredentialService.newCredential();
var enc_c = CredentialService.encryptCredential(new_credential);
@ -201,40 +237,6 @@ angular.module('passmanApp')
});
$scope.show_spinner = true;
var fetchCredentials = function () {
VaultService.getVault($scope.active_vault).then(function (credentials) {
var _credentials = [];
for (var i = 0; i < credentials.length; i++) {
try {
var _c = CredentialService.decryptCredential(angular.copy(credentials[i]));
} catch (e) {
NotificationService.showNotification('An error happend during decryption', 5000);
$rootScope.$broadcast('logout');
SettingsService.setSetting('defaultVaultPass', null);
SettingsService.setSetting('defaultVault', null);
$location.path('/')
}
if (_c) {
_c.tags_raw = _c.tags;
TagService.addTags(_c.tags);
_credentials.push(_c);
}
}
$scope.credentials = _credentials;
$scope.show_spinner = false;
});
};
if ($scope.active_vault) {
$scope.$parent.selectedVault = true;
fetchCredentials();
}
$scope.downloadFile = function (file) {
FileService.getFile(file).then(function (result) {

View file

@ -10,8 +10,33 @@
angular.module('passmanApp')
.controller('CredentialEditCtrl', ['$scope', 'VaultService', 'CredentialService', 'SettingsService', '$location', '$routeParams', 'FileService', 'EncryptService', 'TagService', 'NotificationService',
function ($scope, VaultService, CredentialService, SettingsService, $location, $routeParams, FileService, EncryptService, TagService, NotificationService) {
$scope.active_vault = VaultService.getActiveVault();
if (!SettingsService.getSetting('defaultVault') || !SettingsService.getSetting('defaultVaultPass')) {
if (!$scope.active_vault) {
$location.path('/')
}
} else {
if (SettingsService.getSetting('defaultVault') && SettingsService.getSetting('defaultVaultPass')) {
var _vault = angular.copy(SettingsService.getSetting('defaultVault'));
VaultService.getVault(_vault).then(function (vault) {
vault.vaultKey = SettingsService.getSetting('defaultVaultPass');
VaultService.setActiveVault(vault);
$scope.active_vault = vault;
$scope.pwSettings = VaultService.getVaultSetting('pwSettings',
{
'length': 12,
'useUppercase': true,
'useLowercase': true,
'useDigits': true,
'useSpecialChars': true,
'minimumDigitCount': 3,
'avoidAmbiguousCharacters': false,
'requireEveryCharType': true,
'generateOnCreate': true,
})
})
}
}
$scope.tabs = [{
title: 'General',
@ -35,16 +60,6 @@ angular.module('passmanApp')
color: 'purple'
}];
$scope.pwSettings = {
'length': 12,
'useUppercase': true,
'useLowercase': true,
'useDigits': true,
'useSpecialChars': true,
'minimumDigitCount': 3,
'avoidAmbiguousCharacters': false,
'requireEveryCharType': true
};
if (!SettingsService.getSetting('defaultVault') || !SettingsService.getSetting('defaultVaultPass')) {
if (!$scope.active_vault) {
@ -65,7 +80,7 @@ angular.module('passmanApp')
var storedCredential = SettingsService.getSetting('edit_credential');
if (!storedCredential) {
CredentialService.getCredential($routeParams.credential_id).then(function(result){
CredentialService.getCredential($routeParams.credential_id).then(function (result) {
$scope.storedCredential = CredentialService.decryptCredential(angular.copy(result));
});
} else {
@ -171,10 +186,10 @@ angular.module('passmanApp')
$scope.renewIntervalValue = 0;
$scope.renewIntervalModifier = '0';
$scope.updateInterval = function(renewIntervalValue, renewIntervalModifier){
$scope.updateInterval = function (renewIntervalValue, renewIntervalModifier) {
var value = parseInt(renewIntervalValue);
var modifier = parseInt(renewIntervalModifier);
if( value && modifier) {
if (value && modifier) {
$scope.storedCredential.renew_interval = value * modifier;
}
};

View file

@ -8,18 +8,61 @@
* Controller of the passmanApp
*/
angular.module('passmanApp')
.controller('SettingsCtrl', ['$scope', '$rootScope', 'SettingsService', 'VaultService', 'CredentialService', '$location', '$routeParams', '$http',
function ($scope, $rootScope, SettingsService, VaultService, CredentialService, $location, $routeParams, $http) {
$scope.active_vault = VaultService.getActiveVault();
.controller('SettingsCtrl', ['$scope', '$rootScope', 'SettingsService', 'VaultService', 'CredentialService', '$location', '$routeParams', '$http', 'EncryptService',
function ($scope, $rootScope, SettingsService, VaultService, CredentialService, $location, $routeParams, $http, EncryptService) {
$scope.vault_settings = {};
if (!SettingsService.getSetting('defaultVault') || !SettingsService.getSetting('defaultVaultPass')) {
if (!$scope.active_vault) {
$location.path('/')
}
} else {
if (SettingsService.getSetting('defaultVault') && SettingsService.getSetting('defaultVaultPass')) {
var _vault = angular.copy(SettingsService.getSetting('defaultVault'));
VaultService.getVault(_vault).then(function (vault) {
vault.vaultKey = SettingsService.getSetting('defaultVaultPass');
VaultService.setActiveVault(vault);
$scope.active_vault = vault;
$scope.$parent.selectedVault = true;
$scope.vault_settings.pwSettings = VaultService.getVaultSetting('pwSettings',
{
'length': 12,
'useUppercase': true,
'useLowercase': true,
'useDigits': true,
'useSpecialChars': true,
'minimumDigitCount': 3,
'avoidAmbiguousCharacters': false,
'requireEveryCharType': true,
'generateOnCreate': true,
})
})
}
}
$scope.saveVaultSettings = function () {
var _vault = $scope.active_vault;
_vault.vault_settings = angular.copy($scope.vault_settings);
VaultService.updateVault(_vault).then(function () {
VaultService.setActiveVault(_vault);
});
};
$scope.tabs = [
{
title: 'General settings',
url: 'views/partials/forms/settings/general_settings.html'
},
{
title: 'Password Tool',
title: 'Password Audit',
url: 'views/partials/forms/settings/tool.html'
},
{
title: 'Password settings',
url: 'views/partials/forms/settings/password_settings.html'
},
{
title: 'Import credentials',
@ -33,7 +76,7 @@ angular.module('passmanApp')
},
{
title: 'Sharing',
url:'views/partials/forms/settings/sharing.html'
url: 'views/partials/forms/settings/sharing.html'
}
];
@ -58,47 +101,37 @@ angular.module('passmanApp')
$scope.$watch(function () {
return VaultService.getActiveVault()
}, function (vault) {
if(vault) {
if (vault) {
$scope.active_vault = vault;
}
});
if (!SettingsService.getSetting('defaultVault') || !SettingsService.getSetting('defaultVaultPass')) {
if (!$scope.active_vault) {
$location.path('/')
}
} else {
if (SettingsService.getSetting('defaultVault') && SettingsService.getSetting('defaultVaultPass')) {
var _vault = angular.copy(SettingsService.getSetting('defaultVault'));
_vault.vaultKey = angular.copy(SettingsService.getSetting('defaultVaultPass'));
VaultService.setActiveVault(_vault);
$scope.active_vault = _vault;
}
}
if ($scope.active_vault) {
$scope.$parent.selectedVault = true;
}
$rootScope.$on('logout', function () {
$scope.selectedVault = false;
});
$scope.startScan = function (minStrength) {
VaultService.getVault($scope.active_vault).then(function (credentials) {
VaultService.getVault($scope.active_vault).then(function (vault) {
var results = [];
for (var i = 0; i < credentials.length; i++) {
var c = CredentialService.decryptCredential(angular.copy(credentials[i]));
if (c.password && c.password.length > 0 && c.hidden == 0) {
var zxcvbn_result = zxcvbn(c.password);
if (zxcvbn_result.score <= minStrength) {
results.push({
credential_id: c.credential_id,
label: c.label,
password: c.password,
password_zxcvbn_result: zxcvbn_result
});
for (var i = 0; i < vault.credentials.length; i++) {
var c = angular.copy(vault.credentials[i]);
if (c.password && c.hidden == 0) {
c = CredentialService.decryptCredential(c);
if(c.password){
var zxcvbn_result = zxcvbn(c.password);
if (zxcvbn_result.score <= minStrength) {
results.push({
credential_id: c.credential_id,
label: c.label,
password: c.password,
password_zxcvbn_result: zxcvbn_result
});
}
}
}
//@todo loop custom fields (if any and check secret fields
}
@ -108,7 +141,6 @@ angular.module('passmanApp')
$scope.cancel = function () {
$location.path('/vault/' + $routeParams.vault_id);
};
}]);

View file

@ -99,8 +99,8 @@ angular.module('passmanApp')
var _vault = angular.copy(vault);
_vault.vaultKey = angular.copy(vault_key);
VaultService.setActiveVault(_vault);
VaultService.getVault(vault).then(function (credentials) {
var credential = credentials[0];
VaultService.getVault(vault).then(function (vault) {
var credential = vault.credentials[0];
try {
var c = CredentialService.decryptCredential(credential);
if ($scope.remember_vault_password) {

View file

@ -133,14 +133,18 @@ angular.module('passmanApp')
};
scope.passwordNotNull = false;
scope.$watch("settings", function () {
if(scope.settings) {
if (!scope.password && scope.settings.generateOnCreate) {
scope.generatePasswordStart();
}
}
});
scope.$watch("password", function () {
scope.model = scope.password;
scope.password_repeat = scope.model;
if(!scope.password) {
console.log('Generating new pw');
scope.generatePasswordStart();
}
});
//
scope.onSuccess = function(e) {

View file

@ -34,6 +34,7 @@ angular.module('passmanApp')
} catch(e) {
throw e;
}
}
},
}
}]);

View file

@ -10,8 +10,9 @@
angular.module('passmanApp')
.service('VaultService', ['$http', function ($http) {
// AngularJS will instantiate a singleton by calling "new" on this function
var _this = this;
var _activeVault;
return {
var service = {
getVaults: function(){
var queryUrl = OC.generateUrl('apps/passman/api/v2/vaults');
return $http.get(queryUrl).then(function (response) {
@ -23,18 +24,28 @@ angular.module('passmanApp')
});
},
setActiveVault: function(vault){
this.getVaults().then(function(vaults){
for(var v = 0; v < vaults.length; v++){
if(vaults[v].vault_id == vault.vault_id){
_activeVault = angular.merge(_activeVault, vaults[v]);
}
}
});
_activeVault = vault;
_activeVault = angular.copy(vault);
},
getActiveVault: function(vault){
return _activeVault;
},
getVaultSetting: function(key, default_value){
if(!_activeVault.vault_settings){
return default_value
} else {
return (_activeVault.vault_settings[key] !== undefined) ? _activeVault.vault_settings[key] : default_value
}
},
setVaultSetting: function(key, value){
if(!_activeVault.vault_settings){
return false;
} else {
_activeVault.vault_settings[key] = value;
_this.updateVault(_activeVault);
}
},
createVault: function (vaultName) {
var queryUrl = OC.generateUrl('apps/passman/api/v2/vaults');
return $http.post(queryUrl, { vault_name: vaultName }).then(function (response) {
@ -49,6 +60,11 @@ angular.module('passmanApp')
var queryUrl = OC.generateUrl('apps/passman/api/v2/vaults/' + vault.vault_id);
return $http.get(queryUrl).then(function (response) {
if(response.data){
if(response.data.vault_settings){
response.data.vault_settings = JSON.parse(window.atob(response.data.vault_settings))
} else {
response.data.vault_settings = {};
}
return response.data;
} else {
return response;
@ -56,8 +72,12 @@ angular.module('passmanApp')
});
},
updateVault: function (vault) {
var queryUrl = OC.generateUrl('apps/passman/api/v2/vaults/' + vault.vault_id);
return $http.post(queryUrl).then(function (response) {
var _vault = angular.copy(vault);
delete vault.defaultVaultPass;
delete vault.defaultVault;
_vault.vault_settings = window.btoa(JSON.stringify(_vault.vault_settings))
var queryUrl = OC.generateUrl('apps/passman/api/v2/vaults/' + _vault.vault_id);
return $http.patch(queryUrl, _vault).then(function (response) {
if(response.data){
return response.data;
} else {
@ -85,5 +105,7 @@ angular.module('passmanApp')
}
});
}
}
};
return service;
}]);

File diff suppressed because one or more lines are too long

View file

@ -31,6 +31,8 @@ use \OCP\AppFramework\Db\Entity;
* @method string getPrivateSharingKey()
* @method void setSharingKeysGenerated(integer $value)
* @method integer getSharingKeysGenerated()
* @method void setVaultSettings(integer $value)
* @method integer getVaultSettings()
*/
@ -46,11 +48,13 @@ class Vault extends Entity implements \JsonSerializable{
protected $publicSharingKey;
protected $privateSharingKey;
protected $sharingKeysGenerated;
protected $vaultSettings;
public function __construct() {
// add types in constructor
$this->addType('created', 'integer');
$this->addType('lastAccess', 'integer');
$this->addType('sharing_keys_generated', 'integer');
$this->addType('sharingKeysGenerated', 'integer');
}
/**
* Turns entity attributes into an array
@ -61,9 +65,7 @@ class Vault extends Entity implements \JsonSerializable{
'guid' => $this->getGuid(),
'name' => $this->getName(),
'created' => $this->getCreated(),
'private_sharing_key' => $this->getPrivateSharingKey(),
'public_sharing_key' => $this->getPublicSharingKey(),
'sharing_keys_generated' => $this->getSharingKeysGenerated(),
'last_access' => $this->getlastAccess(),
];
}

View file

@ -26,12 +26,13 @@ class VaultMapper extends Mapper {
* @throws \OCP\AppFramework\Db\DoesNotExistException if not found
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result
*/
public function find($vault_id) {
public function find($vault_id, $user_id) {
$sql = 'SELECT * FROM `*PREFIX*passman_vaults` ' .
'WHERE `user_id` = ?';
return $this->findEntities($sql, [$vault_id]);
'WHERE `id`= ? and `user_id` = ?';
return $this->findEntities($sql, [$vault_id, $user_id]);
}
public function findVaultsFromUser($userId){
$sql = 'SELECT * FROM `*PREFIX*passman_vaults` ' .
'WHERE `user_id` = ? ';
@ -45,14 +46,19 @@ class VaultMapper extends Mapper {
$vault->setUserId($userId);
$vault->setGuid($this->utils->GUID());
$vault->setCreated($this->utils->getTime());
$vault->setlastAccess(0);
$vault->setLastAccess(0);
return parent::insert($vault);
}
public function setLastAccess($vault_id){
public function setLastAccess($vault_id, $user_id){
$vault = new Vault();
$vault->setId($vault_id);
$vault->setlastAccess(time());
$vault->setUserId($user_id);
$vault->setLastAccess(time());
$this->update($vault);
}
public function updateVault(Vault $vault){
$this->update($vault);
}

View file

@ -29,12 +29,21 @@ class VaultService {
return $this->vaultMapper->findVaultsFromUser($userId);
}
public function getById($vault_id, $user_id) {
$vault = $this->vaultMapper->find($vault_id, $user_id);
return $vault;
}
public function createVault($vault_name, $userId) {
return $this->vaultMapper->create($vault_name, $userId);
}
public function setLastAccess($vault_id){
return $this->vaultMapper->setLastAccess($vault_id);
public function updateVault($vault) {
return $this->vaultMapper->updateVault($vault);
}
public function setLastAccess($vault_id, $user_id){
return $this->vaultMapper->setLastAccess($vault_id, $user_id);
}
public function updateSharingKeys($vault_id, $privateKey, $publicKey){

View file

@ -0,0 +1,57 @@
<div class="password_settings">
<div class="col-xs-12 col-sm-5 col-lg-4">
<label>
<span class="label">Password length</span><br />
<input type="number" ng-model="vault_settings.pwSettings.length" min="1">
</label>
<label>
<span class="label">Minimum amount of digits</span><br />
<input type="number" ng-model="vault_settings.pwSettings.minimumDigitCount" min="0">
</label>
<label>
<span class="label">Generate password on creation</span><br />
<input type="checkbox" ng-model="vault_settings.pwSettings.generateOnCreate" min="0">
</label>
</div>
<div class="col-xs-12 col-sm-6 col-lg-6">
<label>
<input type="checkbox"
ng-model="vault_settings.pwSettings.useUppercase">
<span class="label sm">Use uppercase letters</span>
</label>
<label>
<input
ng-model="vault_settings.pwSettings.useLowercase" type="checkbox"
id="lower">
<span class="label sm">Use lowercase letters</span>
</label>
<label>
<input ng-model="vault_settings.pwSettings.useDigits" type="checkbox"
id="digits">
<span class="label sm">Use numbers</span>
</label>
<label>
<input type="checkbox" id="special"
ng-model="vault_settings.pwSettings.useSpecialChars">
<span class="label sm">Use special characters</span>
</label>
<label>
<input type="checkbox" id="ambig"
ng-model="vault_settings.pwSettings.avoidAmbiguousCharacters">
<span class="label sm">Avoid ambiguous characters</span>
</label>
<label>
<input type="checkbox" ng-model="vault_settings.pwSettings.requireEveryCharType"
id="reqevery">
<span class="label sm">Require every character type</span>
</label>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<button class="button" ng-click="saveVaultSettings()">Save</button>
</div>
</div>

View file

@ -61,7 +61,7 @@
<div ng-init="menuOpen = false;">
<table class="credential-table"
ng-if="view_mode === 'list'">
<tr ng-repeat="credential in credentials | credentialSearch:filterOptions | tagFilter:selectedtags | orderBy:'label'| as:this:'filtered_credentials'"
<tr ng-repeat="credential in active_vault.credentials | credentialSearch:filterOptions | tagFilter:selectedtags | orderBy:'label'| as:this:'filtered_credentials'"
ng-if="credential.hidden == 0 && showCredentialRow(credential)"
ng-click="selectCredential(credential)"
ng-class="{'selected': selectedCredential.credential_id == credential.credential_id}">
@ -76,7 +76,7 @@
</tr>
</table>
<ul class="grid-view" ng-show="view_mode === 'grid'">
<li class="credential" ng-repeat="credential in credentials | credentialSearch:filterOptions | tagFilter:selectedtags | orderBy:'label'| as:this:'filtered_credentials'"
<li class="credential" ng-repeat="credential in active_vault.credentials | credentialSearch:filterOptions | tagFilter:selectedtags | orderBy:'label'| as:this:'filtered_credentials'"
ng-if="credential.hidden == 0 && showCredentialRow(credential)"
ng-click="selectCredential(credential)"
use-theme type="'border-color'">