manage cipher subvaults from org admin
This commit is contained in:
parent
a79556dfce
commit
f904558315
|
@ -1,21 +1,24 @@
|
||||||
angular
|
angular
|
||||||
.module('bit.vault')
|
.module('bit.organization')
|
||||||
|
|
||||||
.controller('organizationVaultController', function ($scope, apiService, cipherService, $analytics, $q, $state,
|
.controller('organizationVaultController', function ($scope, apiService, cipherService, $analytics, $q, $state,
|
||||||
$localStorage, $uibModal, $filter) {
|
$localStorage, $uibModal, $filter) {
|
||||||
$scope.logins = [];
|
$scope.logins = [];
|
||||||
$scope.subvaults = [];
|
$scope.subvaults = [];
|
||||||
$scope.folders = [];
|
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
|
|
||||||
$scope.$on('$viewContentLoaded', function () {
|
$scope.$on('$viewContentLoaded', function () {
|
||||||
var subvaultPromise = apiService.subvaults.listOrganization({ orgId: $state.params.orgId }, function (subvaults) {
|
var subvaultPromise = apiService.subvaults.listOrganization({ orgId: $state.params.orgId }, function (subvaults) {
|
||||||
var decSubvaults = [];
|
var decSubvaults = [{
|
||||||
|
id: null,
|
||||||
|
name: 'Unassigned',
|
||||||
|
collapsed: $localStorage.collapsedOrgSubvaults && 'unassigned' in $localStorage.collapsedOrgSubvaults
|
||||||
|
}];
|
||||||
|
|
||||||
for (var i = 0; i < subvaults.Data.length; i++) {
|
for (var i = 0; i < subvaults.Data.length; i++) {
|
||||||
var decSubvault = cipherService.decryptSubvault(subvaults.Data[i], null, true);
|
var decSubvault = cipherService.decryptSubvault(subvaults.Data[i], null, true);
|
||||||
decSubvault.collapsed = $localStorage.collapsedSubvaults &&
|
decSubvault.collapsed = $localStorage.collapsedOrgSubvaults &&
|
||||||
decSubvault.id in $localStorage.collapsedSubvaults;
|
decSubvault.id in $localStorage.collapsedOrgSubvaults;
|
||||||
decSubvaults.push(decSubvault);
|
decSubvaults.push(decSubvault);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,14 +46,20 @@
|
||||||
|
|
||||||
$scope.filterBySubvault = function (subvault) {
|
$scope.filterBySubvault = function (subvault) {
|
||||||
return function (cipher) {
|
return function (cipher) {
|
||||||
|
if (!cipher.subvaultIds || !cipher.subvaultIds.length) {
|
||||||
|
return subvault.id === null;
|
||||||
|
}
|
||||||
|
|
||||||
return cipher.subvaultIds.indexOf(subvault.id) > -1;
|
return cipher.subvaultIds.indexOf(subvault.id) > -1;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.filterByOrphaned = function () {
|
$scope.subvaultSort = function (item) {
|
||||||
return function (cipher) {
|
if (!item.id) {
|
||||||
return !cipher.subvaultIds || !cipher.subvaultIds.length;
|
return '';
|
||||||
};
|
}
|
||||||
|
|
||||||
|
return item.name.toLowerCase();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.collapseExpand = function (subvault) {
|
$scope.collapseExpand = function (subvault) {
|
||||||
|
@ -58,27 +67,30 @@
|
||||||
$localStorage.collapsedOrgSubvaults = {};
|
$localStorage.collapsedOrgSubvaults = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subvault.id in $localStorage.collapsedOrgSubvaults) {
|
var id = subvault.id || 'unassigned';
|
||||||
delete $localStorage.collapsedOrgSubvaults[subvault.id];
|
|
||||||
|
if (id in $localStorage.collapsedOrgSubvaults) {
|
||||||
|
delete $localStorage.collapsedOrgSubvaults[id];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$localStorage.collapsedOrgSubvaults[subvault.id] = true;
|
$localStorage.collapsedOrgSubvaults[id] = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.editSubvaults = function (login) {
|
$scope.editSubvaults = function (cipher) {
|
||||||
var modal = $uibModal.open({
|
var modal = $uibModal.open({
|
||||||
animation: true,
|
animation: true,
|
||||||
templateUrl: 'app/vault/views/vaultLoginSubvaults.html',
|
templateUrl: 'app/organization/views/organizationVaultLoginSubvaults.html',
|
||||||
controller: 'vaultOrganizationLoginSubvaultsController',
|
controller: 'organizationVaultLoginSubvaultsController',
|
||||||
resolve: {
|
resolve: {
|
||||||
loginId: function () { return login.id; }
|
cipher: function () { return cipher; },
|
||||||
|
subvaults: function () { return $scope.subvaults; }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
modal.result.then(function (response) {
|
modal.result.then(function (response) {
|
||||||
if (response.subvaultIds) {
|
if (response.subvaultIds) {
|
||||||
login.subvaultIds = response.subvaultIds;
|
cipher.subvaultIds = response.subvaultIds;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
angular
|
||||||
|
.module('bit.organization')
|
||||||
|
|
||||||
|
.controller('organizationVaultLoginSubvaultsController', function ($scope, apiService, $uibModalInstance, cipherService,
|
||||||
|
cipher, $analytics, subvaults) {
|
||||||
|
$analytics.eventTrack('organizationVaultLoginSubvaultsController', { category: 'Modal' });
|
||||||
|
$scope.cipher = {};
|
||||||
|
$scope.subvaults = [];
|
||||||
|
$scope.selectedSubvaults = {};
|
||||||
|
|
||||||
|
$uibModalInstance.opened.then(function () {
|
||||||
|
var subvaultUsed = [];
|
||||||
|
for (var i = 0; i < subvaults.length; i++) {
|
||||||
|
if (subvaults[i].id) {
|
||||||
|
subvaultUsed.push(subvaults[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$scope.subvaults = subvaultUsed;
|
||||||
|
|
||||||
|
$scope.cipher = cipher;
|
||||||
|
|
||||||
|
var selectedSubvaults = {};
|
||||||
|
if ($scope.cipher.subvaultIds) {
|
||||||
|
for (i = 0; i < $scope.cipher.subvaultIds.length; i++) {
|
||||||
|
selectedSubvaults[$scope.cipher.subvaultIds[i]] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$scope.selectedSubvaults = selectedSubvaults;
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.toggleSubvaultSelectionAll = function ($event) {
|
||||||
|
var subvaults = {};
|
||||||
|
if ($event.target.checked) {
|
||||||
|
for (var i = 0; i < $scope.subvaults.length; i++) {
|
||||||
|
subvaults[$scope.subvaults[i].id] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.selectedSubvaults = subvaults;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.toggleSubvaultSelection = function (id) {
|
||||||
|
if (id in $scope.selectedSubvaults) {
|
||||||
|
delete $scope.selectedSubvaults[id];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$scope.selectedSubvaults[id] = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.subvaultSelected = function (subvault) {
|
||||||
|
return subvault.id in $scope.selectedSubvaults;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.allSelected = function () {
|
||||||
|
return Object.keys($scope.selectedSubvaults).length === $scope.subvaults.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.submit = function () {
|
||||||
|
var request = {
|
||||||
|
subvaultIds: []
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var id in $scope.selectedSubvaults) {
|
||||||
|
if ($scope.selectedSubvaults.hasOwnProperty(id)) {
|
||||||
|
request.subvaultIds.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.submitPromise = apiService.ciphers.putSubvaultsAdmin({ id: cipher.id }, request)
|
||||||
|
.$promise.then(function (response) {
|
||||||
|
$analytics.eventTrack('Edited Login Subvaults');
|
||||||
|
$uibModalInstance.close({
|
||||||
|
action: 'subvaultsEdit',
|
||||||
|
subvaultIds: request.subvaultIds
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.close = function () {
|
||||||
|
$uibModalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
|
});
|
|
@ -1,19 +1,21 @@
|
||||||
<section class="content-header">
|
<section class="content-header">
|
||||||
<h1>
|
<h1>
|
||||||
Org<span class="hidden-xs">anization</span> Vault
|
Org<span class="hidden-xs">anization</span> Vault
|
||||||
<small>{{subvaults.length}} subvaults, {{logins.length}} logins</small>
|
<small>
|
||||||
|
<span ng-pluralize count="subvaults.length" when="{'1': '{} subvault', 'other': '{} subvaults'}"></span>,
|
||||||
|
<span ng-pluralize count="logins.length" when="{'1': '{} login', 'other': '{} logins'}"></span>
|
||||||
|
</small>
|
||||||
</h1>
|
</h1>
|
||||||
</section>
|
</section>
|
||||||
<section class="content">
|
<section class="content">
|
||||||
<div ng-show="loading && !subvaults.length">
|
<p ng-show="loading && !subvaults.length">Loading...</p>
|
||||||
<p>Loading...</p>
|
<div class="box" ng-class="{'collapsed-box': subvault.collapsed}" ng-repeat="subvault in subvaults | orderBy: subvaultSort"
|
||||||
</div>
|
ng-show="subvaults.length">
|
||||||
<div class="box" ng-class="{'collapsed-box': subvault.collapsed}" ng-repeat="subvault in subvaults | orderBy: ['name']"
|
|
||||||
ng-show="subvaults.length && (!main.searchVaultText || subvaultLogins.length)">
|
|
||||||
<div class="box-header with-border">
|
<div class="box-header with-border">
|
||||||
<h3 class="box-title">
|
<h3 class="box-title">
|
||||||
<i class="fa fa-share-alt-square"></i>
|
<i class="fa" ng-class="{'fa-share-alt-square': subvault.id, 'fa-sitemap': !subvault.id}"></i>
|
||||||
{{subvault.name}} <small>{{subvaultLogins.length}} logins</small>
|
{{subvault.name}}
|
||||||
|
<small ng-pluralize count="subvaultLogins.length" when="{'1': '{} login', 'other': '{} logins'}"></small>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="box-tools">
|
<div class="box-tools">
|
||||||
<button type="button" class="btn btn-box-tool" data-widget="collapse" title="Collapse/Expand"
|
<button type="button" class="btn btn-box-tool" data-widget="collapse" title="Collapse/Expand"
|
||||||
|
@ -30,7 +32,7 @@
|
||||||
<table class="table table-striped table-hover table-vmiddle">
|
<table class="table table-striped table-hover table-vmiddle">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="login in subvaultLogins = (logins | filter: filterBySubvault(subvault) |
|
<tr ng-repeat="login in subvaultLogins = (logins | filter: filterBySubvault(subvault) |
|
||||||
filter: (main.searchVaultText || '') | orderBy: ['name', 'username'])">
|
orderBy: ['name', 'username'])">
|
||||||
<td style="width: 70px;">
|
<td style="width: 70px;">
|
||||||
<div class="btn-group dropdown-to-body">
|
<div class="btn-group dropdown-to-body">
|
||||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||||
|
@ -43,7 +45,8 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="javascript:void(0)" ng-click="deleteLogin(login)" class="text-red">
|
<a href="javascript:void(0)" ng-click="deleteLogin(login)" class="text-red"
|
||||||
|
ng-if="subvault.id">
|
||||||
<i class="fa fa-fw fa-remove"></i> Remove
|
<i class="fa fa-fw fa-remove"></i> Remove
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -65,48 +68,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box" ng-show="!loading">
|
|
||||||
<div class="box-header with-border">
|
|
||||||
<h3 class="box-title">
|
|
||||||
<i class="fa fa-sitemap"></i>
|
|
||||||
Orphaned <small>{{orphanedLogins.length}} logins</small>
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div class="box-body" ng-class="{'no-padding': orphanedLogins.length}">
|
|
||||||
<div ng-show="!orphanedLogins.length">
|
|
||||||
<p>No orphaned logins.</p>
|
|
||||||
</div>
|
|
||||||
<div class="table-responsive" ng-show="orphanedLogins.length">
|
|
||||||
<table class="table table-striped table-hover table-vmiddle">
|
|
||||||
<tbody>
|
|
||||||
<tr ng-repeat="login in orphanedLogins = (logins | filter: filterByOrphaned() | orderBy: ['name', 'username'])">
|
|
||||||
<td style="width: 70px;">
|
|
||||||
<div class="btn-group dropdown-to-body">
|
|
||||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
|
||||||
<i class="fa fa-cog"></i> <span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li>
|
|
||||||
<a href="javascript:void(0)" ng-click="editSubvaults(login)">
|
|
||||||
<i class="fa fa-fw fa-share-alt"></i> Subvaults
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="javascript:void(0)" ng-click="deleteLogin(login)" class="text-red">
|
|
||||||
<i class="fa fa-fw fa-trash"></i> Delete
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a href="javascript:void(0)" ng-click="editSubvaults(login)">{{login.name}}</a>
|
|
||||||
<div class="text-sm text-muted">{{login.username}}</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
|
<h4 class="modal-title"><i class="fa fa-share-alt"></i> Subvaults <small>{{cipher.name}}</small></h4>
|
||||||
|
</div>
|
||||||
|
<form name="form" ng-submit="form.$valid && submit()" api-form="submitPromise">
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Edit the subvaults that this login is being shared with.</p>
|
||||||
|
<div class="callout callout-danger validation-errors" ng-show="form.$errors">
|
||||||
|
<h4>Errors have occured</h4>
|
||||||
|
<ul>
|
||||||
|
<li ng-repeat="e in form.$errors">{{e}}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div ng-show="!subvaults.length" class="callout callout-default">
|
||||||
|
<p>No subvaults to manage.</p>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive" ng-show="subvaults.length" style="margin: 0;">
|
||||||
|
<table class="table table-striped table-hover" style="margin: 0;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 40px;">
|
||||||
|
<input type="checkbox"
|
||||||
|
ng-checked="allSelected()"
|
||||||
|
ng-click="toggleSubvaultSelectionAll($event)">
|
||||||
|
</th>
|
||||||
|
<th>Name</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="subvault in subvaults | orderBy: ['name']">
|
||||||
|
<td valign="middle">
|
||||||
|
<input type="checkbox"
|
||||||
|
name="selectedSubvaults[]"
|
||||||
|
value="{{subvault.id}}"
|
||||||
|
ng-checked="subvaultSelected(subvault)"
|
||||||
|
ng-click="toggleSubvaultSelection(subvault.id)">
|
||||||
|
</td>
|
||||||
|
<td valign="middle" ng-click="toggleSubvaultSelection(subvault.id)">
|
||||||
|
{{subvault.name}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="form.$loading" ng-show="subvaults.length">
|
||||||
|
<i class="fa fa-refresh fa-spin loading-icon" ng-show="form.$loading"></i>Save
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -32,6 +32,7 @@
|
||||||
putPartial: { url: _apiUri + '/ciphers/:id/partial', method: 'POST', params: { id: '@id' } },
|
putPartial: { url: _apiUri + '/ciphers/:id/partial', method: 'POST', params: { id: '@id' } },
|
||||||
putShare: { url: _apiUri + '/ciphers/:id/share', method: 'POST', params: { id: '@id' } },
|
putShare: { url: _apiUri + '/ciphers/:id/share', method: 'POST', params: { id: '@id' } },
|
||||||
putSubvaults: { url: _apiUri + '/ciphers/:id/subvaults', method: 'POST', params: { id: '@id' } },
|
putSubvaults: { url: _apiUri + '/ciphers/:id/subvaults', method: 'POST', params: { id: '@id' } },
|
||||||
|
putSubvaultsAdmin: { url: _apiUri + '/ciphers/:id/subvaults-admin', method: 'POST', params: { id: '@id' } },
|
||||||
del: { url: _apiUri + '/ciphers/:id/delete', method: 'POST', params: { id: '@id' } }
|
del: { url: _apiUri + '/ciphers/:id/delete', method: 'POST', params: { id: '@id' } }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -145,6 +145,7 @@
|
||||||
<script src="app/organization/organizationDeleteController.js"></script>
|
<script src="app/organization/organizationDeleteController.js"></script>
|
||||||
<script src="app/organization/organizationBillingChangePlanController.js"></script>
|
<script src="app/organization/organizationBillingChangePlanController.js"></script>
|
||||||
<script src="app/organization/organizationVaultController.js"></script>
|
<script src="app/organization/organizationVaultController.js"></script>
|
||||||
|
<script src="app/organization/organizationVaultLoginSubvaultsController.js"></script>
|
||||||
|
|
||||||
<script src="app/settings/settingsModule.js"></script>
|
<script src="app/settings/settingsModule.js"></script>
|
||||||
<script src="app/settings/settingsController.js"></script>
|
<script src="app/settings/settingsController.js"></script>
|
||||||
|
|
Loading…
Reference in New Issue