mirror of
https://github.com/nextcloud/passman.git
synced 2025-09-11 15:34:29 +08:00
Expire passwords working, requires notifications app
This commit is contained in:
parent
35262e737f
commit
6eafc2d067
14 changed files with 459 additions and 193 deletions
|
@ -13,14 +13,34 @@ namespace OCA\Passman\AppInfo;
|
|||
|
||||
|
||||
use OCP\Util;
|
||||
use OCP\BackgroundJob;
|
||||
use OCA\Passman\Notifier;
|
||||
|
||||
require_once __DIR__ . '/autoload.php';
|
||||
|
||||
$app = new \OCA\Passman\AppInfo\Application();
|
||||
$app->registerNavigationEntry();
|
||||
$app->registerPersonalPage();
|
||||
|
||||
|
||||
$l = \OC::$server->getL10N('passman');
|
||||
$manager = \OC::$server->getNotificationManager();
|
||||
$manager->registerNotifier(function() {
|
||||
return new Notifier(
|
||||
\OC::$server->getL10NFactory()
|
||||
);
|
||||
}, function() use ($l) {
|
||||
return [
|
||||
'id' => 'passman',
|
||||
'name' => $l->t('Passwords'),
|
||||
];
|
||||
});
|
||||
|
||||
/**
|
||||
* Loading translations
|
||||
*
|
||||
* The string has to match the app's folder name
|
||||
*/
|
||||
Util::addTranslations('passman');
|
||||
Util::addTranslations('passman');
|
||||
|
||||
\OCP\BackgroundJob::addRegularTask('\OCA\Passman\Cron\ExpireCredentials', 'run');
|
|
@ -22,7 +22,8 @@ angular
|
|||
'ngPasswordMeter',
|
||||
'ngclipboard',
|
||||
'xeditable',
|
||||
'ngTagsInput'
|
||||
'ngTagsInput',
|
||||
'angularjs-datetime-picker'
|
||||
])
|
||||
.config(function ($routeProvider) {
|
||||
$routeProvider
|
||||
|
|
|
@ -10,198 +10,208 @@
|
|||
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();
|
||||
$scope.active_vault = VaultService.getActiveVault();
|
||||
|
||||
|
||||
$scope.tabs = [{
|
||||
title: 'General',
|
||||
url: 'views/partials/forms/edit_credential/basics.html',
|
||||
color: 'blue'
|
||||
}, {
|
||||
title: 'Password',
|
||||
url: 'views/partials/forms/edit_credential/password.html',
|
||||
color: 'green'
|
||||
}, {
|
||||
title: 'Custom fields',
|
||||
url: 'views/partials/forms/edit_credential/custom_fields.html',
|
||||
color: 'orange'
|
||||
}, {
|
||||
title: 'Files',
|
||||
url: 'views/partials/forms/edit_credential/files.html',
|
||||
color: 'yellow'
|
||||
}, {
|
||||
title: 'OTP',
|
||||
url: 'views/partials/forms/edit_credential/otp.html',
|
||||
color: 'purple'
|
||||
}];
|
||||
$scope.tabs = [{
|
||||
title: 'General',
|
||||
url: 'views/partials/forms/edit_credential/basics.html',
|
||||
color: 'blue'
|
||||
}, {
|
||||
title: 'Password',
|
||||
url: 'views/partials/forms/edit_credential/password.html',
|
||||
color: 'green'
|
||||
}, {
|
||||
title: 'Custom fields',
|
||||
url: 'views/partials/forms/edit_credential/custom_fields.html',
|
||||
color: 'orange'
|
||||
}, {
|
||||
title: 'Files',
|
||||
url: 'views/partials/forms/edit_credential/files.html',
|
||||
color: 'yellow'
|
||||
}, {
|
||||
title: 'OTP',
|
||||
url: 'views/partials/forms/edit_credential/otp.html',
|
||||
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) {
|
||||
$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;
|
||||
}
|
||||
var storedCredential = SettingsService.getSetting('edit_credential');
|
||||
|
||||
if (!storedCredential) {
|
||||
$location.path('/vault/' + $routeParams.vault_id);
|
||||
} else {
|
||||
$scope.storedCredential = CredentialService.decryptCredential(angular.copy(storedCredential));
|
||||
$scope.storedCredential.password_repeat = angular.copy($scope.storedCredential.password);
|
||||
}
|
||||
|
||||
$scope.getTags = function ($query) {
|
||||
return TagService.searchTag($query);
|
||||
};
|
||||
|
||||
$scope.currentTab = {
|
||||
title: 'General',
|
||||
url: 'views/partials/forms/edit_credential/basics.html',
|
||||
color: 'blue'
|
||||
};
|
||||
|
||||
$scope.onClickTab = function (tab) {
|
||||
$scope.currentTab = tab;
|
||||
};
|
||||
|
||||
$scope.isActiveTab = function (tab) {
|
||||
return tab.url == $scope.currentTab.url;
|
||||
};
|
||||
|
||||
/**
|
||||
* Below general edit functions
|
||||
*/
|
||||
|
||||
$scope.pwGenerated = function (pass) {
|
||||
$scope.storedCredential.password_repeat = pass;
|
||||
};
|
||||
|
||||
var _customField = {
|
||||
label: '',
|
||||
value: '',
|
||||
secret: false
|
||||
};
|
||||
$scope.new_custom_field = angular.copy(_customField);
|
||||
|
||||
$scope.addCustomField = function () {
|
||||
if (!$scope.new_custom_field.label) {
|
||||
NotificationService.showNotification('Please fill in a label', 3000);
|
||||
}
|
||||
if (!$scope.new_custom_field.value) {
|
||||
NotificationService.showNotification('Please fill in a value!', 3000);
|
||||
}
|
||||
if (!$scope.new_custom_field.label || !$scope.new_custom_field.value) {
|
||||
return;
|
||||
}
|
||||
$scope.storedCredential.custom_fields.push(angular.copy($scope.new_custom_field));
|
||||
$scope.new_custom_field = angular.copy(_customField);
|
||||
};
|
||||
|
||||
$scope.deleteCustomField = function(field){
|
||||
var idx = $scope.storedCredential.custom_fields.indexOf(field);
|
||||
$scope.storedCredential.custom_fields.splice(idx, 1);
|
||||
};
|
||||
|
||||
$scope.new_file = {
|
||||
name: '',
|
||||
data: null
|
||||
};
|
||||
|
||||
$scope.deleteFile = function(file){
|
||||
var idx = $scope.storedCredential.files.indexOf(file);
|
||||
FileService.deleteFile(file).then(function () {
|
||||
$scope.storedCredential.files.splice(idx, 1);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.fileLoaded = function (file) {
|
||||
var _file = {
|
||||
filename: file.name,
|
||||
size: file.size,
|
||||
mimetype: file.type,
|
||||
data: file.data
|
||||
$scope.pwSettings = {
|
||||
'length': 12,
|
||||
'useUppercase': true,
|
||||
'useLowercase': true,
|
||||
'useDigits': true,
|
||||
'useSpecialChars': true,
|
||||
'minimumDigitCount': 3,
|
||||
'avoidAmbiguousCharacters': false,
|
||||
'requireEveryCharType': true
|
||||
};
|
||||
FileService.uploadFile(_file).then(function (result) {
|
||||
delete result.file_data;
|
||||
result.filename = EncryptService.decryptString(result.filename);
|
||||
$scope.storedCredential.files.push(result);
|
||||
});
|
||||
|
||||
|
||||
$scope.$apply()
|
||||
};
|
||||
|
||||
$scope.fileLoadError = function (error, file) {
|
||||
console.log(error, file)
|
||||
};
|
||||
|
||||
$scope.selected_file = '';
|
||||
$scope.fileprogress = [];
|
||||
$scope.fileSelectProgress = function (progress) {
|
||||
if (progress) {
|
||||
$scope.fileprogress = progress;
|
||||
$scope.$apply()
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$scope.parseQR = function(QRCode){
|
||||
var re = /otpauth:\/\/(totp|hotp)\/(.*)\?(secret|issuer)=(.*)&(issuer|secret)=(.*)/, parsedQR,qrInfo;
|
||||
parsedQR = (QRCode.qrData.match(re));
|
||||
if(parsedQR)
|
||||
qrInfo = {
|
||||
type: parsedQR[1],
|
||||
label: decodeURIComponent(parsedQR[2]),
|
||||
qr_uri: QRCode
|
||||
};
|
||||
qrInfo[parsedQR[3]] = parsedQR[4];
|
||||
qrInfo[parsedQR[5]] = parsedQR[6];
|
||||
$scope.storedCredential.otp = qrInfo;
|
||||
$scope.$apply()
|
||||
};
|
||||
|
||||
$scope.saveCredential = function () {
|
||||
//@TODO validation
|
||||
|
||||
delete $scope.storedCredential.password_repeat;
|
||||
if(!$scope.storedCredential.credential_id){
|
||||
$scope.storedCredential.vault_id = $scope.active_vault.vault_id;
|
||||
CredentialService.createCredential($scope.storedCredential).then(function (result) {
|
||||
$location.path('/vault/' + $routeParams.vault_id);
|
||||
NotificationService.showNotification('Credential created!', 5000)
|
||||
})
|
||||
if (!SettingsService.getSetting('defaultVault') || !SettingsService.getSetting('defaultVaultPass')) {
|
||||
if (!$scope.active_vault) {
|
||||
$location.path('/')
|
||||
}
|
||||
} else {
|
||||
CredentialService.updateCredential($scope.storedCredential).then(function (result) {
|
||||
SettingsService.setSetting('edit_credential', null);
|
||||
$location.path('/vault/' + $routeParams.vault_id);
|
||||
NotificationService.showNotification('Credential updated!', 5000)
|
||||
})
|
||||
}
|
||||
};
|
||||
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;
|
||||
|
||||
$scope.cancel = function(){
|
||||
$location.path('/vault/' + $routeParams.vault_id);
|
||||
}
|
||||
}]);
|
||||
}
|
||||
}
|
||||
if ($scope.active_vault) {
|
||||
$scope.$parent.selectedVault = true;
|
||||
}
|
||||
var storedCredential = SettingsService.getSetting('edit_credential');
|
||||
|
||||
if (!storedCredential) {
|
||||
$location.path('/vault/' + $routeParams.vault_id);
|
||||
} else {
|
||||
$scope.storedCredential = CredentialService.decryptCredential(angular.copy(storedCredential));
|
||||
$scope.storedCredential.password_repeat = angular.copy($scope.storedCredential.password);
|
||||
$scope.storedCredential.expire_time = $scope.storedCredential.expire_time * 1000;
|
||||
}
|
||||
|
||||
$scope.getTags = function ($query) {
|
||||
return TagService.searchTag($query);
|
||||
};
|
||||
|
||||
$scope.currentTab = {
|
||||
title: 'General',
|
||||
url: 'views/partials/forms/edit_credential/basics.html',
|
||||
color: 'blue'
|
||||
};
|
||||
|
||||
$scope.onClickTab = function (tab) {
|
||||
$scope.currentTab = tab;
|
||||
};
|
||||
|
||||
$scope.isActiveTab = function (tab) {
|
||||
return tab.url == $scope.currentTab.url;
|
||||
};
|
||||
|
||||
/**
|
||||
* Below general edit functions
|
||||
*/
|
||||
|
||||
$scope.pwGenerated = function (pass) {
|
||||
$scope.storedCredential.password_repeat = pass;
|
||||
};
|
||||
|
||||
var _customField = {
|
||||
label: '',
|
||||
value: '',
|
||||
secret: false
|
||||
};
|
||||
$scope.new_custom_field = angular.copy(_customField);
|
||||
|
||||
$scope.addCustomField = function () {
|
||||
if (!$scope.new_custom_field.label) {
|
||||
NotificationService.showNotification('Please fill in a label', 3000);
|
||||
}
|
||||
if (!$scope.new_custom_field.value) {
|
||||
NotificationService.showNotification('Please fill in a value!', 3000);
|
||||
}
|
||||
if (!$scope.new_custom_field.label || !$scope.new_custom_field.value) {
|
||||
return;
|
||||
}
|
||||
$scope.storedCredential.custom_fields.push(angular.copy($scope.new_custom_field));
|
||||
$scope.new_custom_field = angular.copy(_customField);
|
||||
};
|
||||
|
||||
$scope.deleteCustomField = function (field) {
|
||||
var idx = $scope.storedCredential.custom_fields.indexOf(field);
|
||||
$scope.storedCredential.custom_fields.splice(idx, 1);
|
||||
};
|
||||
|
||||
$scope.new_file = {
|
||||
name: '',
|
||||
data: null
|
||||
};
|
||||
|
||||
$scope.deleteFile = function (file) {
|
||||
var idx = $scope.storedCredential.files.indexOf(file);
|
||||
FileService.deleteFile(file).then(function () {
|
||||
$scope.storedCredential.files.splice(idx, 1);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.fileLoaded = function (file) {
|
||||
var _file = {
|
||||
filename: file.name,
|
||||
size: file.size,
|
||||
mimetype: file.type,
|
||||
data: file.data
|
||||
};
|
||||
FileService.uploadFile(_file).then(function (result) {
|
||||
delete result.file_data;
|
||||
result.filename = EncryptService.decryptString(result.filename);
|
||||
$scope.storedCredential.files.push(result);
|
||||
});
|
||||
|
||||
|
||||
$scope.$apply()
|
||||
};
|
||||
|
||||
$scope.fileLoadError = function (error, file) {
|
||||
console.log(error, file)
|
||||
};
|
||||
|
||||
$scope.selected_file = '';
|
||||
$scope.fileprogress = [];
|
||||
$scope.fileSelectProgress = function (progress) {
|
||||
if (progress) {
|
||||
$scope.fileprogress = progress;
|
||||
$scope.$apply()
|
||||
|
||||
}
|
||||
};
|
||||
$scope.renewIntervalValue = 0;
|
||||
$scope.renewIntervalModifier = '0';
|
||||
|
||||
$scope.updateInterval = function(renewIntervalValue, renewIntervalModifier){
|
||||
var value = parseInt(renewIntervalValue);
|
||||
var modifier = parseInt(renewIntervalModifier);
|
||||
if( value && modifier) {
|
||||
$scope.storedCredential.renew_interval = value * modifier;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.parseQR = function (QRCode) {
|
||||
var re = /otpauth:\/\/(totp|hotp)\/(.*)\?(secret|issuer)=(.*)&(issuer|secret)=(.*)/, parsedQR, qrInfo;
|
||||
parsedQR = (QRCode.qrData.match(re));
|
||||
if (parsedQR)
|
||||
qrInfo = {
|
||||
type: parsedQR[1],
|
||||
label: decodeURIComponent(parsedQR[2]),
|
||||
qr_uri: QRCode
|
||||
};
|
||||
qrInfo[parsedQR[3]] = parsedQR[4];
|
||||
qrInfo[parsedQR[5]] = parsedQR[6];
|
||||
$scope.storedCredential.otp = qrInfo;
|
||||
$scope.$apply()
|
||||
};
|
||||
|
||||
$scope.saveCredential = function () {
|
||||
//@TODO validation
|
||||
|
||||
delete $scope.storedCredential.password_repeat;
|
||||
if (!$scope.storedCredential.credential_id) {
|
||||
$scope.storedCredential.vault_id = $scope.active_vault.vault_id;
|
||||
CredentialService.createCredential($scope.storedCredential).then(function (result) {
|
||||
$location.path('/vault/' + $routeParams.vault_id);
|
||||
NotificationService.showNotification('Credential created!', 5000)
|
||||
})
|
||||
} else {
|
||||
CredentialService.updateCredential($scope.storedCredential).then(function (result) {
|
||||
SettingsService.setSetting('edit_credential', null);
|
||||
$location.path('/vault/' + $routeParams.vault_id);
|
||||
NotificationService.showNotification('Credential updated!', 5000)
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
$scope.cancel = function () {
|
||||
$location.path('/vault/' + $routeParams.vault_id);
|
||||
}
|
||||
}]);
|
||||
|
|
|
@ -46,6 +46,8 @@ angular.module('passmanApp')
|
|||
_credential[field] = EncryptService.encryptString(JSON.stringify(fieldValue));
|
||||
}
|
||||
|
||||
_credential.expire_time = new Date( angular.copy(credential.expire_time) ).getTime() / 1000;
|
||||
|
||||
var queryUrl = OC.generateUrl('apps/passman/api/v2/credentials');
|
||||
return $http.post(queryUrl, _credential).then(function (response) {
|
||||
if (response.data) {
|
||||
|
@ -62,6 +64,7 @@ angular.module('passmanApp')
|
|||
var fieldValue = angular.copy(credential[field]);
|
||||
_credential[field] = EncryptService.encryptString(JSON.stringify(fieldValue));
|
||||
}
|
||||
_credential.expire_time = new Date( angular.copy(credential.expire_time) ).getTime() / 1000;
|
||||
|
||||
var queryUrl = OC.generateUrl('apps/passman/api/v2/credentials/' + credential.credential_id);
|
||||
return $http.patch(queryUrl, _credential).then(function (response) {
|
||||
|
|
|
@ -33,7 +33,7 @@ angular.module('views/partials/forms/edit_credential/otp.html', []).run(['$templ
|
|||
angular.module('views/partials/forms/edit_credential/password.html', []).run(['$templateCache', function($templateCache) {
|
||||
'use strict';
|
||||
$templateCache.put('views/partials/forms/edit_credential/password.html',
|
||||
'<div class="row"><div class="col-xs-12 col-md-5 col-lg-5"><label>Password</label><div><password-gen ng-model="storedCredential.password" settings="pwSettings" callback="pwGenerated"></password-gen><ng-password-meter password="storedCredential.password"></ng-password-meter></div><label>Repeat password</label><div><input type="password" ng-model="storedCredential.password_repeat"></div><label>Expire date</label><div></div><label>Renew interval</label><div></div></div><div class="col-xs-12 col-md-7 col-lg-7">Password generation settings<div class="row"><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="pwSettings.length" min="1"></label><label><span class="label">Minimum amount of digits</span><br><input type="number" ng-model="pwSettings.minimumDigitCount" min="0"></label></div><div class="col-xs-12 col-sm-6 col-lg-6"><label><input type="checkbox" ng-model="pwSettings.useUppercase"> <span class="label sm">Use uppercase letters</span></label><label><input ng-model="pwSettings.useLowercase" type="checkbox" id="lower"> <span class="label sm">Use lowercase letters</span></label><label><input ng-model="pwSettings.useDigits" type="checkbox" id="digits"> <span class="label sm">Use numbers</span></label><label><input type="checkbox" id="special" ng-model="pwSettings.useSpecialChars"> <span class="label sm">Use special characters</span></label><label><input type="checkbox" id="ambig" ng-model="pwSettings.avoidAmbiguousCharacters"> <span class="label sm">Avoid ambiguous characters</span></label><label><input type="checkbox" ng-model="pwSettings.requireEveryCharType" id="reqevery"> <span class="label sm">Require every character type</span></label></div></div></div></div></div>');
|
||||
'<div class="row"><div class="col-xs-12 col-md-5 col-lg-5"><label>Password</label><div><password-gen ng-model="storedCredential.password" settings="pwSettings" callback="pwGenerated"></password-gen><ng-password-meter password="storedCredential.password"></ng-password-meter></div><label>Repeat password</label><div><input type="password" ng-model="storedCredential.password_repeat"></div><label>Expire date</label><div><span datetime-picker ng-model="storedCredential.expire_time" class="link" future-only ng-show="storedCredential.expire_time == 0" close-on-select="false">No expire date set</span> <span datetime-picker ng-model="storedCredential.expire_time" class="link" future-only ng-show="storedCredential.expire_time != 0" close-on-select="false">{{ storedCredential.expire_time | date:\'dd-MM-yyyy @ HH:mm:ss\'}}</span></div><label>Renew interval</label><div><input type="number" ng-model="renewIntervalValue" min="0" ng-change="updateInterval(renewIntervalValue, renewIntervalModifier)"><select ng-model="renewIntervalModifier" ng-change="updateInterval(renewIntervalValue, renewIntervalModifier)"><option value="0">Disabled</option><option value="86400">Day(s)</option><option value="604800">Week(s)</option><option value="2592000">Month(s)</option><option value="31622400">Year(s)</option></select></div></div><div class="col-xs-12 col-md-7 col-lg-7">Password generation settings<div class="row"><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="pwSettings.length" min="1"></label><label><span class="label">Minimum amount of digits</span><br><input type="number" ng-model="pwSettings.minimumDigitCount" min="0"></label></div><div class="col-xs-12 col-sm-6 col-lg-6"><label><input type="checkbox" ng-model="pwSettings.useUppercase"> <span class="label sm">Use uppercase letters</span></label><label><input ng-model="pwSettings.useLowercase" type="checkbox" id="lower"> <span class="label sm">Use lowercase letters</span></label><label><input ng-model="pwSettings.useDigits" type="checkbox" id="digits"> <span class="label sm">Use numbers</span></label><label><input type="checkbox" id="special" ng-model="pwSettings.useSpecialChars"> <span class="label sm">Use special characters</span></label><label><input type="checkbox" id="ambig" ng-model="pwSettings.avoidAmbiguousCharacters"> <span class="label sm">Avoid ambiguous characters</span></label><label><input type="checkbox" ng-model="pwSettings.requireEveryCharType" id="reqevery"> <span class="label sm">Require every character type</span></label></div></div></div></div></div>');
|
||||
}]);
|
||||
|
||||
angular.module('views/partials/forms/share_credential/basics.html', []).run(['$templateCache', function($templateCache) {
|
||||
|
|
|
@ -16,7 +16,10 @@ use OCA\Passman\Controller\CredentialController;
|
|||
use OCA\Passman\Controller\PageController;
|
||||
use OCA\Passman\Controller\ShareController;
|
||||
use OCA\Passman\Controller\VaultController;
|
||||
use OCA\Passman\Service\UserService;
|
||||
use OCA\Passman\Service\CronService;
|
||||
use OCA\Passman\Service\CredentialService;
|
||||
use OCA\Passman\Utility\Utils;
|
||||
use OCA\Passman\Service\NotificationService;
|
||||
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\IL10N;
|
||||
|
@ -52,10 +55,30 @@ class Application extends App {
|
|||
);
|
||||
});
|
||||
|
||||
|
||||
|
||||
/** Cron **/
|
||||
$container->registerService('CronService', function ($c) {
|
||||
return new CronService(
|
||||
$c->query('CredentialService'),
|
||||
$c->query('Logger'),
|
||||
$c->query('Utils'),
|
||||
$c->query('NotificationService')
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
$container->registerService('Logger', function($c) {
|
||||
return $c->query('ServerContainer')->getLogger();
|
||||
});
|
||||
|
||||
// Aliases for the controllers so we can use the automatic DI
|
||||
$container->registerAlias('CredentialController', CredentialController::class);
|
||||
$container->registerAlias('PageController', PageController::class);
|
||||
$container->registerAlias('VaultController', VaultController::class);
|
||||
$container->registerAlias('CredentialService', CredentialService::class);
|
||||
$container->registerAlias('NotificationService', NotificationService::class);
|
||||
$container->registerAlias('Utils', Utils::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
23
lib/Cron/ExpireCredentials.php
Normal file
23
lib/Cron/ExpireCredentials.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
/**
|
||||
* Nextcloud - passman
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later. See the COPYING file.
|
||||
*
|
||||
* @author Sander Brand <brantje@gmail.com>
|
||||
* @copyright Sander Brand 2016
|
||||
*/
|
||||
|
||||
|
||||
namespace OCA\Passman\Cron;
|
||||
use OCA\Passman\Service\CredentialService;
|
||||
use OCA\Passman\Utility\Utils;
|
||||
use \OCA\Passman\AppInfo\Application;
|
||||
class ExpireCredentials {
|
||||
public static function run() {
|
||||
$app = new Application();
|
||||
$container = $app->getContainer();
|
||||
$container->query('CronService')->expireCredentials();
|
||||
}
|
||||
}
|
|
@ -32,6 +32,12 @@ class CredentialMapper extends Mapper {
|
|||
return $this->findEntities($sql, [$user_id, $vault_id]);
|
||||
}
|
||||
|
||||
public function getExpiredCredentials($timestamp){
|
||||
$sql = 'SELECT * FROM `*PREFIX*passman_credentials` ' .
|
||||
'WHERE `expire_time` > 0 AND `expire_time` < ?';
|
||||
return $this->findEntities($sql, [$timestamp]);
|
||||
}
|
||||
|
||||
public function create($raw_credential){
|
||||
$credential = new Credential();
|
||||
|
||||
|
|
69
lib/Notifier.php
Normal file
69
lib/Notifier.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
/**
|
||||
* Nextcloud - passman
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later. See the COPYING file.
|
||||
*
|
||||
* @author Sander Brand <brantje@gmail.com>
|
||||
* @copyright Sander Brand 2016
|
||||
*/
|
||||
|
||||
namespace OCA\Passman;
|
||||
use OCP\Notification\INotification;
|
||||
use OCP\Notification\INotifier;
|
||||
|
||||
class Notifier implements INotifier {
|
||||
|
||||
protected $factory;
|
||||
|
||||
public function __construct(\OCP\L10N\IFactory $factory) {
|
||||
$this->factory = $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param INotification $notification
|
||||
* @param string $languageCode The code of the language that should be used to prepare the notification
|
||||
*/
|
||||
public function prepare(INotification $notification, $languageCode) {
|
||||
if ($notification->getApp() !== 'passman') {
|
||||
// Not my app => throw
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
|
||||
// Read the language from the notification
|
||||
$l = $this->factory->get('passman', $languageCode);
|
||||
|
||||
switch ($notification->getSubject()) {
|
||||
// Deal with known subjects
|
||||
case 'credential_expired':
|
||||
$notification->setParsedSubject(
|
||||
(string) $l->t('Your credential "%s" expired', $notification->getSubjectParameters())
|
||||
);
|
||||
|
||||
// Deal with the actions for a known subject
|
||||
foreach ($notification->getActions() as $action) {
|
||||
switch ($action->getLabel()) {
|
||||
case 'change':
|
||||
$action->setParsedLabel(
|
||||
(string) $l->t('Change')
|
||||
);
|
||||
break;
|
||||
|
||||
case 'ignore':
|
||||
$action->setParsedLabel(
|
||||
(string) $l->t('Ignore')
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
$notification->addParsedAction($action);
|
||||
}
|
||||
return $notification;
|
||||
break;
|
||||
default:
|
||||
// Unknown subject => Unknown notification => throw
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,4 +36,7 @@ class CredentialService {
|
|||
public function getCredentialsByVaultId($vault_id, $user_id){
|
||||
return $this->credentialMapper->getCredentialsByVaultId($vault_id, $user_id);
|
||||
}
|
||||
public function getExpiredCredentials($timestamp){
|
||||
return $this->credentialMapper->getExpiredCredentials($timestamp);
|
||||
}
|
||||
}
|
40
lib/Service/CronService.php
Normal file
40
lib/Service/CronService.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
/**
|
||||
* Nextcloud - passman
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later. See the COPYING file.
|
||||
*
|
||||
* @author Sander Brand <brantje@gmail.com>
|
||||
* @copyright Sander Brand 2016
|
||||
*/
|
||||
|
||||
namespace OCA\Passman\Service;
|
||||
|
||||
use OCP\IConfig;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\ILogger;
|
||||
use OCA\Passman\Utility\Utils;
|
||||
|
||||
class CronService {
|
||||
|
||||
private $credentialService;
|
||||
private $logger;
|
||||
private $utils;
|
||||
private $notificationService;
|
||||
|
||||
public function __construct(CredentialService $credentialService, ILogger $logger, Utils $utils, NotificationService $notificationService) {
|
||||
$this->credentialService = $credentialService;
|
||||
$this->logger = $logger;
|
||||
$this->utils = $utils;
|
||||
$this->notificationService = $notificationService;
|
||||
}
|
||||
|
||||
public function expireCredentials() {
|
||||
$this->logger->info('Passman cron test', array('app' => 'passman'));
|
||||
$expired_credentials = $this->credentialService->getExpiredCredentials($this->utils->getTime());
|
||||
foreach($expired_credentials as $credential){
|
||||
$this->notificationService->credentialExpiredNotification($credential);
|
||||
}
|
||||
}
|
||||
}
|
50
lib/Service/NotificationService.php
Normal file
50
lib/Service/NotificationService.php
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
/**
|
||||
* Nextcloud - passman
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later. See the COPYING file.
|
||||
*
|
||||
* @author Sander Brand <brantje@gmail.com>
|
||||
* @copyright Sander Brand 2016
|
||||
*/
|
||||
|
||||
namespace OCA\Passman\Service;
|
||||
|
||||
use OCP\IConfig;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
|
||||
use OCA\Passman\Db\FileMapper;
|
||||
|
||||
|
||||
class NotificationService {
|
||||
|
||||
private $manager;
|
||||
|
||||
public function __construct(FileMapper $fileMapper) {
|
||||
$this->manager = \OC::$server->getNotificationManager();
|
||||
}
|
||||
|
||||
function credentialExpiredNotification($credential){
|
||||
$notification = $this->manager->createNotification();
|
||||
$acceptAction = $notification->createAction();
|
||||
$acceptAction->setLabel('change')
|
||||
->setLink('/apps/passman/api/v1/', 'POST');
|
||||
|
||||
$declineAction = $notification->createAction();
|
||||
$declineAction->setLabel('ignore')
|
||||
->setLink('/apps/passman/internal/notifications/read', 'DELETE');
|
||||
|
||||
$notification->setApp('passman')
|
||||
->setUser($credential->getUserId())
|
||||
->setDateTime(new \DateTime())
|
||||
->setObject('credential', $credential->getId()) // $type and $id
|
||||
->setSubject('credential_expired', [$credential->getLabel()]) // $subject and $parameters
|
||||
->setLink('/apps/passman/#/vault/'. $credential->getVaultId() .'/edit/'. $credential->getId())
|
||||
->addAction($acceptAction)
|
||||
->addAction($declineAction);
|
||||
|
||||
$this->manager->notify($notification);
|
||||
}
|
||||
|
||||
}
|
|
@ -11,6 +11,7 @@ script('passman', 'vendor/angular-sanitize/angular-sanitize.min');
|
|||
script('passman', 'vendor/angular-touch/angular-touch.min');
|
||||
script('passman', 'vendor/angular-local-storage/angular-local-storage.min');
|
||||
script('passman', 'vendor/angular-off-click/angular-off-click.min');
|
||||
script('passman', 'vendor/angularjs-datetime-picker/angularjs-datetime-picker.min');
|
||||
script('passman', 'vendor/ng-password-meter/ng-password-meter');
|
||||
script('passman', 'vendor/sjcl/sjcl');
|
||||
script('passman', 'vendor/zxcvbn/zxcvbn');
|
||||
|
@ -64,6 +65,7 @@ style('passman', 'vendor/bootstrap/bootstrap-theme.min');
|
|||
style('passman', 'vendor/font-awesome/font-awesome.min');
|
||||
style('passman', 'vendor/angular-xeditable/xeditable.min');
|
||||
style('passman', 'vendor/ng-tags-input/ng-tags-input.min');
|
||||
style('passman', 'vendor/angularjs-datetime-picker/angularjs-datetime-picker');
|
||||
style('passman', 'app');
|
||||
?>
|
||||
|
||||
|
|
|
@ -14,11 +14,27 @@
|
|||
</div>
|
||||
<label>Expire date</label>
|
||||
<div>
|
||||
<span datetime-picker ng-model="storedCredential.expire_time" class="link"
|
||||
future-only ng-show="storedCredential.expire_time == 0"
|
||||
|
||||
close-on-select="false"
|
||||
> No expire date set</span>
|
||||
<span datetime-picker ng-model="storedCredential.expire_time" class="link"
|
||||
future-only ng-show="storedCredential.expire_time != 0"
|
||||
|
||||
close-on-select="false"> {{ storedCredential.expire_time | date:'dd-MM-yyyy @ HH:mm:ss'}}</span>
|
||||
|
||||
</div>
|
||||
<label>Renew interval</label>
|
||||
<div>
|
||||
|
||||
<input type="number" ng-model="renewIntervalValue" min="0" ng-change="updateInterval(renewIntervalValue, renewIntervalModifier)">
|
||||
<select ng-model="renewIntervalModifier" ng-change="updateInterval(renewIntervalValue, renewIntervalModifier)">
|
||||
<option value="0">Disabled</option>
|
||||
<option value="86400">Day(s)</option>
|
||||
<option value="604800">Week(s)</option>
|
||||
<option value="2592000">Month(s)</option>
|
||||
<option value="31622400">Year(s)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-7 col-lg-7">
|
||||
|
|
Loading…
Add table
Reference in a new issue