mirror of
https://github.com/nextcloud/passman.git
synced 2025-11-08 05:04:04 +08:00
Merge branch 'master' of github.com:nextcloud/passman
This commit is contained in:
commit
3d950d3540
35 changed files with 1358 additions and 246 deletions
|
|
@ -13,14 +13,41 @@ namespace OCA\Passman\AppInfo;
|
|||
|
||||
|
||||
use OCP\Util;
|
||||
use OCP\BackgroundJob;
|
||||
use OCA\Passman\Notifier;
|
||||
use OCA\Passman\Activity;
|
||||
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'),
|
||||
];
|
||||
});
|
||||
|
||||
$manager = \OC::$server->getActivityManager();
|
||||
$manager->registerExtension(function() {
|
||||
return new Activity(
|
||||
\OC::$server->getL10NFactory()
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* 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');
|
||||
|
|
@ -43,5 +43,13 @@ return [
|
|||
['name' => 'file#getFile', 'url' => '/api/v2/file/{file_id}', 'verb' => 'GET'],
|
||||
['name' => 'file#deleteFile', 'url' => '/api/v2/file/{file_id}', 'verb' => 'DELETE'],
|
||||
|
||||
//Sharing stuff
|
||||
['name' => 'share#search', 'url' => '/api/v2/sharing/search', 'verb' => 'POST'],
|
||||
|
||||
|
||||
//Internal API
|
||||
['name' => 'internal#remind', 'url' => '/api/internal/notifications/remind/{credential_id}', 'verb' => 'POST'],
|
||||
['name' => 'internal#read', 'url' => '/api/internal/notifications/read/{credential_id}', 'verb' => 'DELETE'],
|
||||
|
||||
]
|
||||
];
|
||||
|
|
@ -15,19 +15,23 @@ use OCP\IRequest;
|
|||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\AppFramework\ApiController;
|
||||
use OCA\Passman\Service\CredentialService;
|
||||
|
||||
use OCA\Passman\Activity;
|
||||
use OCA\Passman\Service\ActivityService;
|
||||
|
||||
class CredentialController extends ApiController {
|
||||
private $userId;
|
||||
private $credentialService;
|
||||
private $activityService;
|
||||
|
||||
public function __construct($AppName,
|
||||
IRequest $request,
|
||||
$UserId,
|
||||
CredentialService $credentialService) {
|
||||
CredentialService $credentialService,
|
||||
ActivityService $activityService) {
|
||||
parent::__construct($AppName, $request);
|
||||
$this->userId = $UserId;
|
||||
$this->credentialService = $credentialService;
|
||||
$this->activityService = $activityService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -63,6 +67,11 @@ class CredentialController extends ApiController {
|
|||
|
||||
);
|
||||
$credential = $this->credentialService->createCredential($credential);
|
||||
$link = ''; // @TODO create direct link to credential
|
||||
$this->activityService->add(
|
||||
Activity::SUBJECT_ITEM_CREATED_SELF, array($label, $this->userId),
|
||||
'', array(),
|
||||
$link, $this->userId, Activity::TYPE_ITEM_ACTION);
|
||||
return new JSONResponse($credential);
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +79,7 @@ class CredentialController extends ApiController {
|
|||
* @NoAdminRequired
|
||||
*/
|
||||
public function getCredential($credential_id) {
|
||||
return;
|
||||
return new JSONResponse($this->credentialService->getCredentialById($credential_id, $this->userId));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -104,7 +113,37 @@ class CredentialController extends ApiController {
|
|||
'hidden' => $hidden,
|
||||
'otp' => $otp,
|
||||
);
|
||||
|
||||
|
||||
$storedCredential = $this->credentialService->getCredentialById($credential_id, $this->userId);
|
||||
|
||||
$link = ''; // @TODO create direct link to credential
|
||||
|
||||
if (($storedCredential->getDeleteTime() == 0) && $delete_time > 0) {
|
||||
$this->activityService->add(
|
||||
'item_deleted_self', array($label, $this->userId),
|
||||
'', array(),
|
||||
$link, $this->userId, Activity::TYPE_ITEM_ACTION);
|
||||
} else if (($storedCredential->getDeleteTime() > 0) && $delete_time == 0) {
|
||||
$this->activityService->add(
|
||||
'item_recovered_self', array($label, $this->userId),
|
||||
'', array(),
|
||||
$link, $this->userId, Activity::TYPE_ITEM_ACTION);
|
||||
} else if ($label != $storedCredential->getLabel()) {
|
||||
$this->activityService->add(
|
||||
'item_renamed_self', array($storedCredential->getLabel(), $label, $this->userId),
|
||||
'', array(),
|
||||
$link, $this->userId, Activity::TYPE_ITEM_ACTION);
|
||||
} else {
|
||||
$this->activityService->add(
|
||||
'item_edited_self', array($label, $this->userId),
|
||||
'', array(),
|
||||
$link, $this->userId, Activity::TYPE_ITEM_ACTION);
|
||||
}
|
||||
|
||||
|
||||
$credential = $this->credentialService->updateCredential($credential);
|
||||
|
||||
return new JSONResponse($credential);
|
||||
}
|
||||
|
||||
|
|
@ -112,9 +151,20 @@ class CredentialController extends ApiController {
|
|||
* @NoAdminRequired
|
||||
*/
|
||||
public function deleteCredential($credential_id) {
|
||||
return;
|
||||
$credential = $this->credentialService->getCredentialById($credential_id, $this->userId);
|
||||
if($credential){
|
||||
$result = $this->credentialService->deleteCredential($credential);
|
||||
$this->activityService->add(
|
||||
'item_destroyed_self', array($credential->getLabel()),
|
||||
'', array(),
|
||||
'', $this->userId, Activity::TYPE_ITEM_ACTION);
|
||||
} else {
|
||||
$result = false;
|
||||
}
|
||||
return new JSONResponse($result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
|
|
|
|||
57
controller/internalcontroller.php
Normal file
57
controller/internalcontroller.php
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?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\Controller;
|
||||
|
||||
use OCP\IRequest;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\AppFramework\ApiController;
|
||||
use OCA\Passman\Service\CredentialService;
|
||||
|
||||
class InternalController extends ApiController {
|
||||
private $userId;
|
||||
private $credentialService;
|
||||
public function __construct($AppName,
|
||||
IRequest $request,
|
||||
$UserId,
|
||||
CredentialService $credentialService){
|
||||
parent::__construct($AppName, $request);
|
||||
$this->userId = $UserId;
|
||||
$this->credentialService = $credentialService;
|
||||
}
|
||||
|
||||
function remind($credential_id){
|
||||
$credential = $this->credentialService->getCredentialById($credential_id, $this->userId);
|
||||
$credential->setExpireTime(time()+ (24 * 60 * 60));
|
||||
$this->credentialService->upd($credential);
|
||||
|
||||
$manager = \OC::$server->getNotificationManager();
|
||||
$notification = $manager->createNotification();
|
||||
$notification->setApp('passman')
|
||||
->setObject('credential', $credential_id)
|
||||
->setUser($this->userId);
|
||||
$manager->markProcessed($notification);
|
||||
}
|
||||
|
||||
function read($credential_id){
|
||||
|
||||
$credential = $this->credentialService->getCredentialById($credential_id, $this->userId);
|
||||
$credential->setExpireTime(0);
|
||||
$this->credentialService->upd($credential);
|
||||
|
||||
$manager = \OC::$server->getNotificationManager();
|
||||
$notification = $manager->createNotification();
|
||||
$notification->setApp('passman')
|
||||
->setObject('credential', $credential_id)
|
||||
->setUser($this->userId);
|
||||
$manager->markProcessed($notification);
|
||||
}
|
||||
}
|
||||
106
controller/sharecontroller.php
Normal file
106
controller/sharecontroller.php
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
<?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\Controller;
|
||||
|
||||
use OCP\IRequest;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\AppFramework\ApiController;
|
||||
|
||||
use OCP\IGroup;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IUserManager;
|
||||
use OCP\IUser;
|
||||
|
||||
use OCA\Passman\Service\VaultService;
|
||||
use OCA\Passman\Service\CredentialService;
|
||||
use OCA\Passman\Service\UserService;
|
||||
|
||||
|
||||
class ShareController extends ApiController {
|
||||
private $userId;
|
||||
private $activityService;
|
||||
private $groupManager;
|
||||
private $userManager;
|
||||
|
||||
private $limit = 50;
|
||||
private $offset = 0;
|
||||
|
||||
private $result = [];
|
||||
|
||||
public function __construct($AppName,
|
||||
IRequest $request,
|
||||
IUser $UserId,
|
||||
IGroupManager $groupManager,
|
||||
IUserManager $userManager,
|
||||
ActivityService $activityService
|
||||
) {
|
||||
parent::__construct($AppName, $request);
|
||||
$this->userId = $UserId;
|
||||
$this->userManager = $userManager;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->activityService = $activityService;
|
||||
}
|
||||
|
||||
|
||||
public function searchUsers($search) {
|
||||
$users = array();
|
||||
$usersTmp = $this->userManager->searchDisplayName($search, $this->limit, $this->offset);
|
||||
|
||||
foreach ($usersTmp as $user) {
|
||||
$users[] = array(
|
||||
'text' => $user->getDisplayName(),
|
||||
'uid' => $user->getUID(),
|
||||
'type' => 'user'
|
||||
);
|
||||
}
|
||||
$this->result = array_merge($this->result, $users);
|
||||
}
|
||||
|
||||
public function searchGroups($search){
|
||||
|
||||
$groups = array();
|
||||
$groupsTmp = $this->groupManager->search($search, $this->limit, $this->offset);
|
||||
|
||||
|
||||
foreach ($groupsTmp as $group) {
|
||||
$groups[] = array(
|
||||
'text' => $group->getGID(),
|
||||
'uid' => $group->getGID(),
|
||||
'type' => 'group'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
$this->result = array_merge($this->result, $groups);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function search($search) {
|
||||
$this->searchUsers($search);
|
||||
$this->searchGroups($search);
|
||||
|
||||
return new JSONResponse($this->result);
|
||||
}
|
||||
|
||||
|
||||
public function share($credential){
|
||||
|
||||
$link = '';
|
||||
$this->activityService->add(
|
||||
'item_shared', array($credential->label, $this->userId),
|
||||
'', array(),
|
||||
$link, $this->userId, Activity::TYPE_ITEM_ACTION);
|
||||
}
|
||||
|
||||
}
|
||||
15
css/app.css
15
css/app.css
|
|
@ -171,9 +171,9 @@
|
|||
display: inline-block;
|
||||
font-weight: bold; }
|
||||
#app-content #app-content-wrapper .actions.creatable .searchboxContainer {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
margin-right: 10px; }
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 0; }
|
||||
#app-content #app-content-wrapper .actions.creatable .searchboxContainer .searchbox {
|
||||
display: inline-block; }
|
||||
#app-content #app-content-wrapper .credential-table {
|
||||
|
|
@ -203,6 +203,8 @@
|
|||
border-radius: 5px;
|
||||
background-clip: padding-box;
|
||||
/* stops bg color from leaking outside the border: */ }
|
||||
#app-content #app-content-wrapper .credential-table tr td .tags .tag:last-child {
|
||||
margin-right: 8px; }
|
||||
#app-content #app-content-wrapper .credential-table tr td .icon-more {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
|
|
@ -352,6 +354,13 @@
|
|||
#app-navigation > ul ul {
|
||||
display: inherit !important; }
|
||||
|
||||
.sharing_table td:first-child {
|
||||
width: 55%; }
|
||||
.sharing_table td:first-child tags-input .tags {
|
||||
width: 100%; }
|
||||
.sharing_table td:first-child .autocomplete {
|
||||
margin-top: 35px; }
|
||||
|
||||
.table {
|
||||
width: 100%; }
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
74
css/vendor/angularjs-datetime-picker/angularjs-datetime-picker.css
vendored
Normal file
74
css/vendor/angularjs-datetime-picker/angularjs-datetime-picker.css
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
.angularjs-datetime-picker {
|
||||
color: #333;
|
||||
font: normal 14px sans-serif;
|
||||
border: 1px solid #ddd;
|
||||
display: inline-block;
|
||||
background: #fff;
|
||||
}
|
||||
.angularjs-datetime-picker > .adp-month {
|
||||
text-align: center;
|
||||
line-height: 22px;
|
||||
padding: 10px;
|
||||
background: #fcfcfc;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #ddd;
|
||||
position: relative;
|
||||
}
|
||||
.angularjs-datetime-picker > .adp-month > button {
|
||||
color: #555;
|
||||
font: normal 14px sans-serif;
|
||||
outline: none;
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.angularjs-datetime-picker > .adp-month > button:hover {
|
||||
color: #333;
|
||||
}
|
||||
.angularjs-datetime-picker > .adp-month > button.adp-prev {
|
||||
left: 10px;
|
||||
}
|
||||
.angularjs-datetime-picker > .adp-month > button.adp-next {
|
||||
right: 10px;
|
||||
}
|
||||
.angularjs-datetime-picker > .adp-days {
|
||||
width: 210px; /* 30 x 7 */
|
||||
margin: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
.angularjs-datetime-picker > .adp-days > .adp-day-of-week, .angularjs-datetime-picker > .adp-days > .adp-day {
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
border: 1px solid transparent;
|
||||
width: 30px;
|
||||
line-height: 28px;
|
||||
float: left;
|
||||
}
|
||||
.angularjs-datetime-picker > .adp-days > .adp-day-of-week {
|
||||
font-weight: bold;
|
||||
}
|
||||
.angularjs-datetime-picker > .adp-days > .adp-day:not(.selectable) {
|
||||
opacity: 0.15;
|
||||
cursor: default;
|
||||
}
|
||||
.angularjs-datetime-picker > .adp-days > .adp-day.selectable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.angularjs-datetime-picker > .adp-days > .adp-day.selected {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
.angularjs-datetime-picker > .adp-days > .adp-day.selectable:hover {
|
||||
background: #eee;
|
||||
}
|
||||
.angularjs-datetime-picker > .adp-days:after {
|
||||
content: '';
|
||||
display: block;
|
||||
clear: left;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.angularjs-datetime-picker input[type=range] {
|
||||
width: 150px;
|
||||
}
|
||||
|
|
@ -22,7 +22,8 @@ angular
|
|||
'ngPasswordMeter',
|
||||
'ngclipboard',
|
||||
'xeditable',
|
||||
'ngTagsInput'
|
||||
'ngTagsInput',
|
||||
'angularjs-datetime-picker'
|
||||
])
|
||||
.config(function ($routeProvider) {
|
||||
$routeProvider
|
||||
|
|
@ -108,5 +109,7 @@ jQuery(document).ready(function () {
|
|||
}
|
||||
};
|
||||
$(window).resize(_.debounce(adjustControlsWidth, 256));
|
||||
adjustControlsWidth()
|
||||
setTimeout(function(){
|
||||
adjustControlsWidth()
|
||||
},200)
|
||||
});
|
||||
|
|
@ -95,7 +95,7 @@ angular.module('passmanApp')
|
|||
if (notification) {
|
||||
NotificationService.hideNotification(notification);
|
||||
}
|
||||
NotificationService.showNotification('Credential recovered <a class="undoRestore" data-item-id="' + credential.credential_id + '">[Undo]</a>', 7500,
|
||||
NotificationService.showNotification('Credential recovered <a class="undoRestore" data-item-id="' + credential.credential_id + '">[Undo]</a>', 5000,
|
||||
function () {
|
||||
CredentialService.updateCredential(_credential).then(function (result) {
|
||||
notification = false;
|
||||
|
|
@ -105,6 +105,19 @@ angular.module('passmanApp')
|
|||
|
||||
};
|
||||
|
||||
$scope.destroyCredential = function(credential){
|
||||
var _credential = angular.copy(credential);
|
||||
CredentialService.destroyCredential(_credential.credential_id).then(function (result) {
|
||||
for (var i = 0; i < $scope.credentials.length; i++) {
|
||||
if ($scope.credentials[i].credential_id == credential.credential_id) {
|
||||
$scope.credentials.splice(i,1);
|
||||
NotificationService.showNotification('Credential destroyed', 5000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.itemFilter = {
|
||||
label: ''
|
||||
};
|
||||
|
|
@ -168,14 +181,10 @@ angular.module('passmanApp')
|
|||
|
||||
$rootScope.$on('logout', function () {
|
||||
console.log('Logout received, clean up');
|
||||
$scope.credentials = [];
|
||||
if ($scope.hasOwnProperty('$parent')) {
|
||||
if ($scope.$parent.hasOwnProperty('selectedVault')) {
|
||||
$scope.$parent.selectedVault = false;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.active_vault = null;
|
||||
$scope.credentials = [];
|
||||
// $scope.$parent.selectedVault = false;
|
||||
|
||||
});
|
||||
|
||||
var fetchCredentials = function () {
|
||||
|
|
|
|||
|
|
@ -10,198 +10,211 @@
|
|||
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) {
|
||||
CredentialService.getCredential($routeParams.credential_id).then(function(result){
|
||||
$scope.storedCredential = CredentialService.decryptCredential(angular.copy(result));
|
||||
});
|
||||
} 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
|
||||
//@TODO When credential is expired and has renew interval set, calc new expire time.
|
||||
|
||||
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);
|
||||
}
|
||||
}]);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,13 @@
|
|||
angular.module('passmanApp')
|
||||
.controller('MainCtrl', ['$scope', '$rootScope', function ($scope, $rootScope) {
|
||||
$scope.selectedVault = false;
|
||||
|
||||
$rootScope.$on('app_menu', function(evt, shown){
|
||||
$scope.app_sidebar = shown;
|
||||
});
|
||||
|
||||
$rootScope.$on('logout', function () {
|
||||
$scope.selectedVault = false;
|
||||
})
|
||||
}]);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
angular.module('passmanApp')
|
||||
.controller('MenuCtrl', ['$scope', 'VaultService', 'SettingsService', '$location', '$rootScope', 'TagService',
|
||||
function ($scope, VaultService, SettingsService, $location, $rootScope, TagService) {
|
||||
$scope.logout = function () {
|
||||
$rootScope.logout = function () {
|
||||
SettingsService.setSetting('defaultVaultPass', false);
|
||||
$rootScope.$broadcast('logout');
|
||||
$location.path('/');
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* Controller of the passmanApp
|
||||
*/
|
||||
angular.module('passmanApp')
|
||||
.controller('ShareCtrl', ['$scope', 'VaultService', 'CredentialService', 'SettingsService', '$location', '$routeParams', function ($scope, VaultService, CredentialService, SettingsService, $location, $routeParams) {
|
||||
.controller('ShareCtrl', ['$scope', 'VaultService', 'CredentialService', 'SettingsService', '$location', '$routeParams', 'ShareService', function ($scope, VaultService, CredentialService, SettingsService, $location, $routeParams, ShareService) {
|
||||
$scope.active_vault = VaultService.getActiveVault();
|
||||
|
||||
$scope.tabs = [{
|
||||
|
|
@ -60,21 +60,9 @@ angular.module('passmanApp')
|
|||
};
|
||||
|
||||
|
||||
$scope.searchUsersAndGroups = function($query){
|
||||
|
||||
};
|
||||
|
||||
$scope.share_settings = {
|
||||
credentialSharedWithUserAndGroup:[
|
||||
{
|
||||
userId: 'someuser',
|
||||
accessLevel: 'CAN_VIEW'
|
||||
},
|
||||
{
|
||||
userId: 'someuser',
|
||||
accessLevel: 'CAN_EDIT'
|
||||
}
|
||||
]
|
||||
credentialSharedWithUserAndGroup:[]
|
||||
};
|
||||
|
||||
$scope.accessLevels = [
|
||||
|
|
@ -86,8 +74,30 @@ angular.module('passmanApp')
|
|||
label: 'Can view',
|
||||
value: 'CAN_VIEW'
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
$scope.inputSharedWith = [];
|
||||
$scope.selectedAccessLevel = 'CAN_VIEW';
|
||||
|
||||
$scope.searchUsers = function($query){
|
||||
return ShareService.search($query)
|
||||
};
|
||||
|
||||
$scope.shareWith = function(shareWith, selectedAccessLevel){
|
||||
$scope.inputSharedWith = [];
|
||||
if(shareWith.length > 0) {
|
||||
for (var i = 0; i < shareWith.length; i++) {
|
||||
$scope.share_settings.credentialSharedWithUserAndGroup.push(
|
||||
{
|
||||
userId: shareWith[i].uid,
|
||||
displayName: shareWith[i].text,
|
||||
type: shareWith[i].type,
|
||||
accessLevel: selectedAccessLevel
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}]);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -72,6 +75,26 @@ angular.module('passmanApp')
|
|||
}
|
||||
});
|
||||
},
|
||||
getCredential: function(id){
|
||||
var queryUrl = OC.generateUrl('apps/passman/api/v2/credentials/' + id);
|
||||
return $http.get(queryUrl).then(function (response) {
|
||||
if (response.data) {
|
||||
return response.data;
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
});
|
||||
},
|
||||
destroyCredential: function(id){
|
||||
var queryUrl = OC.generateUrl('apps/passman/api/v2/credentials/' + id);
|
||||
return $http.delete(queryUrl).then(function (response) {
|
||||
if (response.data) {
|
||||
return response.data;
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
});
|
||||
},
|
||||
encryptCredential: function (credential) {
|
||||
for (var i = 0; i < _encryptedFields.length; i++) {
|
||||
var field = _encryptedFields[i];
|
||||
|
|
|
|||
25
js/app/services/shareservice.js
Normal file
25
js/app/services/shareservice.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name passmanApp.ShareService
|
||||
* @description
|
||||
* # ShareService
|
||||
* Service in the passmanApp.
|
||||
*/
|
||||
angular.module('passmanApp')
|
||||
.service('ShareService', ['$http', function ($http) {
|
||||
var _tags = [];
|
||||
return {
|
||||
search: function (string) {
|
||||
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/search');
|
||||
return $http.post(queryUrl, {search: string}).then(function (response) {
|
||||
if (response.data) {
|
||||
return response.data;
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
|
@ -3,7 +3,7 @@ angular.module('templates-main', ['views/edit_credential.html', 'views/partials/
|
|||
angular.module('views/edit_credential.html', []).run(['$templateCache', function($templateCache) {
|
||||
'use strict';
|
||||
$templateCache.put('views/edit_credential.html',
|
||||
'<div id="controls"><div class="actions creatable"><div class="breadcrumb"><div class="crumb svg ui-droppable" data-dir="/"><a><i class="fa fa-home"></i></a></div><div class="crumb svg" data-dir="/Test"><a>{{active_vault.name}}</a></div><div class="crumb svg last" data-dir="/Test"><a ng-if="storedCredential.credential_id">Edit credential "{{storedCredential.label}}"</a> <a ng-if="!storedCredential.credential_id">Create new credential</a></div></div></div></div><ul class="tab_header"><li ng-repeat="tab in tabs track by $index" class="tab" ng-class="{active:isActiveTab(tab)}" ng-click="onClickTab(tab)">{{tab.title}}</li></ul><div class="tab_container edit_credential"><div ng-include="currentTab.url"></div><button ng-click="saveCredential()">Save</button> <button ng-click="cancel()">Cancel</button></div>');
|
||||
'<div id="controls"><div class="actions creatable"><div class="breadcrumb"><div class="crumb svg ui-droppable" data-dir="/"><a ng-click="logout()"><i class="fa fa-home"></i></a></div><div class="crumb svg" data-dir="/Test"><a ng-click="cancel()">{{active_vault.name}}</a></div><div class="crumb svg last" data-dir="/Test"><a ng-if="storedCredential.credential_id">Edit credential "{{storedCredential.label}}"</a> <a ng-if="!storedCredential.credential_id">Create new credential</a></div></div></div></div><ul class="tab_header"><li ng-repeat="tab in tabs track by $index" class="tab" ng-class="{active:isActiveTab(tab)}" ng-click="onClickTab(tab)">{{tab.title}}</li></ul><div class="tab_container edit_credential"><div ng-include="currentTab.url"></div><button ng-click="saveCredential()">Save</button> <button ng-click="cancel()">Cancel</button></div>');
|
||||
}]);
|
||||
|
||||
angular.module('views/partials/forms/edit_credential/basics.html', []).run(['$templateCache', function($templateCache) {
|
||||
|
|
@ -33,13 +33,15 @@ 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) {
|
||||
'use strict';
|
||||
$templateCache.put('views/partials/forms/share_credential/basics.html',
|
||||
'<div class="row"><div class="col-xs-12 col-md-6"><div><table class="table"><thead><tr><td><input type="text" placeholder="Search user or groups"></td><td><select><option ng-repeat="lvl in accessLevels" value="{{lvl.value}}">{{lvl.label}}</option></select><button class="button">+</button></td></tr></thead></table></div></div></div><div class="row"><div class="col-xs-12 col-md-6"><table class="table shared_table"><thead><tr><td>User / group</td><td>Access</td></tr></thead><tr ng-repeat="user in share_settings.credentialSharedWithUserAndGroup"><td>{{user.userId}}</td><td>{{user.accessLevel}}</td></tr></table></div></div>');
|
||||
'<div class="row"><div class="col-xs-12 col-md-6"><div><table class="table sharing_table"><thead><tr><td><tags-input ng-model="inputSharedWith" replace-spaces-with-dashes="false" add-from-autocomplete-only="true" placeholder="Search users or groups..."><auto-complete source="searchUsers($query)" min-length="0" template="autocomplete-template"></auto-complete></tags-input></td><td><select ng-model="selectedAccessLevel"><option ng-repeat="lvl in accessLevels" value="{{lvl.value}}">{{lvl.label}}</option></select><button class="button" ng-click="shareWith(inputSharedWith, selectedAccessLevel)">+</button></td></tr></thead></table></div></div></div><div class="row"><div class="col-xs-12 col-md-6"><table class="table shared_table" ng-show="share_settings.credentialSharedWithUserAndGroup.length > 0"><thead><tr><td>User / group</td><td>Access</td></tr></thead><tr ng-repeat="user in share_settings.credentialSharedWithUserAndGroup"><td><i class="fa fa-user" ng-if="user.type === \'user\'"></i> <i class="fa fa-group" ng-if="user.type === \'group\'"></i> {{user.userId}}</td><td>{{user.accessLevel}}</td></tr></table></div></div><script type="text/ng-template" id="autocomplete-template"><i class="fa fa-user" ng-if="data.type === \'user\'"></i>\n' +
|
||||
' <i class="fa fa-group" ng-if="data.type === \'group\'"></i>\n' +
|
||||
' {{data.text}}</script>');
|
||||
}]);
|
||||
|
||||
angular.module('views/partials/forms/share_credential/expire_settings.html', []).run(['$templateCache', function($templateCache) {
|
||||
|
|
@ -57,13 +59,13 @@ angular.module('views/partials/password-meter.html', []).run(['$templateCache',
|
|||
angular.module('views/share_credential.html', []).run(['$templateCache', function($templateCache) {
|
||||
'use strict';
|
||||
$templateCache.put('views/share_credential.html',
|
||||
'<div id="controls"><div class="actions creatable"><div class="breadcrumb"><div class="crumb svg ui-droppable" data-dir="/"><a><i class="fa fa-home"></i></a></div><div class="crumb svg" data-dir="/Test"><a>{{active_vault.name}}</a></div><div class="crumb svg last" data-dir="/Test"><a ng-if="storedCredential.credential_id">Share credential "{{storedCredential.label}}"</a></div></div></div></div><ul class="tab_header"><li ng-repeat="tab in tabs track by $index" class="tab" ng-class="{active:isActiveTab(tab)}" ng-click="onClickTab(tab)">{{tab.title}}</li></ul><div class="tab_container edit_credential"><div ng-include="currentTab.url"></div><button>Share</button> <button ng-click="cancel()">Cancel</button></div>');
|
||||
'<div id="controls"><div class="actions creatable"><div class="breadcrumb"><div class="crumb svg ui-droppable" data-dir="/"><a ng-click="logout()"><i class="fa fa-home"></i></a></div><div class="crumb svg" data-dir="/Test"><a ng-click="cancel()">{{active_vault.name}}</a></div><div class="crumb svg last" data-dir="/Test"><a ng-if="storedCredential.credential_id">Share credential "{{storedCredential.label}}"</a></div></div></div></div><ul class="tab_header"><li ng-repeat="tab in tabs track by $index" class="tab" ng-class="{active:isActiveTab(tab)}" ng-click="onClickTab(tab)">{{tab.title}}</li></ul><div class="tab_container edit_credential"><div ng-include="currentTab.url"></div><button>Share</button> <button ng-click="cancel()">Cancel</button></div>');
|
||||
}]);
|
||||
|
||||
angular.module('views/show_vault.html', []).run(['$templateCache', function($templateCache) {
|
||||
'use strict';
|
||||
$templateCache.put('views/show_vault.html',
|
||||
'<div id="controls"><div class="breadcrumb"><div class="breadcrumb"><div class="crumb svg ui-droppable" data-dir="/"><a><i class="fa fa-home"></i></a></div><div class="crumb svg last"><a>{{active_vault.name}}</a></div></div></div><div class="actions creatable"><span ng-click="menuOpen = !menuOpen" class="button new" ng-init="menuOpen = false" off-click="menuOpen = false;"><span>+</span></span><div class="actionList popovermenu bubble menu" ng-show="menuOpen"><ul><li><span ng-click="addCredential()" class="menuitem action"><span class="icon icon-rename"></span> <span>New credential</span></span></li><li><span href="#" class="menuitem action"><span class="icon icon-shared"></span> <span>New shared credential</span></span></li></ul></div><div class="searchboxContainer"><input type="text" ng-model="itemFilter.label" class="searchbox" placeholder="Search credential..."></div><span class="title" ng-if="delete_time">Showing deleted since: <span ng-if="delete_time == 1">All time</span> <span ng-if="delete_time > 1">{{delete_time | date:\'dd-MM-yyyy @ HH:mm:ss\'}}</span></span></div></div><div off-click="closeSelected()"><table class="credential-table" ng-init="menuOpen = false;"><tr ng-repeat="credential in credentials | filter:itemFilter | tagFilter:selectedtags | as:this:\'filtered_credentials\'" ng-if="credential.hidden == 0 && showCredentialRow(credential)" ng-click="selectCredential(credential)" ng-class="{\'selected\': selectedCredential.credential_id == credential.credential_id}"><td><span class="icon"><i class="fa fa-lock"></i></span> <span class="label">{{credential.label}}</span> <span class="tags"><span class="tag" ng-repeat="tag in credential.tags_raw">{{tag.text}}</span></span></td></tr></table><div id="app-sidebar" class="detailsView scroll-container app_sidebar" ng-show="selectedCredential"><span class="close icon-close" ng-click="closeSelected()" alt="Close"></span><table><tr ng-show="selectedCredential.label"><td>Label</td><td>{{selectedCredential.label}}</td></tr><tr ng-show="selectedCredential.username"><td>Account</td><td><span credential-field value="selectedCredential.username"></span></td></tr><tr ng-show="selectedCredential.password"><td>Password</td><td><span credential-field value="selectedCredential.password" secret="\'true\'"></span></td></tr><tr ng-show="selectedCredential.otp.secret"><td>OTP</td><td><span otp-generator secret="selectedCredential.otp.secret"></span></td></tr><tr ng-show="selectedCredential.email"><td>E-mail</td><td><span credential-field value="selectedCredential.email"></span></td></tr><tr ng-show="selectedCredential.url"><td>URL</td><td><span credential-field value="selectedCredential.url"></span></td></tr><tr ng-show="selectedCredential.files.length > 0"><td>Files</td><td><div ng-repeat="file in selectedCredential.files" class="link" ng-click="downloadFile(file)">{{file.filename}} ({{file.size | bytes}})</div></td></tr><tr ng-repeat="field in selectedCredential.custom_fields"><td>{{field.label}}</td><td><span credential-field value="field.value" secret="field.secret"></span></td></tr><tr ng-show="selectedCredential.changed"><td>Changed</td><td>{{selectedCredential.changed * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}</td></tr><tr ng-show="selectedCredential.created"><td>Created</td><td>{{selectedCredential.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}</td></tr></table><div class="tags"><span class="tag" ng-repeat="tag in selectedCredential.tags">{{tag.text}}</span></div><div ng-show="selectedCredential"><span class="button" ng-click="editCredential(selectedCredential)" ng-if="selectedCredential.delete_time == 0"><span class="fa fa-edit"></span> Edit</span> <span class="button" ng-click="deleteCredential(selectedCredential)" ng-if="selectedCredential.delete_time == 0"><span class="fa fa-trash"></span> Delete</span> <span class="button" ng-if="selectedCredential.delete_time > 0" ng-click="recoverCredential(selectedCredential)"><span class="fa fa-recycle"></span> Recover</span> <span class="button" ng-if="selectedCredential.delete_time == 0" ng-click="shareCredential(selectedCredential)"><span class="fa fa-share"></span> Share</span></div></div></div>');
|
||||
'<div id="controls"><div class="breadcrumb"><div class="breadcrumb"><div class="crumb svg ui-droppable" data-dir="/"><a><i class="fa fa-home"></i></a></div><div class="crumb svg last"><a>{{active_vault.name}}</a></div></div></div><div class="actions creatable"><span ng-click="menuOpen = !menuOpen" class="button new" ng-init="menuOpen = false" off-click="menuOpen = false;"><span>+</span></span><div class="actionList popovermenu bubble menu" ng-show="menuOpen"><ul><li><span ng-click="addCredential()" class="menuitem action"><span class="icon icon-rename"></span> <span>New credential</span></span></li><li><span href="#" class="menuitem action"><span class="icon icon-shared"></span> <span>New shared credential</span></span></li></ul></div><div class="searchboxContainer"><input type="text" ng-model="itemFilter.label" class="searchbox" placeholder="Search credential..."></div><span class="title" ng-if="delete_time">Showing deleted since: <span ng-if="delete_time == 1">All time</span> <span ng-if="delete_time > 1">{{delete_time | date:\'dd-MM-yyyy @ HH:mm:ss\'}}</span></span></div></div><div off-click="closeSelected()"><table class="credential-table" ng-init="menuOpen = false;"><tr ng-repeat="credential in credentials | filter:itemFilter | tagFilter:selectedtags | as:this:\'filtered_credentials\'" ng-if="credential.hidden == 0 && showCredentialRow(credential)" ng-click="selectCredential(credential)" ng-class="{\'selected\': selectedCredential.credential_id == credential.credential_id}"><td><span class="icon"><i class="fa fa-lock"></i></span> <span class="label">{{credential.label}}</span> <span class="tags"><span class="tag" ng-repeat="tag in credential.tags_raw">{{tag.text}}</span></span></td></tr></table><div id="app-sidebar" class="detailsView scroll-container app_sidebar" ng-show="selectedCredential"><span class="close icon-close" ng-click="closeSelected()" alt="Close"></span><table><tr ng-show="selectedCredential.label"><td>Label</td><td>{{selectedCredential.label}}</td></tr><tr ng-show="selectedCredential.username"><td>Account</td><td><span credential-field value="selectedCredential.username"></span></td></tr><tr ng-show="selectedCredential.password"><td>Password</td><td><span credential-field value="selectedCredential.password" secret="\'true\'"></span></td></tr><tr ng-show="selectedCredential.otp.secret"><td>OTP</td><td><span otp-generator secret="selectedCredential.otp.secret"></span></td></tr><tr ng-show="selectedCredential.email"><td>E-mail</td><td><span credential-field value="selectedCredential.email"></span></td></tr><tr ng-show="selectedCredential.url"><td>URL</td><td><span credential-field value="selectedCredential.url"></span></td></tr><tr ng-show="selectedCredential.files.length > 0"><td>Files</td><td><div ng-repeat="file in selectedCredential.files" class="link" ng-click="downloadFile(file)">{{file.filename}} ({{file.size | bytes}})</div></td></tr><tr ng-repeat="field in selectedCredential.custom_fields"><td>{{field.label}}</td><td><span credential-field value="field.value" secret="field.secret"></span></td></tr><tr ng-show="selectedCredential.changed"><td>Changed</td><td>{{selectedCredential.changed * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}</td></tr><tr ng-show="selectedCredential.created"><td>Created</td><td>{{selectedCredential.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}</td></tr></table><div class="tags"><span class="tag" ng-repeat="tag in selectedCredential.tags">{{tag.text}}</span></div><div ng-show="selectedCredential"><span class="button" ng-click="editCredential(selectedCredential)" ng-if="selectedCredential.delete_time == 0"><span class="fa fa-edit"></span> Edit</span> <span class="button" ng-click="deleteCredential(selectedCredential)" ng-if="selectedCredential.delete_time == 0"><span class="fa fa-trash"></span> Delete</span> <span class="button" ng-if="selectedCredential.delete_time > 0" ng-click="recoverCredential(selectedCredential)"><span class="fa fa-recycle"></span> Recover</span> <span class="button" ng-if="selectedCredential.delete_time > 0" ng-click="destroyCredential(selectedCredential)"><span class="fa fa-bomb"></span> Destroy</span> <span class="button" ng-if="selectedCredential.delete_time == 0" ng-click="shareCredential(selectedCredential)"><span class="fa fa-share"></span> Share</span></div></div></div>');
|
||||
}]);
|
||||
|
||||
angular.module('views/vaults.html', []).run(['$templateCache', function($templateCache) {
|
||||
|
|
|
|||
1
js/vendor/angularjs-datetime-picker/angularjs-datetime-picker.min.js
vendored
Normal file
1
js/vendor/angularjs-datetime-picker/angularjs-datetime-picker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
268
lib/Activity.php
Normal file
268
lib/Activity.php
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
<?php
|
||||
/**
|
||||
* ownCloud - 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;
|
||||
|
||||
class Activity implements \OCP\Activity\IExtension {
|
||||
const TYPE_ITEM_ACTION = 'passman_item_action';
|
||||
const TYPE_ITEM_EXPIRED = 'passman_item_expired';
|
||||
const TYPE_ITEM_SHARED = 'passman_item_shared';
|
||||
const TYPE_ITEM_RENAMED = 'passman_item_renamed';
|
||||
|
||||
const SUBJECT_ITEM_CREATED = 'item_created';
|
||||
const SUBJECT_ITEM_CREATED_SELF = 'item_created_self';
|
||||
const SUBJECT_ITEM_EDITED = 'item_edited';
|
||||
const SUBJECT_ITEM_EDITED_SELF = 'item_edited_self';
|
||||
const SUBJECT_APPLY_REV = 'item_apply_revision';
|
||||
const SUBJECT_APPLY_REV_SELF = 'item_apply_revision_self';
|
||||
const SUBJECT_ITEM_DELETED = 'item_deleted';
|
||||
const SUBJECT_ITEM_DELETED_SELF = 'item_deleted_self';
|
||||
const SUBJECT_ITEM_RECOVERED = 'item_recovered';
|
||||
const SUBJECT_ITEM_RECOVERED_SELF = 'item_recovered_self';
|
||||
const SUBJECT_ITEM_DESTROYED = 'item_destroyed';
|
||||
const SUBJECT_ITEM_DESTROYED_SELF = 'item_destroyed_self';
|
||||
const SUBJECT_ITEM_EXPIRED = 'item_expired';
|
||||
const SUBJECT_ITEM_SHARED = 'item_shared';
|
||||
const SUBJECT_ITEM_RENAMED = 'item_renamed';
|
||||
const SUBJECT_ITEM_RENAMED_SELF = 'item_renamed_self';
|
||||
|
||||
|
||||
/**
|
||||
* The extension can return an array of additional notification types.
|
||||
* If no additional types are to be added false is to be returned
|
||||
*
|
||||
* @param string $languageCode
|
||||
* @return array|false
|
||||
*/
|
||||
public function getNotificationTypes($languageCode) {
|
||||
$l = \OC::$server->getL10N('passman', $languageCode);
|
||||
return array(
|
||||
self::TYPE_ITEM_ACTION => $l->t('A Passman item has been created, modified or deleted'),
|
||||
self::TYPE_ITEM_EXPIRED => $l->t('A Passman item has expired'),
|
||||
self::TYPE_ITEM_SHARED => $l->t('A Passman item has been shared'),
|
||||
self::TYPE_ITEM_RENAMED => $l->t('A Passman item has been renamed')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The extension can filter the types based on the filter if required.
|
||||
* In case no filter is to be applied false is to be returned unchanged.
|
||||
*
|
||||
* @param array $types
|
||||
* @param string $filter
|
||||
* @return array|false
|
||||
*/
|
||||
public function filterNotificationTypes($types, $filter) {
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given method additional types to be displayed in the settings can be returned.
|
||||
* In case no additional types are to be added false is to be returned.
|
||||
*
|
||||
* @param string $method
|
||||
* @return array|false
|
||||
*/
|
||||
public function getDefaultTypes($method) {
|
||||
if ($method === 'stream') {
|
||||
return array(
|
||||
self::TYPE_ITEM_ACTION,
|
||||
self::TYPE_ITEM_EXPIRED,
|
||||
self::TYPE_ITEM_SHARED,
|
||||
self::TYPE_ITEM_EXPIRED,
|
||||
self::TYPE_ITEM_RENAMED,
|
||||
);
|
||||
}
|
||||
if ($method === 'email') {
|
||||
return array(
|
||||
self::TYPE_ITEM_EXPIRED,
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The extension can translate a given message to the requested languages.
|
||||
* If no translation is available false is to be returned.
|
||||
*
|
||||
* @param string $app
|
||||
* @param string $text
|
||||
* @param array $params
|
||||
* @param boolean $stripPath
|
||||
* @param boolean $highlightParams
|
||||
* @param string $languageCode
|
||||
* @return string|false
|
||||
*/
|
||||
public function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode) {
|
||||
$l = new \OC_L10N('passman', $languageCode);
|
||||
if ($app === 'passman') {
|
||||
switch ($text) {
|
||||
case self::SUBJECT_ITEM_CREATED:
|
||||
return $l->t('%1$s has been created by %2$s', $params)->__toString();
|
||||
case self::SUBJECT_ITEM_CREATED_SELF:
|
||||
return $l->t('You created %1$s', $params)->__toString();
|
||||
case self::SUBJECT_ITEM_EDITED:
|
||||
return $l->t('%1$s has been updated by %2$s', $params)->__toString();
|
||||
case self::SUBJECT_ITEM_EDITED_SELF:
|
||||
return $l->t('You updated %1$s', $params)->__toString();
|
||||
case self::SUBJECT_APPLY_REV:
|
||||
return $l->t('%2$s has revised %1$s to the revision of %3$s', $params)->__toString();
|
||||
case self::SUBJECT_APPLY_REV_SELF:
|
||||
return $l->t('You reverted %1$s back to the revision of %3$s', $params)->__toString();
|
||||
case self::SUBJECT_ITEM_RENAMED:
|
||||
return $l->t('%3$s has renamed %1$s to %2$s', $params)->__toString();
|
||||
case self::SUBJECT_ITEM_RENAMED_SELF:
|
||||
return $l->t('You renamed %1$s to %2$s', $params)->__toString();
|
||||
case self::SUBJECT_ITEM_DELETED:
|
||||
return $l->t('%1$s has been deleted by %2$s', $params)->__toString();
|
||||
case self::SUBJECT_ITEM_DELETED_SELF:
|
||||
return $l->t('You deleted %1$s', $params)->__toString();
|
||||
case self::SUBJECT_ITEM_RECOVERED:
|
||||
return $l->t('%1$s has been recovered by %2$s', $params)->__toString();
|
||||
case self::SUBJECT_ITEM_RECOVERED_SELF:
|
||||
return $l->t('You recovered %1$s', $params)->__toString();
|
||||
case self::SUBJECT_ITEM_DESTROYED:
|
||||
return $l->t('%1$s has been permanently deleted by %2$s', $params)->__toString();
|
||||
case self::SUBJECT_ITEM_DESTROYED_SELF:
|
||||
return $l->t('You permanently deleted %1$s', $params)->__toString();
|
||||
case self::SUBJECT_ITEM_EXPIRED:
|
||||
return $l->t('The password of %1$s has expired, renew it now.', $params)->__toString();
|
||||
case self::SUBJECT_ITEM_SHARED:
|
||||
return $l->t('%s has been shared', $params)->__toString();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The extension can define the type of parameters for translation
|
||||
*
|
||||
* Currently known types are:
|
||||
* * file => will strip away the path of the file and add a tooltip with it
|
||||
* * username => will add the avatar of the user
|
||||
*
|
||||
* @param string $app
|
||||
* @param string $text
|
||||
* @return array|false
|
||||
*/
|
||||
public function getSpecialParameterList($app, $text) {
|
||||
if ($app === 'passman') {
|
||||
switch ($text) {
|
||||
case self::SUBJECT_ITEM_CREATED:
|
||||
case self::SUBJECT_ITEM_CREATED_SELF:
|
||||
case self::SUBJECT_ITEM_EDITED:
|
||||
case self::SUBJECT_ITEM_EDITED_SELF:
|
||||
case self::SUBJECT_ITEM_DELETED:
|
||||
case self::SUBJECT_ITEM_DELETED_SELF:
|
||||
case self::SUBJECT_ITEM_RECOVERED:
|
||||
case self::SUBJECT_ITEM_RECOVERED_SELF:
|
||||
case self::SUBJECT_ITEM_DESTROYED:
|
||||
case self::SUBJECT_ITEM_DESTROYED_SELF:
|
||||
return array(
|
||||
0 => 'passman',
|
||||
1 => 'username',
|
||||
);
|
||||
case self::SUBJECT_APPLY_REV:
|
||||
case self::SUBJECT_APPLY_REV_SELF:
|
||||
return array(
|
||||
0 => 'passman',
|
||||
1 => 'username',
|
||||
2 => '', //unknown
|
||||
);
|
||||
case self::SUBJECT_ITEM_EXPIRED:
|
||||
case self::SUBJECT_ITEM_RENAMED_SELF:
|
||||
case self::SUBJECT_ITEM_RENAMED:
|
||||
case self::SUBJECT_ITEM_SHARED:
|
||||
return array(
|
||||
0 => 'passman',
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A string naming the css class for the icon to be used can be returned.
|
||||
* If no icon is known for the given type false is to be returned.
|
||||
*
|
||||
* @param string $type
|
||||
* @return string|false
|
||||
*/
|
||||
public function getTypeIcon($type) {
|
||||
switch ($type) {
|
||||
case self::TYPE_ITEM_ACTION:
|
||||
case self::TYPE_ITEM_EXPIRED:
|
||||
return 'icon-password';
|
||||
case self::TYPE_ITEM_SHARED:
|
||||
return 'icon-share';
|
||||
case self::TYPE_ITEM_RENAMED:
|
||||
return '';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The extension can define the parameter grouping by returning the index as integer.
|
||||
* In case no grouping is required false is to be returned.
|
||||
*
|
||||
* @param array $activity
|
||||
* @return integer|false
|
||||
*/
|
||||
public function getGroupParameter($activity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The extension can define additional navigation entries. The array returned has to contain two keys 'top'
|
||||
* and 'apps' which hold arrays with the relevant entries.
|
||||
* If no further entries are to be added false is no be returned.
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function getNavigation() {
|
||||
$l = \OC::$server->getL10N('passman');
|
||||
return array(
|
||||
'top' => array(),
|
||||
'apps' => array(
|
||||
array(
|
||||
'id' => 'passman',
|
||||
'name' => (string) $l->t('Passwords'),
|
||||
'url' => '',//FIXME: $this->URLGenerator->linkToRoute('activity.Activities.showList', array('filter' => 'passman')),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The extension can check if a customer filter (given by a query string like filter=abc) is valid or not.
|
||||
*
|
||||
* @param string $filterValue
|
||||
* @return boolean
|
||||
*/
|
||||
public function isFilterValid($filterValue) {
|
||||
return $filterValue === 'passman';
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given filter the extension can specify the sql query conditions including parameters for that query.
|
||||
* In case the extension does not know the filter false is to be returned.
|
||||
* The query condition and the parameters are to be returned as array with two elements.
|
||||
* E.g. return array('`app` = ? and `message` like ?', array('mail', 'ownCloud%'));
|
||||
*
|
||||
* @param string $filter
|
||||
* @return array|false
|
||||
*/
|
||||
public function getQueryForFilter($filter) {
|
||||
if ($filter === 'passman') {
|
||||
return array('`app` = ?', array('passman'));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,13 @@ use OC\Files\View;
|
|||
|
||||
use OCA\Passman\Controller\CredentialController;
|
||||
use OCA\Passman\Controller\PageController;
|
||||
use OCA\Passman\Controller\ShareController;
|
||||
use OCA\Passman\Controller\VaultController;
|
||||
use OCA\Passman\Service\ActivityService;
|
||||
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;
|
||||
|
|
@ -23,6 +29,7 @@ class Application extends App {
|
|||
public function __construct () {
|
||||
parent::__construct('passman');
|
||||
$container = $this->getContainer();
|
||||
$server = $container->getServer();
|
||||
// Allow automatic DI for the View, until we migrated to Nodes API
|
||||
$container->registerService(View::class, function() {
|
||||
return new View('');
|
||||
|
|
@ -30,11 +37,58 @@ class Application extends App {
|
|||
$container->registerService('isCLI', function() {
|
||||
return \OC::$CLI;
|
||||
});
|
||||
|
||||
/**
|
||||
* Controllers
|
||||
*/
|
||||
$container->registerService('ShareController', function($c) {
|
||||
$container = $this->getContainer();
|
||||
$server = $container->getServer();
|
||||
return new ShareController(
|
||||
$c->query('AppName'),
|
||||
$c->query('Request'),
|
||||
$server->getUserSession()->getUser(),
|
||||
$server->getGroupManager(),
|
||||
$server->getUserManager(),
|
||||
$server->getShareManager(),
|
||||
$server->getURLGenerator(),
|
||||
$server->getL10N($c->query('AppName')),
|
||||
$c->query('ActivityService')
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
|
||||
/** Cron **/
|
||||
$container->registerService('CronService', function ($c) {
|
||||
return new CronService(
|
||||
$c->query('CredentialService'),
|
||||
$c->query('Logger'),
|
||||
$c->query('Utils'),
|
||||
$c->query('NotificationService'),
|
||||
$c->query('ActivityService'),
|
||||
$c->query('ServerContainer')->getDb()
|
||||
);
|
||||
});
|
||||
|
||||
$container->registerService('Db', function () {
|
||||
return new Db();
|
||||
});
|
||||
|
||||
$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('ActivityService', ActivityService::class);
|
||||
$container->registerAlias('Utils', Utils::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the navigation entry
|
||||
*/
|
||||
|
|
|
|||
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,18 @@ 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 getCredentialById($credential_id, $user_id){
|
||||
$sql = 'SELECT * FROM `*PREFIX*passman_credentials` ' .
|
||||
'WHERE `id` = ? and `user_id` = ? ';
|
||||
return $this->findEntity($sql,[$credential_id, $user_id]);
|
||||
}
|
||||
|
||||
public function create($raw_credential){
|
||||
$credential = new Credential();
|
||||
|
||||
|
|
@ -58,7 +70,7 @@ class CredentialMapper extends Mapper {
|
|||
return parent::insert($credential);
|
||||
}
|
||||
|
||||
public function update($raw_credential){
|
||||
public function updateCredential($raw_credential){
|
||||
if(!$raw_credential['guid']){
|
||||
$raw_credential['guid'] = $this->utils->GUID();
|
||||
}
|
||||
|
|
@ -90,4 +102,11 @@ class CredentialMapper extends Mapper {
|
|||
return parent::update($credential);
|
||||
}
|
||||
|
||||
public function deleteCredential(Credential $credential){
|
||||
$this->delete($credential);
|
||||
}
|
||||
|
||||
public function upd(Credential $credential){
|
||||
$this->update($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, click here to update the credential.', $notification->getSubjectParameters())
|
||||
);
|
||||
|
||||
// Deal with the actions for a known subject
|
||||
foreach ($notification->getActions() as $action) {
|
||||
switch ($action->getLabel()) {
|
||||
case 'remind':
|
||||
$action->setParsedLabel(
|
||||
(string) $l->t('Remind me later')
|
||||
);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
67
lib/Service/ActivityService.php
Normal file
67
lib/Service/ActivityService.php
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<?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 ActivityService {
|
||||
|
||||
private $manager;
|
||||
|
||||
public function __construct() {
|
||||
$this->manager = \OC::$server->getActivityManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* @subject = One of these: item_created, item_edited, item_apply_revision
|
||||
* item_deleted, item_recovered, item_destroyed,
|
||||
* item_expired, item_shared
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @subjectParams = Subject | Subject params
|
||||
* item_created = array($itemName,$user)
|
||||
* item_edited = array($itemName,$user)
|
||||
* item_apply_revision = array($itemName,$user,$revision);
|
||||
* item_deleted = array($itemName,$user)
|
||||
* item_recovered = array($itemName,$user)
|
||||
* item_destroyed = array($itemName,$user)
|
||||
* item_expired = array($itemName)
|
||||
* item_shared = array($itemName)
|
||||
* @message = Custom message (not needed)
|
||||
* @messageParams = Message params (not needed)
|
||||
* @link = will be -> <ownCloud>/apps/activity/$link
|
||||
* @user = Target user
|
||||
* @type = Can be passman_password or passman_password_shared
|
||||
* @priority = Int -> [10,20,30,40,50]
|
||||
*/
|
||||
public function add($subject,$subjectParams=array(),
|
||||
$message='',$messageParams=array(),
|
||||
$link='',$user=null,$type='') {
|
||||
$activity = $this->manager->generateEvent();
|
||||
$activity->setType($type);
|
||||
$activity->setApp('passman');
|
||||
$activity->setSubject($subject, $subjectParams);
|
||||
$activity->setLink($link);
|
||||
$activity->setAffectedUser($user);
|
||||
$activity->setAuthor($user);
|
||||
$activity->setTimestamp(time());
|
||||
$activity->setMessage($message, $messageParams);
|
||||
print_r($this->manager->publish($activity));
|
||||
return array('success'=>'ok');
|
||||
}
|
||||
}
|
||||
|
|
@ -30,10 +30,25 @@ class CredentialService {
|
|||
}
|
||||
|
||||
public function updateCredential($credential) {
|
||||
return $this->credentialMapper->update($credential);
|
||||
return $this->credentialMapper->updateCredential($credential);
|
||||
}
|
||||
public function upd($credential) {
|
||||
return $this->credentialMapper->upd($credential);
|
||||
}
|
||||
|
||||
public function getCredentialsByVaultId($vault_id, $user_id){
|
||||
public function deleteCredential($credential){
|
||||
return $this->credentialMapper->deleteCredential($credential);
|
||||
}
|
||||
|
||||
public function getCredentialsByVaultId($vault_id, $user_id) {
|
||||
return $this->credentialMapper->getCredentialsByVaultId($vault_id, $user_id);
|
||||
}
|
||||
|
||||
public function getExpiredCredentials($timestamp) {
|
||||
return $this->credentialMapper->getExpiredCredentials($timestamp);
|
||||
}
|
||||
|
||||
public function getCredentialById($credential_id, $user_id){
|
||||
return $this->credentialMapper->getCredentialById($credential_id, $user_id);
|
||||
}
|
||||
}
|
||||
57
lib/Service/CronService.php
Normal file
57
lib/Service/CronService.php
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?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;
|
||||
use OCA\Passman\Activity;
|
||||
use OCP\IDBConnection;
|
||||
class CronService {
|
||||
|
||||
private $credentialService;
|
||||
private $logger;
|
||||
private $utils;
|
||||
private $notificationService;
|
||||
private $activityService;
|
||||
private $db;
|
||||
public function __construct(CredentialService $credentialService, ILogger $logger, Utils $utils, NotificationService $notificationService, ActivityService $activityService, $db) {
|
||||
$this->credentialService = $credentialService;
|
||||
$this->logger = $logger;
|
||||
$this->utils = $utils;
|
||||
$this->notificationService = $notificationService;
|
||||
$this->activityService = $activityService;
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
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){
|
||||
$link = ''; // @TODO create direct link to credential
|
||||
|
||||
$sql = 'SELECT count(*) as rows from `*PREFIX*notifications` WHERE `subject`= \'credential_expired\' AND object_id=?';
|
||||
$query = $this->db->prepareQuery($sql);
|
||||
$query->bindParam(1, $credential->getId(), \PDO::PARAM_INT);
|
||||
$result = $query->execute();
|
||||
if($result->fetchRow()['rows'] === 0) {
|
||||
$this->activityService->add(
|
||||
Activity::SUBJECT_ITEM_EXPIRED, array($credential->getLabel(), $credential->getUserId()),
|
||||
'', array(),
|
||||
$link, $credential->getUserId(), Activity::TYPE_ITEM_ACTION);
|
||||
$this->notificationService->credentialExpiredNotification($credential);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
53
lib/Service/NotificationService.php
Normal file
53
lib/Service/NotificationService.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?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){
|
||||
$urlGenerator = \OC::$server->getURLGenerator();
|
||||
$link = $urlGenerator->getAbsoluteURL($urlGenerator->linkTo('','index.php/apps/passman/#/vault/'. $credential->getVaultId() .'/edit/'. $credential->getId()));
|
||||
$api = $urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'index.php/apps/passman'));
|
||||
$notification = $this->manager->createNotification();
|
||||
$remindAction = $notification->createAction();
|
||||
$remindAction->setLabel('remind')
|
||||
->setLink($api. '/api/internal/notifications/remind/'. $credential->getId() , 'POST');
|
||||
|
||||
$declineAction = $notification->createAction();
|
||||
$declineAction->setLabel('ignore')
|
||||
->setLink($api . '/api/internal/notifications/read/'. $credential->getId(), '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($link)
|
||||
->addAction($declineAction)
|
||||
->addAction($remindAction);
|
||||
|
||||
$this->manager->notify($notification);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -35,9 +35,10 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
.searchboxContainer{
|
||||
display: inline-block;
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 0;
|
||||
|
||||
|
||||
.searchbox{
|
||||
display: inline-block;
|
||||
|
|
@ -74,6 +75,9 @@
|
|||
margin-right: 3px;
|
||||
@include border-radius(5px);
|
||||
}
|
||||
.tag:last-child{
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.icon-more {
|
||||
display: inline-block;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,19 @@
|
|||
.sharing_table{
|
||||
td:first-child{
|
||||
width: 55%;
|
||||
tags-input{
|
||||
.tags {
|
||||
width: 100%;
|
||||
input {
|
||||
//width: 90% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.autocomplete{
|
||||
margin-top: 35px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.table{
|
||||
width: 100%;
|
||||
}
|
||||
|
|
@ -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');
|
||||
|
|
@ -43,6 +44,7 @@ script('passman', 'app/services/fileservice');
|
|||
script('passman', 'app/services/encryptservice');
|
||||
script('passman', 'app/services/tagservice');
|
||||
script('passman', 'app/services/notificationservice');
|
||||
script('passman', 'app/services/shareservice');
|
||||
script('passman', 'app/directives/passwordgen');
|
||||
script('passman', 'app/directives/fileselect');
|
||||
script('passman', 'app/directives/progressbar');
|
||||
|
|
@ -63,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');
|
||||
?>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
<div class="actions creatable">
|
||||
<div class="breadcrumb">
|
||||
<div class="crumb svg ui-droppable" data-dir="/">
|
||||
<a><i class="fa fa-home"></i></a>
|
||||
<a ng-click="logout()"><i class="fa fa-home"></i></a>
|
||||
</div>
|
||||
<div class="crumb svg" data-dir="/Test">
|
||||
<a>{{active_vault.name}}</a>
|
||||
<a ng-click="cancel()">{{active_vault.name}}</a>
|
||||
</div>
|
||||
<div class="crumb svg last" data-dir="/Test">
|
||||
<a ng-if="storedCredential.credential_id">Edit credential
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -1,19 +1,29 @@
|
|||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<div>
|
||||
<table class="table">
|
||||
<table class="table sharing_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><input type="text" placeholder="Search user or groups"></td>
|
||||
<td>
|
||||
<tags-input ng-model="inputSharedWith"
|
||||
replace-spaces-with-dashes="false"
|
||||
add-from-autocomplete-only="true"
|
||||
placeholder="Search users or groups...">
|
||||
<auto-complete source="searchUsers($query)"
|
||||
min-length="0"
|
||||
template="autocomplete-template"></auto-complete>
|
||||
</tags-input>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<select>
|
||||
<select ng-model="selectedAccessLevel">
|
||||
<option ng-repeat="lvl in accessLevels"
|
||||
value="{{lvl.value}}">
|
||||
{{lvl.label}}
|
||||
</option>
|
||||
</select>
|
||||
<button class="button">
|
||||
<button class="button"
|
||||
ng-click="shareWith(inputSharedWith, selectedAccessLevel)">
|
||||
+
|
||||
</button>
|
||||
</td>
|
||||
|
|
@ -25,7 +35,8 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<table class="table shared_table">
|
||||
<table class="table shared_table"
|
||||
ng-show="share_settings.credentialSharedWithUserAndGroup.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>User / group</td>
|
||||
|
|
@ -33,9 +44,19 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="user in share_settings.credentialSharedWithUserAndGroup">
|
||||
<td>{{user.userId}}</td>
|
||||
<td>
|
||||
<i class="fa fa-user" ng-if="user.type === 'user'"></i>
|
||||
<i class="fa fa-group" ng-if="user.type === 'group'"></i>
|
||||
{{user.userId}}
|
||||
</td>
|
||||
<td>{{user.accessLevel}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/ng-template" id="autocomplete-template">
|
||||
<i class="fa fa-user" ng-if="data.type === 'user'"></i>
|
||||
<i class="fa fa-group" ng-if="data.type === 'group'"></i>
|
||||
{{data.text}}
|
||||
</script>
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
<div class="actions creatable">
|
||||
<div class="breadcrumb">
|
||||
<div class="crumb svg ui-droppable" data-dir="/">
|
||||
<a><i class="fa fa-home"></i></a>
|
||||
<a ng-click="logout()"><i class="fa fa-home"></i></a>
|
||||
</div>
|
||||
<div class="crumb svg" data-dir="/Test">
|
||||
<a>{{active_vault.name}}</a>
|
||||
<a ng-click="cancel()">{{active_vault.name}}</a>
|
||||
</div>
|
||||
<div class="crumb svg last" data-dir="/Test">
|
||||
<a ng-if="storedCredential.credential_id">Share credential
|
||||
|
|
|
|||
|
|
@ -173,6 +173,10 @@
|
|||
ng-click="recoverCredential(selectedCredential)">
|
||||
<span class="fa fa-recycle"></span> Recover
|
||||
</span>
|
||||
<span class="button" ng-if="selectedCredential.delete_time > 0"
|
||||
ng-click="destroyCredential(selectedCredential)">
|
||||
<span class="fa fa-bomb"></span> Destroy
|
||||
</span>
|
||||
<span class="button" ng-if="selectedCredential.delete_time == 0" ng-click="shareCredential(selectedCredential)">
|
||||
<span class="fa fa-share"></span> Share
|
||||
</span>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue