refactor(UAC): refactor common views to components (#1013)

pull/994/head
Anthony Lapenna 2017-07-12 09:51:51 +02:00 committed by GitHub
parent 344eee098d
commit a864641692
30 changed files with 510 additions and 481 deletions

View File

@ -20,8 +20,6 @@ angular.module('portainer', [
'portainer.services',
'auth',
'dashboard',
'common.accesscontrol.panel',
'common.accesscontrol.form',
'container',
'containerConsole',
'containerLogs',

View File

@ -1,126 +0,0 @@
<div ng-controller="AccessControlFormController">
<div class="col-sm-12 form-section-title">
Access control
</div>
<!-- access-control-switch -->
<div class="form-group">
<div class="col-sm-12">
<label for="ownership" class="control-label text-left">
Enable access control
<portainer-tooltip position="bottom" message="When enabled, you can restrict the access and management of this resource."></portainer-tooltip>
</label>
<label class="switch" style="margin-left: 20px;">
<input name="ownership" type="checkbox" ng-model="formValues.enableAccessControl" ng-click="synchronizeFormData()"><i></i>
</label>
</div>
</div>
<!-- !access-control-switch -->
<!-- restricted-access -->
<div class="form-group" ng-if="formValues.enableAccessControl" style="margin-bottom: 0">
<div class="boxselector_wrapper">
<div ng-if="isAdmin">
<input type="radio" id="access_administrators" ng-model="formValues.Ownership" ng-click="synchronizeFormData()" value="administrators">
<label for="access_administrators">
<div class="boxselector_header">
<i ng-class="'administrators' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
Administrators
</div>
<p>I want to restrict the management of this resource to administrators only</p>
</label>
</div>
<div ng-if="isAdmin">
<input type="radio" id="access_restricted" ng-model="formValues.Ownership" ng-click="synchronizeFormData()" value="restricted">
<label for="access_restricted">
<div class="boxselector_header">
<i ng-class="'restricted' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
Restricted
</div>
<p>
I want to restrict the management of this resource to a set of users and/or teams
</p>
</label>
</div>
<div ng-if="!isAdmin">
<input type="radio" id="access_private" ng-model="formValues.Ownership" ng-click="synchronizeFormData()" value="private">
<label for="access_private">
<div class="boxselector_header">
<i ng-class="'private' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
Private
</div>
<p>
I want to this resource to be manageable by myself only
</p>
</label>
</div>
<div ng-if="!isAdmin && availableTeams.length > 0">
<input type="radio" id="access_restricted" ng-model="formValues.Ownership" ng-click="synchronizeFormData()" value="restricted">
<label for="access_restricted">
<div class="boxselector_header">
<i ng-class="'restricted' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
Restricted
</div>
<p ng-if="availableTeams.length === 1">
I want any member of my team (<b>{{ availableTeams[0].Name }}</b>) to be able to manage this resource
</p>
<p ng-if="availableTeams.length > 1">
I want to restrict the management of this resource to one or more of my teams
</p>
</label>
</div>
</div>
</div>
<!-- restricted-access -->
<!-- authorized-teams -->
<div class="form-group" ng-if="formValues.enableAccessControl && formValues.Ownership === 'restricted' && (isAdmin || (!isAdmin && availableTeams.length > 1))" >
<div class="col-sm-12">
<label for="group-access" class="control-label text-left">
Authorized teams
<portainer-tooltip ng-if="isAdmin && availableTeams.length > 0" position="bottom" message="You can select which teams(s) will be able to manage this resource."></portainer-tooltip>
<portainer-tooltip ng-if="!isAdmin && availableTeams.length > 1" position="bottom" message="As you are a member of multiple teams, you can select which teams(s) will be able to manage this resource."></portainer-tooltip>
</label>
<span ng-if="isAdmin && availableTeams.length === 0" class="small text-muted" style="margin-left: 20px;">
You have not yet created any team. Head over the <a ui-sref="teams">teams view</a> to manage user teams.
</span>
<span isteven-multi-select
ng-if="(isAdmin && availableTeams.length > 0) || (!isAdmin && availableTeams.length > 1)"
input-model="availableTeams"
output-model="formValues.Ownership_Teams"
button-label="Name"
item-label="Name"
tick-property="ticked"
helper-elements="filter"
search-property="Name"
on-item-click="synchronizeFormData()"
translation="{nothingSelected: 'Select one or more teams', search: 'Search...'}"
style="margin-left: 20px;">
</span>
</div>
</div>
<!-- !authorized-teams -->
<!-- authorized-users -->
<div class="form-group" ng-if="formValues.enableAccessControl && formValues.Ownership === 'restricted' && isAdmin">
<div class="col-sm-12">
<label for="group-access" class="control-label text-left">
Authorized users
<portainer-tooltip ng-if="isAdmin && availableUsers.length > 0" position="bottom" message="You can select which user(s) will be able to manage this resource."></portainer-tooltip>
</label>
<span ng-if="availableUsers.length === 0" class="small text-muted" style="margin-left: 20px;">
You have not yet created any user. Head over the <a ui-sref="users">users view</a> to manage users.
</span>
<span isteven-multi-select
ng-if="availableUsers.length > 0"
input-model="availableUsers"
output-model="formValues.Ownership_Users"
button-label="Username"
item-label="Username"
tick-property="ticked"
helper-elements="filter"
search-property="Username"
on-item-click="synchronizeFormData()"
translation="{nothingSelected: 'Select one or more users', search: 'Search...'}"
style="margin-left: 20px;">
</span>
</div>
</div>
<!-- !authorized-users -->
</div>

View File

@ -1,55 +0,0 @@
angular.module('common.accesscontrol.form', [])
.controller('AccessControlFormController', ['$q', '$scope', '$state', 'UserService', 'ResourceControlService', 'Notifications', 'Authentication', 'ModalService', 'ControllerDataPipeline',
function ($q, $scope, $state, UserService, ResourceControlService, Notifications, Authentication, ModalService, ControllerDataPipeline) {
$scope.availableTeams = [];
$scope.availableUsers = [];
$scope.formValues = {
enableAccessControl: true,
Ownership_Teams: [],
Ownership_Users: [],
Ownership: 'private'
};
$scope.synchronizeFormData = function() {
ControllerDataPipeline.setAccessControlFormData($scope.formValues.enableAccessControl,
$scope.formValues.Ownership, $scope.formValues.Ownership_Users, $scope.formValues.Ownership_Teams);
};
function initAccessControlForm() {
$('#loadingViewSpinner').show();
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1 ? true: false;
$scope.isAdmin = isAdmin;
if (isAdmin) {
$scope.formValues.Ownership = 'administrators';
}
$q.all({
availableTeams: UserService.userTeams(userDetails.ID),
availableUsers: isAdmin ? UserService.users(false) : []
})
.then(function success(data) {
$scope.availableUsers = data.availableUsers;
var availableTeams = data.availableTeams;
$scope.availableTeams = availableTeams;
if (!isAdmin && availableTeams.length === 1) {
$scope.formValues.Ownership_Teams = availableTeams;
}
$scope.synchronizeFormData();
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve access control information');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
initAccessControlForm();
}]);

View File

@ -1,158 +0,0 @@
angular.module('common.accesscontrol.panel', [])
.controller('AccessControlPanelController', ['$q', '$scope', '$state', 'UserService', 'ResourceControlService', 'Notifications', 'Authentication', 'ModalService', 'ControllerDataPipeline', 'FormValidator',
function ($q, $scope, $state, UserService, ResourceControlService, Notifications, Authentication, ModalService, ControllerDataPipeline, FormValidator) {
$scope.state = {
displayAccessControlPanel: false,
canEditOwnership: false,
editOwnership: false,
formValidationError: ''
};
$scope.formValues = {
Ownership: 'public',
Ownership_Users: [],
Ownership_Teams: []
};
$scope.authorizedUsers = [];
$scope.availableUsers = [];
$scope.authorizedTeams = [];
$scope.availableTeams = [];
$scope.confirmUpdateOwnership = function (force) {
if (!validateForm()) {
return;
}
ModalService.confirmAccessControlUpdate(function (confirmed) {
if(!confirmed) { return; }
updateOwnership();
});
};
function processOwnershipFormValues() {
var userIds = [];
angular.forEach($scope.formValues.Ownership_Users, function(user) {
userIds.push(user.Id);
});
var teamIds = [];
angular.forEach($scope.formValues.Ownership_Teams, function(team) {
teamIds.push(team.Id);
});
var administratorsOnly = $scope.formValues.Ownership === 'administrators' ? true : false;
return {
ownership: $scope.formValues.Ownership,
authorizedUserIds: administratorsOnly ? [] : userIds,
authorizedTeamIds: administratorsOnly ? [] : teamIds,
administratorsOnly: administratorsOnly
};
}
function validateForm() {
$scope.state.formValidationError = '';
var error = '';
var accessControlData = {
ownership: $scope.formValues.Ownership,
authorizedUsers: $scope.formValues.Ownership_Users,
authorizedTeams: $scope.formValues.Ownership_Teams
};
var isAdmin = $scope.isAdmin;
error = FormValidator.validateAccessControl(accessControlData, isAdmin);
if (error) {
$scope.state.formValidationError = error;
return false;
}
return true;
}
function updateOwnership() {
$('#loadingViewSpinner').show();
var accessControlData = ControllerDataPipeline.getAccessControlData();
var resourceId = accessControlData.resourceId;
var ownershipParameters = processOwnershipFormValues();
ResourceControlService.applyResourceControlChange(accessControlData.resourceType, resourceId,
$scope.resourceControl, ownershipParameters)
.then(function success(data) {
Notifications.success('Access control successfully updated');
$state.reload();
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to update access control');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
function initAccessControlPanel() {
$('#loadingViewSpinner').show();
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1 ? true: false;
var userId = userDetails.ID;
$scope.isAdmin = isAdmin;
var accessControlData = ControllerDataPipeline.getAccessControlData();
var resourceControl = accessControlData.resourceControl;
$scope.resourceType = accessControlData.resourceType;
$scope.resourceControl = resourceControl;
if (isAdmin) {
if (resourceControl) {
$scope.formValues.Ownership = resourceControl.Ownership === 'private' ? 'restricted' : resourceControl.Ownership;
} else {
$scope.formValues.Ownership = 'public';
}
} else {
$scope.formValues.Ownership = 'public';
}
ResourceControlService.retrieveOwnershipDetails(resourceControl)
.then(function success(data) {
$scope.authorizedUsers = data.authorizedUsers;
$scope.authorizedTeams = data.authorizedTeams;
return ResourceControlService.retrieveUserPermissionsOnResource(userId, isAdmin, resourceControl);
})
.then(function success(data) {
$scope.state.canEditOwnership = data.isPartOfRestrictedUsers || data.isLeaderOfAnyRestrictedTeams;
$scope.state.canChangeOwnershipToTeam = data.isPartOfRestrictedUsers;
return $q.all({
availableUsers: isAdmin ? UserService.users(false) : [],
availableTeams: isAdmin || data.isPartOfRestrictedUsers ? UserService.userTeams(userId) : []
});
})
.then(function success(data) {
$scope.availableUsers = data.availableUsers;
angular.forEach($scope.availableUsers, function(user) {
var found = _.find($scope.authorizedUsers, { Id: user.Id });
if (found) {
user.selected = true;
}
});
$scope.availableTeams = data.availableTeams;
angular.forEach(data.availableTeams, function(team) {
var found = _.find($scope.authorizedTeams, { Id: team.Id });
if (found) {
team.selected = true;
}
});
if (data.availableTeams.length === 1) {
$scope.formValues.Ownership_Teams.push(data.availableTeams[0]);
}
$scope.state.displayAccessControlPanel = true;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve access control information');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
initAccessControlPanel();
}]);

View File

@ -87,7 +87,13 @@
</div>
</div>
<div ng-include="'app/components/common/accessControlPanel/accessControlPanel.html'" ng-if="container && applicationState.application.authentication"></div>
<!-- access-control-panel -->
<por-access-control-panel
ng-if="container && applicationState.application.authentication"
resource-control="container.ResourceControl"
resource-type="'container'">
</por-access-control-panel>
<!-- !access-control-panel -->
<div ng-if="container.State.Health" class="row">
<div class="col-lg-12 col-md-12 col-xs-12">

View File

@ -1,6 +1,6 @@
angular.module('container', [])
.controller('ContainerController', ['$scope', '$state','$stateParams', '$filter', 'Container', 'ContainerCommit', 'ContainerService', 'ImageHelper', 'Network', 'Notifications', 'Pagination', 'ModalService', 'ControllerDataPipeline',
function ($scope, $state, $stateParams, $filter, Container, ContainerCommit, ContainerService, ImageHelper, Network, Notifications, Pagination, ModalService, ControllerDataPipeline) {
.controller('ContainerController', ['$scope', '$state','$stateParams', '$filter', 'Container', 'ContainerCommit', 'ContainerService', 'ImageHelper', 'Network', 'Notifications', 'Pagination', 'ModalService',
function ($scope, $state, $stateParams, $filter, Container, ContainerCommit, ContainerService, ImageHelper, Network, Notifications, Pagination, ModalService) {
$scope.activityTime = 0;
$scope.portBindings = [];
$scope.config = {
@ -19,7 +19,6 @@ function ($scope, $state, $stateParams, $filter, Container, ContainerCommit, Con
Container.get({id: $stateParams.id}, function (d) {
var container = new ContainerDetailsViewModel(d);
$scope.container = container;
ControllerDataPipeline.setAccessControlData('container', $stateParams.id, container.ResourceControl);
$scope.container.edit = false;
$scope.container.newContainerName = $filter('trimcontainername')(container.Name);

View File

@ -1,8 +1,8 @@
// @@OLD_SERVICE_CONTROLLER: this service should be rewritten to use services.
// See app/components/templates/templatesController.js as a reference.
angular.module('createContainer', [])
.controller('CreateContainerController', ['$q', '$scope', '$state', '$stateParams', '$filter', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'Network', 'ResourceControlService', 'Authentication', 'Notifications', 'ContainerService', 'ImageService', 'ControllerDataPipeline', 'FormValidator',
function ($q, $scope, $state, $stateParams, $filter, Container, ContainerHelper, Image, ImageHelper, Volume, Network, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, ControllerDataPipeline, FormValidator) {
.controller('CreateContainerController', ['$q', '$scope', '$state', '$stateParams', '$filter', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'Network', 'ResourceControlService', 'Authentication', 'Notifications', 'ContainerService', 'ImageService', 'FormValidator',
function ($q, $scope, $state, $stateParams, $filter, Container, ContainerHelper, Image, ImageHelper, Volume, Network, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, FormValidator) {
$scope.formValues = {
alwaysPull: true,
@ -13,7 +13,8 @@ function ($q, $scope, $state, $stateParams, $filter, Container, ContainerHelper,
Labels: [],
ExtraHosts: [],
IPv4: '',
IPv6: ''
IPv6: '',
AccessControlData: new AccessControlFormData()
};
$scope.state = {
@ -285,7 +286,7 @@ function ($q, $scope, $state, $stateParams, $filter, Container, ContainerHelper,
$scope.create = function () {
$('#createContainerSpinner').show();
var accessControlData = ControllerDataPipeline.getAccessControlFormData();
var accessControlData = $scope.formValues.AccessControlData;
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1 ? true : false;

View File

@ -98,7 +98,7 @@
</div>
<!-- !port-mapping -->
<!-- access-control -->
<div ng-include="'app/components/common/accessControlForm/accessControlForm.html'" ng-if="applicationState.application.authentication"></div>
<por-access-control-form form-data="formValues.AccessControlData"></por-access-control-form>
<!-- !access-control -->
<!-- actions -->
<div class="col-sm-12 form-section-title">

View File

@ -1,8 +1,8 @@
// @@OLD_SERVICE_CONTROLLER: this service should be rewritten to use services.
// See app/components/templates/templatesController.js as a reference.
angular.module('createService', [])
.controller('CreateServiceController', ['$q', '$scope', '$state', 'Service', 'ServiceHelper', 'SecretHelper', 'SecretService', 'VolumeService', 'NetworkService', 'ImageHelper', 'LabelHelper', 'Authentication', 'ResourceControlService', 'Notifications', 'ControllerDataPipeline', 'FormValidator', 'RegistryService', 'HttpRequestHelper',
function ($q, $scope, $state, Service, ServiceHelper, SecretHelper, SecretService, VolumeService, NetworkService, ImageHelper, LabelHelper, Authentication, ResourceControlService, Notifications, ControllerDataPipeline, FormValidator, RegistryService, HttpRequestHelper) {
.controller('CreateServiceController', ['$q', '$scope', '$state', 'Service', 'ServiceHelper', 'SecretHelper', 'SecretService', 'VolumeService', 'NetworkService', 'ImageHelper', 'LabelHelper', 'Authentication', 'ResourceControlService', 'Notifications', 'FormValidator', 'RegistryService', 'HttpRequestHelper',
function ($q, $scope, $state, Service, ServiceHelper, SecretHelper, SecretService, VolumeService, NetworkService, ImageHelper, LabelHelper, Authentication, ResourceControlService, Notifications, FormValidator, RegistryService, HttpRequestHelper) {
$scope.formValues = {
Name: '',
@ -26,7 +26,8 @@ function ($q, $scope, $state, Service, ServiceHelper, SecretHelper, SecretServic
PlacementPreferences: [],
UpdateDelay: 0,
FailureAction: 'pause',
Secrets: []
Secrets: [],
AccessControlData: new AccessControlFormData()
};
$scope.state = {
@ -286,7 +287,7 @@ function ($q, $scope, $state, Service, ServiceHelper, SecretHelper, SecretServic
$scope.create = function createService() {
$('#createServiceSpinner').show();
var accessControlData = ControllerDataPipeline.getAccessControlFormData();
var accessControlData = $scope.formValues.AccessControlData;
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1 ? true : false;

View File

@ -101,7 +101,7 @@
</div>
<!-- !port-mapping -->
<!-- access-control -->
<div ng-include="'app/components/common/accessControlForm/accessControlForm.html'" ng-if="applicationState.application.authentication"></div>
<por-access-control-form form-data="formValues.AccessControlData"></por-access-control-form>
<!-- !access-control -->
<!-- actions -->
<div class="col-sm-12 form-section-title">

View File

@ -1,10 +1,11 @@
angular.module('createVolume', [])
.controller('CreateVolumeController', ['$scope', '$state', 'VolumeService', 'SystemService', 'ResourceControlService', 'Authentication', 'Notifications', 'ControllerDataPipeline', 'FormValidator',
function ($scope, $state, VolumeService, SystemService, ResourceControlService, Authentication, Notifications, ControllerDataPipeline, FormValidator) {
.controller('CreateVolumeController', ['$scope', '$state', 'VolumeService', 'SystemService', 'ResourceControlService', 'Authentication', 'Notifications', 'FormValidator',
function ($scope, $state, VolumeService, SystemService, ResourceControlService, Authentication, Notifications, FormValidator) {
$scope.formValues = {
Driver: 'local',
DriverOptions: []
DriverOptions: [],
AccessControlData: new AccessControlFormData()
};
$scope.state = {
@ -40,8 +41,8 @@ function ($scope, $state, VolumeService, SystemService, ResourceControlService,
var driver = $scope.formValues.Driver;
var driverOptions = $scope.formValues.DriverOptions;
var volumeConfiguration = VolumeService.createVolumeConfiguration(name, driver, driverOptions);
var accessControlData = $scope.formValues.AccessControlData;
var userDetails = Authentication.getUserDetails();
var accessControlData = ControllerDataPipeline.getAccessControlFormData();
var isAdmin = userDetails.role === 1 ? true : false;
if (!validateForm(accessControlData, isAdmin)) {

View File

@ -65,7 +65,7 @@
</div>
<!-- !driver-options -->
<!-- access-control -->
<div ng-include="'app/components/common/accessControlForm/accessControlForm.html'" ng-if="applicationState.application.authentication"></div>
<por-access-control-form form-data="formValues.AccessControlData"></por-access-control-form>
<!-- !access-control -->
<!-- actions -->
<div class="col-sm-12 form-section-title">

View File

@ -125,7 +125,13 @@
</div>
</div>
<div ng-include="'app/components/common/accessControlPanel/accessControlPanel.html'" ng-if="service && applicationState.application.authentication"></div>
<!-- access-control-panel -->
<por-access-control-panel
ng-if="service && applicationState.application.authentication"
resource-control="service.ResourceControl"
resource-type="'service'">
</por-access-control-panel>
<!-- !access-control-panel -->
<div class="row">
<hr>

View File

@ -1,6 +1,6 @@
angular.module('service', [])
.controller('ServiceController', ['$q', '$scope', '$stateParams', '$state', '$location', '$timeout', '$anchorScroll', 'ServiceService', 'Secret', 'SecretHelper', 'Service', 'ServiceHelper', 'LabelHelper', 'TaskService', 'NodeService', 'Notifications', 'Pagination', 'ModalService', 'ControllerDataPipeline',
function ($q, $scope, $stateParams, $state, $location, $timeout, $anchorScroll, ServiceService, Secret, SecretHelper, Service, ServiceHelper, LabelHelper, TaskService, NodeService, Notifications, Pagination, ModalService, ControllerDataPipeline) {
.controller('ServiceController', ['$q', '$scope', '$stateParams', '$state', '$location', '$timeout', '$anchorScroll', 'ServiceService', 'Secret', 'SecretHelper', 'Service', 'ServiceHelper', 'LabelHelper', 'TaskService', 'NodeService', 'Notifications', 'Pagination', 'ModalService',
function ($q, $scope, $stateParams, $state, $location, $timeout, $anchorScroll, ServiceService, Secret, SecretHelper, Service, ServiceHelper, LabelHelper, TaskService, NodeService, Notifications, Pagination, ModalService) {
$scope.state = {};
$scope.state.pagination_count = Pagination.getPaginationCount('service_tasks');
@ -299,7 +299,6 @@ function ($q, $scope, $stateParams, $state, $location, $timeout, $anchorScroll,
translateServiceArrays(service);
$scope.service = service;
ControllerDataPipeline.setAccessControlData('service', $stateParams.id, service.ResourceControl);
originalService = angular.copy(service);
return $q.all({

View File

@ -68,7 +68,7 @@
</div>
<!-- !env -->
<!-- access-control -->
<div ng-include="'app/components/common/accessControlForm/accessControlForm.html'" ng-if="applicationState.application.authentication"></div>
<por-access-control-form form-data="formValues.AccessControlData"></por-access-control-form>
<!-- !access-control -->
<div class="form-group">
<div class="col-sm-12">
@ -202,13 +202,14 @@
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!formValues.network" ng-click="createTemplate()">Create</button>
<i id="createContainerSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<span class="small text-muted" style="margin-left: 10px" ng-if="globalNetworkCount === 0 && applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">
<span class="small text-muted" style="margin-left: 10px" ng-if="globalNetworkCount === 0 && applicationState.endpoint.mode.provider === 'DOCKER_SWARM' && !state.formValidationError">
When using Swarm, we recommend deploying containers in a shared network. Looks like you don't have any shared network, head over the <a ui-sref="networks">networks view</a> to create one.
</span>
<span ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'" style="margin-left: 10px">
<span ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && !state.formValidationError" style="margin-left: 10px">
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
<span class="small text-muted" style="margin-left: 5px;">App templates cannot be deployed as Swarm Mode services for the moment. You can still use them to quickly deploy containers on the Docker host.</span>
</span>
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span>
</div>
</div>
<!-- !actions -->

View File

@ -1,6 +1,6 @@
angular.module('templates', [])
.controller('TemplatesController', ['$scope', '$q', '$state', '$stateParams', '$anchorScroll', '$filter', 'ContainerService', 'ContainerHelper', 'ImageService', 'NetworkService', 'TemplateService', 'TemplateHelper', 'VolumeService', 'Notifications', 'Pagination', 'ResourceControlService', 'Authentication', 'ControllerDataPipeline', 'FormValidator',
function ($scope, $q, $state, $stateParams, $anchorScroll, $filter, ContainerService, ContainerHelper, ImageService, NetworkService, TemplateService, TemplateHelper, VolumeService, Notifications, Pagination, ResourceControlService, Authentication, ControllerDataPipeline, FormValidator) {
.controller('TemplatesController', ['$scope', '$q', '$state', '$stateParams', '$anchorScroll', '$filter', 'ContainerService', 'ContainerHelper', 'ImageService', 'NetworkService', 'TemplateService', 'TemplateHelper', 'VolumeService', 'Notifications', 'Pagination', 'ResourceControlService', 'Authentication', 'FormValidator',
function ($scope, $q, $state, $stateParams, $anchorScroll, $filter, ContainerService, ContainerHelper, ImageService, NetworkService, TemplateService, TemplateHelper, VolumeService, Notifications, Pagination, ResourceControlService, Authentication, FormValidator) {
$scope.state = {
selectedTemplate: null,
showAdvancedOptions: false,
@ -14,7 +14,8 @@ function ($scope, $q, $state, $stateParams, $anchorScroll, $filter, ContainerSer
$scope.formValues = {
network: '',
name: ''
name: '',
AccessControlData: new AccessControlFormData()
};
$scope.addVolume = function () {
@ -49,7 +50,7 @@ function ($scope, $q, $state, $stateParams, $anchorScroll, $filter, ContainerSer
$('#createContainerSpinner').show();
var userDetails = Authentication.getUserDetails();
var accessControlData = ControllerDataPipeline.getAccessControlFormData();
var accessControlData = $scope.formValues.AccessControlData;
var isAdmin = userDetails.role === 1 ? true : false;
if (!validateForm(accessControlData, isAdmin)) {

View File

@ -47,7 +47,13 @@
</div>
</div>
<div ng-include="'app/components/common/accessControlPanel/accessControlPanel.html'" ng-if="volume && applicationState.application.authentication"></div>
<!-- access-control-panel -->
<por-access-control-panel
ng-if="volume && applicationState.application.authentication"
resource-control="volume.ResourceControl"
resource-type="'volume'">
</por-access-control-panel>
<!-- !access-control-panel -->
<div class="row" ng-if="!(volume.Options | emptyobject)">
<div class="col-lg-12 col-md-12 col-xs-12">

View File

@ -1,6 +1,6 @@
angular.module('volume', [])
.controller('VolumeController', ['$scope', '$state', '$stateParams', 'VolumeService', 'Notifications', 'ControllerDataPipeline',
function ($scope, $state, $stateParams, VolumeService, Notifications, ControllerDataPipeline) {
.controller('VolumeController', ['$scope', '$state', '$stateParams', 'VolumeService', 'Notifications',
function ($scope, $state, $stateParams, VolumeService, Notifications) {
$scope.removeVolume = function removeVolume() {
$('#loadingViewSpinner').show();
@ -22,7 +22,6 @@ function ($scope, $state, $stateParams, VolumeService, Notifications, Controller
VolumeService.volume($stateParams.id)
.then(function success(data) {
var volume = data;
ControllerDataPipeline.setAccessControlData('volume', volume.Id, volume.ResourceControl);
$scope.volume = volume;
})
.catch(function error(err) {

View File

@ -0,0 +1,12 @@
angular.module('portainer').component('porAccessControlForm', {
templateUrl: 'app/directives/accessControlForm/porAccessControlForm.html',
controller: 'porAccessControlFormController',
bindings: {
// This object will be populated with the form data.
// Model reference in porAccessControlFromModel.js
formData: '=',
// Optional. An existing resource control object that will be used to set
// the default values of the component.
resourceControl: '<'
}
});

View File

@ -0,0 +1,124 @@
<div>
<div class="col-sm-12 form-section-title">
Access control
</div>
<!-- access-control-switch -->
<div class="form-group">
<div class="col-sm-12">
<label for="ownership" class="control-label text-left">
Enable access control
<portainer-tooltip position="bottom" message="When enabled, you can restrict the access and management of this resource."></portainer-tooltip>
</label>
<label class="switch" style="margin-left: 20px;">
<input name="ownership" type="checkbox" ng-model="$ctrl.formData.AccessControlEnabled"><i></i>
</label>
</div>
</div>
<!-- !access-control-switch -->
<!-- restricted-access -->
<div class="form-group" ng-if="$ctrl.formData.AccessControlEnabled" style="margin-bottom: 0">
<div class="boxselector_wrapper">
<div ng-if="$ctrl.isAdmin">
<input type="radio" id="access_administrators" ng-model="$ctrl.formData.Ownership" value="administrators">
<label for="access_administrators">
<div class="boxselector_header">
<i ng-class="'administrators' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
Administrators
</div>
<p>I want to restrict the management of this resource to administrators only</p>
</label>
</div>
<div ng-if="$ctrl.isAdmin">
<input type="radio" id="access_restricted" ng-model="$ctrl.formData.Ownership" value="restricted">
<label for="access_restricted">
<div class="boxselector_header">
<i ng-class="'restricted' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
Restricted
</div>
<p>
I want to restrict the management of this resource to a set of users and/or teams
</p>
</label>
</div>
<div ng-if="!$ctrl.isAdmin">
<input type="radio" id="access_private" ng-model="$ctrl.formData.Ownership" value="private">
<label for="access_private">
<div class="boxselector_header">
<i ng-class="'private' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
Private
</div>
<p>
I want to this resource to be manageable by myself only
</p>
</label>
</div>
<div ng-if="!$ctrl.isAdmin && $ctrl.availableTeams.length > 0">
<input type="radio" id="access_restricted" ng-model="$ctrl.formData.Ownership" value="restricted">
<label for="access_restricted">
<div class="boxselector_header">
<i ng-class="'restricted' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
Restricted
</div>
<p ng-if="$ctrl.availableTeams.length === 1">
I want any member of my team (<b>{{ $ctrl.availableTeams[0].Name }}</b>) to be able to manage this resource
</p>
<p ng-if="$ctrl.availableTeams.length > 1">
I want to restrict the management of this resource to one or more of my teams
</p>
</label>
</div>
</div>
</div>
<!-- restricted-access -->
<!-- authorized-teams -->
<div class="form-group" ng-if="$ctrl.formData.AccessControlEnabled && $ctrl.formData.Ownership === 'restricted' && ($ctrl.isAdmin || (!$ctrl.isAdmin && $ctrl.availableTeams.length > 1))" >
<div class="col-sm-12">
<label for="group-access" class="control-label text-left">
Authorized teams
<portainer-tooltip ng-if="$ctrl.isAdmin && $ctrl.availableTeams.length > 0" position="bottom" message="You can select which teams(s) will be able to manage this resource."></portainer-tooltip>
<portainer-tooltip ng-if="!$ctrl.isAdmin && $ctrl.availableTeams.length > 1" position="bottom" message="As you are a member of multiple teams, you can select which teams(s) will be able to manage this resource."></portainer-tooltip>
</label>
<span ng-if="$ctrl.isAdmin && $ctrl.availableTeams.length === 0" class="small text-muted" style="margin-left: 20px;">
You have not yet created any team. Head over the <a ui-sref="teams">teams view</a> to manage user teams.
</span>
<span isteven-multi-select
ng-if="($ctrl.isAdmin && $ctrl.availableTeams.length > 0) || (!$ctrl.isAdmin && $ctrl.availableTeams.length > 1)"
input-model="$ctrl.availableTeams"
output-model="$ctrl.formData.AuthorizedTeams"
button-label="Name"
item-label="Name"
tick-property="selected"
helper-elements="filter"
search-property="Name"
translation="{nothingSelected: 'Select one or more teams', search: 'Search...'}"
style="margin-left: 20px;">
</span>
</div>
</div>
<!-- !authorized-teams -->
<!-- authorized-users -->
<div class="form-group" ng-if="$ctrl.formData.AccessControlEnabled && $ctrl.formData.Ownership === 'restricted' && $ctrl.isAdmin">
<div class="col-sm-12">
<label for="group-access" class="control-label text-left">
Authorized users
<portainer-tooltip ng-if="$ctrl.isAdmin && $ctrl.availableUsers.length > 0" position="bottom" message="You can select which user(s) will be able to manage this resource."></portainer-tooltip>
</label>
<span ng-if="$ctrl.availableUsers.length === 0" class="small text-muted" style="margin-left: 20px;">
You have not yet created any user. Head over the <a ui-sref="users">users view</a> to manage users.
</span>
<span isteven-multi-select
ng-if="$ctrl.availableUsers.length > 0"
input-model="$ctrl.availableUsers"
output-model="$ctrl.formData.AuthorizedUsers"
button-label="Username"
item-label="Username"
tick-property="selected"
helper-elements="filter"
search-property="Username"
translation="{nothingSelected: 'Select one or more users', search: 'Search...'}"
style="margin-left: 20px;">
</span>
</div>
</div>
<!-- !authorized-users -->
</div>

View File

@ -0,0 +1,76 @@
angular.module('portainer')
.controller('porAccessControlFormController', ['$q', 'UserService', 'Notifications', 'Authentication', 'ResourceControlService',
function ($q, UserService, Notifications, Authentication, ResourceControlService) {
var ctrl = this;
ctrl.availableTeams = [];
ctrl.availableUsers = [];
function setOwnership(resourceControl, isAdmin) {
if (isAdmin && resourceControl.Ownership === 'private') {
ctrl.formData.Ownership = 'restricted';
} else {
ctrl.formData.Ownership = resourceControl.Ownership;
}
}
function setAuthorizedUsersAndTeams(authorizedUsers, authorizedTeams) {
angular.forEach(ctrl.availableUsers, function(user) {
var found = _.find(authorizedUsers, { Id: user.Id });
if (found) {
user.selected = true;
}
});
angular.forEach(ctrl.availableTeams, function(team) {
var found = _.find(authorizedTeams, { Id: team.Id });
if (found) {
team.selected = true;
}
});
}
function initComponent() {
$('#loadingViewSpinner').show();
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1 ? true: false;
ctrl.isAdmin = isAdmin;
if (isAdmin) {
ctrl.formData.Ownership = 'administrators';
}
$q.all({
availableTeams: UserService.userTeams(userDetails.ID),
availableUsers: isAdmin ? UserService.users(false) : []
})
.then(function success(data) {
ctrl.availableUsers = data.availableUsers;
var availableTeams = data.availableTeams;
ctrl.availableTeams = availableTeams;
if (!isAdmin && availableTeams.length === 1) {
ctrl.formData.AuthorizedTeams = availableTeams;
}
return $q.when(ctrl.resourceControl && ResourceControlService.retrieveOwnershipDetails(ctrl.resourceControl));
})
.then(function success(data) {
if (data) {
var authorizedUsers = data.authorizedUsers;
var authorizedTeams = data.authorizedTeams;
setOwnership(ctrl.resourceControl, isAdmin);
setAuthorizedUsersAndTeams(authorizedUsers, authorizedTeams);
}
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve access control information');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
initComponent();
}]);

View File

@ -0,0 +1,6 @@
function AccessControlFormData() {
this.AccessControlEnabled = true;
this.Ownership = 'private';
this.AuthorizedUsers = [];
this.AuthorizedTeams = [];
}

View File

@ -0,0 +1,12 @@
angular.module('portainer').component('porAccessControlPanel', {
templateUrl: 'app/directives/accessControlPanel/porAccessControlPanel.html',
controller: 'porAccessControlPanelController',
bindings: {
// The component will display information about this resource control object.
resourceControl: '=',
// This component is usually displayed inside a resource-details view.
// This variable specifies the type of the associated resource.
// Accepted values: 'container', 'service' or 'volume'.
resourceType: '<'
}
});

View File

@ -1,5 +1,5 @@
<div class="row" ng-controller="AccessControlPanelController">
<div class="col-sm-12" ng-if="state.displayAccessControlPanel">
<div class="row">
<div class="col-sm-12" ng-if="$ctrl.state.displayAccessControlPanel">
<rd-widget>
<rd-widget-header icon="fa-eye" title="Access control"></rd-widget-header>
<rd-widget-body classes="no-padding">
@ -9,63 +9,63 @@
<tr>
<td>Ownership</td>
<td>
<i ng-class="resourceControl.Ownership | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
<span ng-if="!resourceControl">
<i ng-class="$ctrl.resourceControl.Ownership | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
<span ng-if="!$ctrl.resourceControl">
public
<portainer-tooltip message="This resource can be managed by any user with access to this endpoint." position="bottom" style="margin-left: -3px;"></portainer-tooltip>
</span>
<span ng-if="resourceControl">
{{ resourceControl.Ownership }}
<portainer-tooltip ng-if="resourceControl.Ownership === 'administrators'" message="This resource can only be managed by administrators." position="bottom" style="margin-left: -3px;"></portainer-tooltip>
<portainer-tooltip ng-if="resourceControl.Ownership === 'private'" message="Management of this resource is restricted to a single user." position="bottom" style="margin-left: -3px;"></portainer-tooltip>
<portainer-tooltip ng-if="resourceControl.Ownership === 'restricted'" message="This resource can be managed by a restricted set of users and/or teams." position="bottom" style="margin-left: -3px;"></portainer-tooltip>
<span ng-if="$ctrl.resourceControl">
{{ $ctrl.resourceControl.Ownership }}
<portainer-tooltip ng-if="$ctrl.resourceControl.Ownership === 'administrators'" message="This resource can only be managed by administrators." position="bottom" style="margin-left: -3px;"></portainer-tooltip>
<portainer-tooltip ng-if="$ctrl.resourceControl.Ownership === 'private'" message="Management of this resource is restricted to a single user." position="bottom" style="margin-left: -3px;"></portainer-tooltip>
<portainer-tooltip ng-if="$ctrl.resourceControl.Ownership === 'restricted'" message="This resource can be managed by a restricted set of users and/or teams." position="bottom" style="margin-left: -3px;"></portainer-tooltip>
</span>
</td>
</tr>
<!-- !ownership -->
<tr ng-if="resourceControl.Type === 2 && resourceType === 'container'">
<tr ng-if="$ctrl.resourceControl.Type === 2 && $ctrl.resourceType === 'container'">
<td colspan="2">
<i class="fa fa-info-circle" aria-hidden="true" style="margin-right: 2px;"></i>
Access control on this resource is inherited from the following service: <a ui-sref="service({ id: resourceControl.ResourceId })">{{ resourceControl.ResourceId | truncate }}</a>
Access control on this resource is inherited from the following service: <a ui-sref="service({ id: $ctrl.resourceControl.ResourceId })">{{ $ctrl.resourceControl.ResourceId | truncate }}</a>
<portainer-tooltip message="Access control applied on a service is also applied on each container of that service." position="bottom" style="margin-left: 2px;"></portainer-tooltip>
</td>
</tr>
<tr ng-if="resourceControl.Type === 1 && resourceType === 'volume'">
<tr ng-if="$ctrl.resourceControl.Type === 1 && $ctrl.resourceType === 'volume'">
<td colspan="2">
<i class="fa fa-info-circle" aria-hidden="true" style="margin-right: 2px;"></i>
Access control on this resource is inherited from the following container: <a ui-sref="container({ id: resourceControl.ResourceId })">{{ resourceControl.ResourceId | truncate }}</a>
Access control on this resource is inherited from the following container: <a ui-sref="container({ id: $ctrl.resourceControl.ResourceId })">{{ $ctrl.resourceControl.ResourceId | truncate }}</a>
<portainer-tooltip message="Access control applied on a container created using a template is also applied on each volume associated to the container." position="bottom" style="margin-left: 2px;"></portainer-tooltip>
</td>
</tr>
<!-- authorized-users -->
<tr ng-if="resourceControl.UserAccesses.length > 0">
<tr ng-if="$ctrl.resourceControl.UserAccesses.length > 0">
<td>Authorized users</td>
<td>
<span ng-repeat="user in authorizedUsers">{{user.Username}}{{$last ? '' : ', '}} </span>
<span ng-repeat="user in $ctrl.authorizedUsers">{{user.Username}}{{$last ? '' : ', '}} </span>
</td>
</tr>
<!-- !authorized-users -->
<!-- authorized-teams -->
<tr ng-if="resourceControl.TeamAccesses.length > 0">
<tr ng-if="$ctrl.resourceControl.TeamAccesses.length > 0">
<td>Authorized teams</td>
<td>
<span ng-repeat="team in authorizedTeams">{{team.Name}}{{$last ? '' : ', '}} </span>
<span ng-repeat="team in $ctrl.authorizedTeams">{{team.Name}}{{$last ? '' : ', '}} </span>
</td>
</tr>
<!-- !authorized-teams -->
<!-- edit-ownership -->
<tr ng-if="!(resourceControl.Type === 1 && resourceType === 'volume') && !(resourceControl.Type === 2 && resourceType === 'container') && !state.editOwnership && (isAdmin || state.canEditOwnership)">
<tr ng-if="!($ctrl.resourceControl.Type === 1 && $ctrl.resourceType === 'volume') && !($ctrl.resourceControl.Type === 2 && $ctrl.resourceType === 'container') && !$ctrl.state.editOwnership && ($ctrl.isAdmin || $ctrl.state.canEditOwnership)">
<td colspan="2">
<a class="btn-outline-secondary" ng-click="state.editOwnership = true"><i class="fa fa-edit space-right" aria-hidden="true"></i>Change ownership</a>
<a class="btn-outline-secondary" ng-click="$ctrl.state.editOwnership = true"><i class="fa fa-edit space-right" aria-hidden="true"></i>Change ownership</a>
</td>
</tr>
<!-- !edit-ownership -->
<!-- edit-ownership-choices -->
<tr ng-if="state.editOwnership">
<tr ng-if="$ctrl.state.editOwnership">
<td colspan="2">
<div class="boxselector_wrapper">
<div ng-if="isAdmin">
<input type="radio" id="access_administrators" ng-model="formValues.Ownership" value="administrators">
<div ng-if="$ctrl.isAdmin">
<input type="radio" id="access_administrators" ng-model="$ctrl.formValues.Ownership" value="administrators">
<label for="access_administrators">
<div class="boxselector_header">
<i ng-class="'administrators' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
@ -74,8 +74,8 @@
<p>I want to restrict the management of this resource to administrators only</p>
</label>
</div>
<div ng-if="isAdmin">
<input type="radio" id="access_restricted" ng-model="formValues.Ownership" value="restricted">
<div ng-if="$ctrl.isAdmin">
<input type="radio" id="access_restricted" ng-model="$ctrl.formValues.Ownership" value="restricted">
<label for="access_restricted">
<div class="boxselector_header">
<i ng-class="'restricted' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
@ -86,23 +86,23 @@
</p>
</label>
</div>
<div ng-if="!isAdmin && state.canChangeOwnershipToTeam && availableTeams.length > 0">
<input type="radio" id="access_restricted" ng-model="formValues.Ownership" value="restricted">
<div ng-if="!$ctrl.isAdmin && $ctrl.state.canChangeOwnershipToTeam && $ctrl.availableTeams.length > 0">
<input type="radio" id="access_restricted" ng-model="$ctrl.formValues.Ownership" value="restricted">
<label for="access_restricted">
<div class="boxselector_header">
<i ng-class="'restricted' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
Restricted
</div>
<p ng-if="availableTeams.length === 1">
I want any member of my team (<b>{{ availableTeams[0].Name }}</b>) to be able to manage this resource
<p ng-if="$ctrl.availableTeams.length === 1">
I want any member of my team (<b>{{ $ctrl.availableTeams[0].Name }}</b>) to be able to manage this resource
</p>
<p ng-if="availableTeams.length > 1">
<p ng-if="$ctrl.availableTeams.length > 1">
I want to restrict the management of this resource to one or more of my teams
</p>
</label>
</div>
<div>
<input type="radio" id="access_public" ng-model="formValues.Ownership" value="public">
<input type="radio" id="access_public" ng-model="$ctrl.formValues.Ownership" value="public">
<label for="access_public">
<div class="boxselector_header">
<i ng-class="'public' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
@ -116,16 +116,16 @@
</tr>
<!-- edit-ownership-choices -->
<!-- select-teams -->
<tr ng-if="state.editOwnership && formValues.Ownership === 'restricted' && (isAdmin || !isAdmin && availableTeams.length > 1)">
<tr ng-if="$ctrl.state.editOwnership && $ctrl.formValues.Ownership === 'restricted' && ($ctrl.isAdmin || !$ctrl.isAdmin && $ctrl.availableTeams.length > 1)">
<td colspan="2">
<span>Teams</span>
<span ng-if="isAdmin && availableTeams.length === 0" class="small text-muted" style="margin-left: 10px;">
<span ng-if="$ctrl.isAdmin && $ctrl.availableTeams.length === 0" class="small text-muted" style="margin-left: 10px;">
You have not yet created any team. Head over the <a ui-sref="teams">teams view</a> to manage user teams.
</span>
<span isteven-multi-select
ng-if="(isAdmin && availableTeams.length > 0) || (!isAdmin && availableTeams.length > 1)"
input-model="availableTeams"
output-model="formValues.Ownership_Teams"
ng-if="($ctrl.isAdmin && $ctrl.availableTeams.length > 0) || (!$ctrl.isAdmin && $ctrl.availableTeams.length > 1)"
input-model="$ctrl.availableTeams"
output-model="$ctrl.formValues.Ownership_Teams"
button-label="Name"
item-label="Name"
tick-property="selected"
@ -138,16 +138,16 @@
</tr>
<!-- !select-teams -->
<!-- select-users -->
<tr ng-if="isAdmin && state.editOwnership && formValues.Ownership === 'restricted'">
<tr ng-if="$ctrl.isAdmin && $ctrl.state.editOwnership && $ctrl.formValues.Ownership === 'restricted'">
<td colspan="2">
<span>Users</span>
<span ng-if="availableUsers.length === 0" class="small text-muted" style="margin-left: 10px;">
<span ng-if="$ctrl.availableUsers.length === 0" class="small text-muted" style="margin-left: 10px;">
You have not yet created any user. Head over the <a ui-sref="users">users view</a> to manage users.
</span>
<span isteven-multi-select
ng-if="availableUsers.length > 0"
input-model="availableUsers"
output-model="formValues.Ownership_Users"
ng-if="$ctrl.availableUsers.length > 0"
input-model="$ctrl.availableUsers"
output-model="$ctrl.formValues.Ownership_Users"
button-label="Username"
item-label="Username"
tick-property="selected"
@ -160,12 +160,12 @@
</tr>
<!-- !select-users -->
<!-- ownership-actions -->
<tr ng-if="state.editOwnership">
<tr ng-if="$ctrl.state.editOwnership">
<td colspan="2">
<div>
<a type="button" class="btn btn-default btn-sm" ng-click="state.editOwnership = false">Cancel</a>
<a type="button" class="btn btn-primary btn-sm" ng-click="confirmUpdateOwnership()">Update ownership</a>
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span>
<a type="button" class="btn btn-default btn-sm" ng-click="$ctrl.state.editOwnership = false">Cancel</a>
<a type="button" class="btn btn-primary btn-sm" ng-click="$ctrl.confirmUpdateOwnership()">Update ownership</a>
<span class="text-danger" ng-if="$ctrl.state.formValidationError" style="margin-left: 5px;">{{ $ctrl.state.formValidationError }}</span>
</div>
</td>
</tr>

View File

@ -0,0 +1,156 @@
angular.module('portainer')
.controller('porAccessControlPanelController', ['$q', '$state', 'UserService', 'ResourceControlService', 'Notifications', 'Authentication', 'ModalService', 'FormValidator',
function ($q, $state, UserService, ResourceControlService, Notifications, Authentication, ModalService, FormValidator) {
var ctrl = this;
ctrl.state = {
displayAccessControlPanel: false,
canEditOwnership: false,
editOwnership: false,
formValidationError: ''
};
ctrl.formValues = {
Ownership: 'public',
Ownership_Users: [],
Ownership_Teams: []
};
ctrl.authorizedUsers = [];
ctrl.availableUsers = [];
ctrl.authorizedTeams = [];
ctrl.availableTeams = [];
ctrl.confirmUpdateOwnership = function (force) {
if (!validateForm()) {
return;
}
ModalService.confirmAccessControlUpdate(function (confirmed) {
if(!confirmed) { return; }
updateOwnership();
});
};
function validateForm() {
ctrl.state.formValidationError = '';
var error = '';
var accessControlData = {
AccessControlEnabled: ctrl.formValues.Ownership === 'public' ? false : true,
Ownership: ctrl.formValues.Ownership,
AuthorizedUsers: ctrl.formValues.Ownership_Users,
AuthorizedTeams: ctrl.formValues.Ownership_Teams
};
var isAdmin = ctrl.isAdmin;
error = FormValidator.validateAccessControl(accessControlData, isAdmin);
if (error) {
ctrl.state.formValidationError = error;
return false;
}
return true;
}
function processOwnershipFormValues() {
var userIds = [];
angular.forEach(ctrl.formValues.Ownership_Users, function(user) {
userIds.push(user.Id);
});
var teamIds = [];
angular.forEach(ctrl.formValues.Ownership_Teams, function(team) {
teamIds.push(team.Id);
});
var administratorsOnly = ctrl.formValues.Ownership === 'administrators' ? true : false;
return {
ownership: ctrl.formValues.Ownership,
authorizedUserIds: administratorsOnly ? [] : userIds,
authorizedTeamIds: administratorsOnly ? [] : teamIds,
administratorsOnly: administratorsOnly
};
}
function updateOwnership() {
$('#loadingViewSpinner').show();
var resourceId = ctrl.resourceControl.ResourceId;
var ownershipParameters = processOwnershipFormValues();
ResourceControlService.applyResourceControlChange(ctrl.resourceType, resourceId,
ctrl.resourceControl, ownershipParameters)
.then(function success(data) {
Notifications.success('Access control successfully updated');
$state.reload();
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to update access control');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
function initComponent() {
$('#loadingViewSpinner').show();
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1 ? true: false;
var userId = userDetails.ID;
ctrl.isAdmin = isAdmin;
var resourceControl = ctrl.resourceControl;
if (isAdmin) {
if (resourceControl) {
ctrl.formValues.Ownership = resourceControl.Ownership === 'private' ? 'restricted' : resourceControl.Ownership;
} else {
ctrl.formValues.Ownership = 'public';
}
} else {
ctrl.formValues.Ownership = 'public';
}
ResourceControlService.retrieveOwnershipDetails(resourceControl)
.then(function success(data) {
ctrl.authorizedUsers = data.authorizedUsers;
ctrl.authorizedTeams = data.authorizedTeams;
return ResourceControlService.retrieveUserPermissionsOnResource(userId, isAdmin, resourceControl);
})
.then(function success(data) {
ctrl.state.canEditOwnership = data.isPartOfRestrictedUsers || data.isLeaderOfAnyRestrictedTeams;
ctrl.state.canChangeOwnershipToTeam = data.isPartOfRestrictedUsers;
return $q.all({
availableUsers: isAdmin ? UserService.users(false) : [],
availableTeams: isAdmin || data.isPartOfRestrictedUsers ? UserService.userTeams(userId) : []
});
})
.then(function success(data) {
ctrl.availableUsers = data.availableUsers;
angular.forEach(ctrl.availableUsers, function(user) {
var found = _.find(ctrl.authorizedUsers, { Id: user.Id });
if (found) {
user.selected = true;
}
});
ctrl.availableTeams = data.availableTeams;
angular.forEach(data.availableTeams, function(team) {
var found = _.find(ctrl.authorizedTeams, { Id: team.Id });
if (found) {
team.selected = true;
}
});
if (data.availableTeams.length === 1) {
ctrl.formValues.Ownership_Teams.push(data.availableTeams[0]);
}
ctrl.state.displayAccessControlPanel = true;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve access control information');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
initComponent();
}]);

View File

@ -4,25 +4,25 @@ angular.module('portainer.helpers')
var helper = {};
helper.retrieveAuthorizedUsers = function(resourceControl, users) {
var authorizedUserNames = [];
var authorizedUsers = [];
angular.forEach(resourceControl.UserAccesses, function(access) {
var user = _.find(users, { Id: access.UserId });
if (user) {
authorizedUserNames.push(user);
authorizedUsers.push(user);
}
});
return authorizedUserNames;
return authorizedUsers;
};
helper.retrieveAuthorizedTeams = function(resourceControl, teams) {
var authorizedTeamNames = [];
var authorizedTeams = [];
angular.forEach(resourceControl.TeamAccesses, function(access) {
var team = _.find(teams, { Id: access.TeamId });
if (team) {
authorizedTeamNames.push(team);
authorizedTeams.push(team);
}
});
return authorizedTeamNames;
return authorizedTeams;
};
helper.isLeaderOfAnyRestrictedTeams = function(userMemberships, resourceControl) {

View File

@ -29,14 +29,14 @@ angular.module('portainer.services')
};
service.applyResourceControl = function(resourceControlType, resourceIdentifier, userId, accessControlData, subResources) {
if (!accessControlData.accessControlEnabled) {
if (!accessControlData.AccessControlEnabled) {
return;
}
var authorizedUserIds = [];
var authorizedTeamIds = [];
var administratorsOnly = false;
switch (accessControlData.ownership) {
switch (accessControlData.Ownership) {
case 'administrators':
administratorsOnly = true;
break;
@ -44,10 +44,10 @@ angular.module('portainer.services')
authorizedUserIds.push(userId);
break;
case 'restricted':
angular.forEach(accessControlData.authorizedUsers, function(user) {
angular.forEach(accessControlData.AuthorizedUsers, function(user) {
authorizedUserIds.push(user.Id);
});
angular.forEach(accessControlData.authorizedTeams, function(team) {
angular.forEach(accessControlData.AuthorizedTeams, function(team) {
authorizedTeamIds.push(team.Id);
});
break;
@ -83,9 +83,9 @@ angular.module('portainer.services')
teams: resourceControl.TeamAccesses.length > 0 ? TeamService.teams() : []
})
.then(function success(data) {
var authorizedUserNames = ResourceControlHelper.retrieveAuthorizedUsers(resourceControl, data.users);
var authorizedTeamNames = ResourceControlHelper.retrieveAuthorizedTeams(resourceControl, data.teams);
deferred.resolve({ authorizedUsers: authorizedUserNames, authorizedTeams: authorizedTeamNames });
var authorizedUsers = ResourceControlHelper.retrieveAuthorizedUsers(resourceControl, data.users);
var authorizedTeams = ResourceControlHelper.retrieveAuthorizedTeams(resourceControl, data.teams);
deferred.resolve({ authorizedUsers: authorizedUsers, authorizedTeams: authorizedTeams });
})
.catch(function error(err) {
deferred.reject({ msg: 'Unable to retrieve user and team information', err: err });

View File

@ -1,36 +0,0 @@
// ControllerDataPipeline is used to transfer data between multiple controllers.
angular.module('portainer.services')
.factory('ControllerDataPipeline', [function ControllerDataPipelineFactory() {
'use strict';
var pipeline = {};
// accessControlData is used to manage the data required by the accessControlPanelController.
var accessControlData = {};
pipeline.setAccessControlData = function (type, resourceId, resourceControl) {
accessControlData.resourceType = type;
accessControlData.resourceId = resourceId;
accessControlData.resourceControl = resourceControl;
};
pipeline.getAccessControlData = function() {
return accessControlData;
};
// accessControlFormData is used to manage the data available in the scope of the accessControlFormController.
var accessControlFormData = {};
pipeline.setAccessControlFormData = function(accessControlEnabled, ownership, authorizedUsers, authorizedTeams) {
accessControlFormData.accessControlEnabled = accessControlEnabled;
accessControlFormData.ownership = ownership;
accessControlFormData.authorizedUsers = authorizedUsers;
accessControlFormData.authorizedTeams = authorizedTeams;
};
pipeline.getAccessControlFormData = function() {
return accessControlFormData;
};
return pipeline;
}]);

View File

@ -5,16 +5,16 @@ angular.module('portainer.services')
var validator = {};
validator.validateAccessControl = function(accessControlData, isAdmin) {
if (!accessControlData.accessControlEnabled) {
if (!accessControlData.AccessControlEnabled) {
return '';
}
if (isAdmin && accessControlData.ownership === 'restricted' &&
accessControlData.authorizedUsers.length === 0 &&
accessControlData.authorizedTeams.length === 0) {
if (isAdmin && accessControlData.Ownership === 'restricted' &&
accessControlData.AuthorizedUsers.length === 0 &&
accessControlData.AuthorizedTeams.length === 0) {
return 'You must specify at least one team or user.';
} else if (!isAdmin && accessControlData.ownership === 'restricted' &&
accessControlData.authorizedTeams.length === 0) {
} else if (!isAdmin && accessControlData.Ownership === 'restricted' &&
accessControlData.AuthorizedTeams.length === 0) {
return 'You must specify at least a team.';
}
return '';

View File

@ -29,7 +29,7 @@ module.exports = function (grunt) {
grunt.registerTask('after-copy', [
'filerev',
'usemin',
'clean:tmp'
'clean:tmp'
]);
grunt.registerTask('build-webapp', [
'config:prod',
@ -37,7 +37,7 @@ module.exports = function (grunt) {
'before-copy',
'copy:assets',
'after-copy'
]);
]);
grunt.registerTask('build', [
'config:dev',
'clean:app',
@ -52,7 +52,7 @@ module.exports = function (grunt) {
]);
grunt.task.registerTask('release', 'release:<platform>:<arch>', function(p, a) {
grunt.task.run(['config:prod', 'clean:all', 'shell:buildBinary:'+p+':'+a, 'before-copy', 'copy:assets', 'after-copy' ]);
});
});
grunt.registerTask('lint', ['eslint']);
grunt.registerTask('run-dev', ['build', 'shell:run', 'watch:build']);
grunt.registerTask('clear', ['clean:app']);
@ -69,10 +69,10 @@ module.exports = function (grunt) {
js: ['app/**/*.js', '!app/**/*.spec.js'],
jsTpl: ['<%= distdir %>/templates/**/*.js'],
jsVendor: [
'bower_components/angular-multi-select/isteven-multi-select.js',
'bower_components/bootbox.js/bootbox.js',
'bower_components/jquery/dist/jquery.min.js',
'bower_components/bootstrap/dist/js/bootstrap.min.js',
'bower_components/angular-multi-select/isteven-multi-select.js',
'bower_components/bootbox.js/bootbox.js',
'bower_components/Chart.js/Chart.min.js',
'bower_components/filesize/lib/filesize.min.js',
'bower_components/lodash/dist/lodash.min.js',
@ -86,9 +86,9 @@ module.exports = function (grunt) {
tpl: ['app/components/**/*.html', 'app/directives/**/*.html'],
css: ['assets/css/app.css'],
cssVendor: [
'bower_components/bootstrap/dist/css/bootstrap.css',
'bower_components/angular-multi-select/isteven-multi-select.css',
'bower_components/angular-ui-select/dist/select.min.css',
'bower_components/bootstrap/dist/css/bootstrap.css',
'bower_components/font-awesome/css/font-awesome.min.css',
'bower_components/rdash-ui/dist/css/rdash.min.css',
'bower_components/toastr/toastr.min.css',
@ -248,7 +248,7 @@ module.exports = function (grunt) {
return 'build/build_in_container.sh ' + p + ' ' + a;
}
}
},
},
run: {
command: [
'docker rm -f portainer',