mirror of
https://github.com/portainer/portainer.git
synced 2025-08-09 07:45:22 +02:00
feat(api): rewrite access control management in Docker (#3337)
* feat(api): decorate Docker resource creation response with resource control * fix(api): fix a potential resource control conflict between stacks/volumes * feat(api): generate a default private resource control instead of admin only * fix(api): fix default RC value * fix(api): update RC authorizations check to support admin only flag * refactor(api): relocate access control related methods * fix(api): fix a potential conflict when fetching RC from database * refactor(api): refactor access control logic * refactor(api): remove the concept of DecoratedStack * feat(api): automatically remove RC when removing a Docker resource * refactor(api): update filter resource methods documentation * refactor(api): update proxy package structure * refactor(api): renamed proxy/misc package * feat(api): re-introduce ResourceControlDelete operation as admin restricted * refactor(api): relocate default endpoint authorizations * feat(api): migrate RBAC data * feat(app): ResourceControl management refactor * fix(api): fix access control issue on stack deletion and automatically delete RC * fix(api): fix stack filtering * fix(api): fix UpdateResourceControl operation checks * refactor(api): introduce a NewTransport builder method * refactor(api): inject endpoint in Docker transport * refactor(api): introduce Docker client into Docker transport * refactor(api): refactor http/proxy package * feat(api): inspect a Docker resource labels during access control validation * fix(api): only apply automatic resource control creation on success response * fix(api): fix stack access control check * fix(api): use StatusCreated instead of StatusOK for automatic resource control creation * fix(app): resource control fixes * fix(api): fix an issue preventing administrator to inspect a resource with a RC * refactor(api): remove useless error return * refactor(api): document DecorateStacks function * fix(api): fix invalid resource control type for container deletion * feat(api): support Docker system networks * feat(api): update Swagger docs * refactor(api): rename transport variable * refactor(api): rename transport variable * feat(networks): add system tag for system networks * feat(api): add support for resource control labels * feat(api): upgrade to DBVersion 22 * refactor(api): refactor access control management in Docker proxy * refactor(api): re-implement docker proxy taskListOperation * refactor(api): review parameters declaration * refactor(api): remove extra blank line * refactor(api): review method comments * fix(api): fix invalid ServerAddress property and review method visibility * feat(api): update error message * feat(api): update restrictedVolumeBrowserOperation method * refactor(api): refactor method parameters * refactor(api): minor refactor * refactor(api): change Azure transport visibility * refactor(api): update struct documentation * refactor(api): update struct documentation * feat(api): review restrictedResourceOperation method * refactor(api): remove unused authorization methods * feat(api): apply RBAC when enabled on stack operations * fix(api): fix invalid data migration procedure for DBVersion = 22 * fix(app): RC duplicate on private resource * feat(api): change Docker API version logic for libcompose/client factory * fix(api): update access denied error message to be Docker API compliant * fix(api): update volume browsing authorizations data migration * fix(api): fix an issue with access control in multi-node agent Swarm cluster
This commit is contained in:
parent
198e92c734
commit
19d4db13be
118 changed files with 3600 additions and 3020 deletions
|
@ -101,7 +101,7 @@
|
|||
<td ng-if="$ctrl.showOwnershipColumn">
|
||||
<span>
|
||||
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
|
||||
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = 'administrators' }}
|
||||
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -272,7 +272,7 @@
|
|||
<td ng-if="$ctrl.showOwnershipColumn" ng-show="$ctrl.columnVisibility.columns.ownership.display">
|
||||
<span>
|
||||
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
|
||||
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = 'administrators' }}
|
||||
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<td>
|
||||
<a ng-if="!parentCtrl.offlineMode" ui-sref="docker.networks.network({ id: item.Id, nodeName: item.NodeName })" title="{{ item.Name }}">{{ item.Name | truncate:40 }}</a>
|
||||
<span ng-if="parentCtrl.offlineMode">{{ item.Name | truncate:40 }}</span>
|
||||
<span style="margin-left: 10px;" class="label label-info image-tag space-left" ng-if="item.ResourceControl.System">System</span>
|
||||
</td>
|
||||
<td>{{ item.StackName ? item.StackName : '-' }}</td>
|
||||
<td>{{ item.Scope }}</td>
|
||||
|
@ -22,6 +23,6 @@
|
|||
<td ng-if="parentCtrl.showOwnershipColumn">
|
||||
<span>
|
||||
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
|
||||
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = 'administrators' }}
|
||||
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = RCO.ADMINISTRATORS }}
|
||||
</span>
|
||||
</td>
|
|
@ -1,3 +1,5 @@
|
|||
import { ResourceControlOwnership as RCO } from 'Portainer/models/resourceControl/resourceControlOwnership';
|
||||
|
||||
angular.module('portainer.docker')
|
||||
.directive('networkRowContent', [function networkRowContent() {
|
||||
var directive = {
|
||||
|
@ -9,6 +11,9 @@ angular.module('portainer.docker')
|
|||
parentCtrl: '<',
|
||||
allowCheckbox: '<',
|
||||
allowExpand: '<'
|
||||
},
|
||||
controller: ($scope) => {
|
||||
$scope.RCO = RCO;
|
||||
}
|
||||
};
|
||||
return directive;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import _ from 'lodash-es';
|
||||
|
||||
angular.module('portainer.docker')
|
||||
.controller('NetworksDatatableController', ['$scope', '$controller', 'PREDEFINED_NETWORKS', 'DatatableService',
|
||||
function ($scope, $controller, PREDEFINED_NETWORKS, DatatableService) {
|
||||
.controller('NetworksDatatableController', ['$scope', '$controller', 'NetworkHelper', 'DatatableService',
|
||||
function ($scope, $controller, NetworkHelper, DatatableService) {
|
||||
|
||||
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
|
||||
|
||||
this.disableRemove = function(item) {
|
||||
return PREDEFINED_NETWORKS.includes(item.Name);
|
||||
return NetworkHelper.isSystemNetwork(item);
|
||||
};
|
||||
|
||||
this.state = Object.assign(this.state, {
|
||||
|
@ -15,7 +15,7 @@ angular.module('portainer.docker')
|
|||
})
|
||||
|
||||
/**
|
||||
* Do not allow PREDEFINED_NETWORKS to be selected
|
||||
* Do not allow system networks to be selected
|
||||
*/
|
||||
this.allowSelection = function(item) {
|
||||
return !this.disableRemove(item);
|
||||
|
|
|
@ -101,7 +101,7 @@
|
|||
<td ng-if="$ctrl.showOwnershipColumn">
|
||||
<span>
|
||||
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
|
||||
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = 'administrators' }}
|
||||
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -156,7 +156,7 @@
|
|||
<td ng-if="$ctrl.showOwnershipColumn">
|
||||
<span>
|
||||
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
|
||||
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = 'administrators' }}
|
||||
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -164,7 +164,7 @@
|
|||
<td ng-if="$ctrl.showOwnershipColumn">
|
||||
<span>
|
||||
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
|
||||
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = 'administrators' }}
|
||||
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ResourceControlViewModel } from '../../portainer/models/resourceControl';
|
||||
import { ResourceControlViewModel } from 'Portainer/models/resourceControl/resourceControl';
|
||||
|
||||
export function ConfigViewModel(data) {
|
||||
this.Id = data.ID;
|
||||
|
@ -9,9 +9,7 @@ export function ConfigViewModel(data) {
|
|||
this.Labels = data.Spec.Labels;
|
||||
this.Data = atob(data.Spec.Data);
|
||||
|
||||
if (data.Portainer) {
|
||||
if (data.Portainer.ResourceControl) {
|
||||
this.ResourceControl = new ResourceControlViewModel(data.Portainer.ResourceControl);
|
||||
}
|
||||
if (data.Portainer && data.Portainer.ResourceControl) {
|
||||
this.ResourceControl = new ResourceControlViewModel(data.Portainer.ResourceControl);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import _ from 'lodash-es';
|
||||
import { ResourceControlViewModel } from '../../portainer/models/resourceControl';
|
||||
import { ResourceControlViewModel } from 'Portainer/models/resourceControl/resourceControl';
|
||||
|
||||
export function createStatus(statusText) {
|
||||
var status = _.toLower(statusText);
|
||||
|
@ -103,9 +103,7 @@ export function ContainerDetailsViewModel(data) {
|
|||
this.Config = data.Config;
|
||||
this.HostConfig = data.HostConfig;
|
||||
this.Mounts = data.Mounts;
|
||||
if (data.Portainer) {
|
||||
if (data.Portainer.ResourceControl) {
|
||||
this.ResourceControl = new ResourceControlViewModel(data.Portainer.ResourceControl);
|
||||
}
|
||||
if (data.Portainer && data.Portainer.ResourceControl) {
|
||||
this.ResourceControl = new ResourceControlViewModel(data.Portainer.ResourceControl);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ResourceControlViewModel } from "../../portainer/models/resourceControl";
|
||||
import { ResourceControlViewModel } from 'Portainer/models/resourceControl/resourceControl';
|
||||
|
||||
export function NetworkViewModel(data) {
|
||||
this.Id = data.Id;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ResourceControlViewModel } from '../../portainer/models/resourceControl'
|
||||
import { ResourceControlViewModel } from 'Portainer/models/resourceControl/resourceControl';
|
||||
|
||||
export function SecretViewModel(data) {
|
||||
this.Id = data.ID;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ResourceControlViewModel } from '../../portainer/models/resourceControl';
|
||||
import { ResourceControlViewModel } from 'Portainer/models/resourceControl/resourceControl';
|
||||
|
||||
export function ServiceViewModel(data, runningTasks, allTasks) {
|
||||
this.Model = data;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ResourceControlViewModel } from "../../portainer/models/resourceControl";
|
||||
import { ResourceControlViewModel } from 'Portainer/models/resourceControl/resourceControl';
|
||||
|
||||
export function VolumeViewModel(data) {
|
||||
this.Id = data.Name;
|
||||
|
|
|
@ -107,14 +107,14 @@ function ContainerServiceFactory($q, Container, ResourceControlService, LogHelpe
|
|||
|
||||
service.createAndStartContainer = function(configuration) {
|
||||
var deferred = $q.defer();
|
||||
var containerID;
|
||||
var container;
|
||||
service.createContainer(configuration)
|
||||
.then(function success(data) {
|
||||
containerID = data.Id;
|
||||
return service.startContainer(containerID);
|
||||
container = data;
|
||||
return service.startContainer(container.Id);
|
||||
})
|
||||
.then(function success() {
|
||||
deferred.resolve({ Id: containerID });
|
||||
deferred.resolve(container);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject(err);
|
||||
|
@ -129,13 +129,9 @@ function ContainerServiceFactory($q, Container, ResourceControlService, LogHelpe
|
|||
.then(function success(data) {
|
||||
if (data.message) {
|
||||
deferred.reject({ msg: data.message, err: data.message });
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
if (container.ResourceControl && container.ResourceControl.Type === 1) {
|
||||
return ResourceControlService.deleteResourceControl(container.ResourceControl.Id);
|
||||
}
|
||||
})
|
||||
.then(function success() {
|
||||
deferred.resolve();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to remove container', err: err });
|
||||
|
|
|
@ -43,14 +43,13 @@ function ServiceServiceFactory($q, Service, ServiceHelper, TaskService, Resource
|
|||
var deferred = $q.defer();
|
||||
|
||||
Service.remove({id: service.Id}).$promise
|
||||
.then(function success() {
|
||||
if (service.ResourceControl && service.ResourceControl.Type === 2) {
|
||||
return ResourceControlService.deleteResourceControl(service.ResourceControl.Id);
|
||||
.then(function success(data) {
|
||||
if (data.message) {
|
||||
deferred.reject({ msg: data.message, err: data.message });
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
})
|
||||
.then(function success() {
|
||||
deferred.resolve();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to remove service', err: err });
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { VolumeViewModel } from '../models/volume';
|
||||
|
||||
angular.module('portainer.docker')
|
||||
.factory('VolumeService', ['$q', 'Volume', 'VolumeHelper', 'ResourceControlService', function VolumeServiceFactory($q, Volume, VolumeHelper, ResourceControlService) {
|
||||
.factory('VolumeService', ['$q', 'Volume', 'VolumeHelper', function VolumeServiceFactory($q, Volume, VolumeHelper) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
|
||||
|
@ -45,13 +45,9 @@ angular.module('portainer.docker')
|
|||
.then(function success(data) {
|
||||
if (data.message) {
|
||||
deferred.reject({ msg: data.message, err: data.message });
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
if (volume.ResourceControl && volume.ResourceControl.Type === 3) {
|
||||
return ResourceControlService.deleteResourceControl(volume.ResourceControl.Id);
|
||||
}
|
||||
})
|
||||
.then(function success() {
|
||||
deferred.resolve();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to remove volume', err: err });
|
||||
|
|
|
@ -104,9 +104,9 @@ class CreateConfigController {
|
|||
}
|
||||
|
||||
async createAsync() {
|
||||
let accessControlData = this.formValues.AccessControlData;
|
||||
let userDetails = this.Authentication.getUserDetails();
|
||||
let isAdmin = this.Authentication.isAdmin();
|
||||
const accessControlData = this.formValues.AccessControlData;
|
||||
const userDetails = this.Authentication.getUserDetails();
|
||||
const isAdmin = this.Authentication.isAdmin();
|
||||
|
||||
if (this.formValues.ConfigContent === "") {
|
||||
this.state.formValidationError = "Config content must not be empty";
|
||||
|
@ -117,19 +117,13 @@ class CreateConfigController {
|
|||
return;
|
||||
}
|
||||
|
||||
let config = this.prepareConfiguration();
|
||||
const config = this.prepareConfiguration();
|
||||
|
||||
try {
|
||||
let data = await this.ConfigService.create(config);
|
||||
let configIdentifier = data.ID;
|
||||
let userId = userDetails.ID;
|
||||
await this.ResourceControlService.applyResourceControl(
|
||||
"config",
|
||||
configIdentifier,
|
||||
userId,
|
||||
accessControlData,
|
||||
[]
|
||||
);
|
||||
const data = await this.ConfigService.create(config);
|
||||
const resourceControl = data.Portainer.ResourceControl;
|
||||
const userId = userDetails.ID;
|
||||
await this.ResourceControlService.applyResourceControl(userId, accessControlData, resourceControl);
|
||||
this.Notifications.success("Config successfully created");
|
||||
this.$state.go("docker.configs", {}, { reload: true });
|
||||
} catch (err) {
|
||||
|
|
|
@ -759,16 +759,14 @@ function ($q, $scope, $async, $state, $timeout, $transition$, $filter, Container
|
|||
}
|
||||
|
||||
function applyResourceControl(newContainer) {
|
||||
var containerIdentifier = newContainer.Id;
|
||||
var userId = Authentication.getUserDetails().ID;
|
||||
const userId = Authentication.getUserDetails().ID;
|
||||
const resourceControl = newContainer.Portainer.ResourceControl;
|
||||
const containerId = newContainer.Id;
|
||||
const accessControlData = $scope.formValues.AccessControlData;
|
||||
|
||||
return $q.when(ResourceControlService.applyResourceControl(
|
||||
'container',
|
||||
containerIdentifier,
|
||||
userId,
|
||||
$scope.formValues.AccessControlData, []
|
||||
)).then(function onApplyResourceControlSuccess() {
|
||||
return containerIdentifier;
|
||||
return ResourceControlService.applyResourceControl(userId, accessControlData, resourceControl)
|
||||
.then(function onApplyResourceControlSuccess() {
|
||||
return containerId;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<div class="btn-group" role="group" aria-label="...">
|
||||
<button authorization="DockerContainerStart" class="btn btn-success btn-sm" ng-click="start()" ng-disabled="container.State.Running"><i class="fa fa-play space-right" aria-hidden="true"></i>Start</button>
|
||||
<button authorization="DockerContainerStop" class="btn btn-danger btn-sm" ng-click="stop()" ng-disabled="!container.State.Running"><i class="fa fa-stop space-right" aria-hidden="true"></i>Stop</button>
|
||||
<button authorization=DockerContainerKill" class="btn btn-danger btn-sm" ng-click="kill()" ng-disabled="!container.State.Running"><i class="fa fa-bomb space-right" aria-hidden="true"></i>Kill</button>
|
||||
<button authorization="DockerContainerKill" class="btn btn-danger btn-sm" ng-click="kill()" ng-disabled="!container.State.Running"><i class="fa fa-bomb space-right" aria-hidden="true"></i>Kill</button>
|
||||
<button authorization="DockerContainerRestart" class="btn btn-primary btn-sm" ng-click="restart()" ng-disabled="!container.State.Running"><i class="fa fa-sync space-right" aria-hidden="true"></i>Restart</button>
|
||||
<button authorization="DockerContainerPause" class="btn btn-primary btn-sm" ng-click="pause()" ng-disabled="!container.State.Running || container.State.Paused"><i class="fa fa-pause space-right" aria-hidden="true"></i>Pause</button>
|
||||
<button authorization="DockerContainerUnpause" class="btn btn-primary btn-sm" ng-click="unpause()" ng-disabled="!container.State.Paused"><i class="fa fa-play space-right" aria-hidden="true"></i>Resume</button>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import moment from 'moment';
|
||||
|
||||
angular.module('portainer.docker')
|
||||
.controller('ContainerController', ['$q', '$scope', '$state','$transition$', '$filter', 'Commit', 'ContainerHelper', 'ContainerService', 'ImageHelper', 'NetworkService', 'Notifications', 'ModalService', 'ResourceControlService', 'RegistryService', 'ImageService', 'HttpRequestHelper',
|
||||
function ($q, $scope, $state, $transition$, $filter, Commit, ContainerHelper, ContainerService, ImageHelper, NetworkService, Notifications, ModalService, ResourceControlService, RegistryService, ImageService, HttpRequestHelper) {
|
||||
.controller('ContainerController', ['$q', '$scope', '$state','$transition$', '$filter', 'Commit', 'ContainerHelper', 'ContainerService', 'ImageHelper', 'NetworkService', 'Notifications', 'ModalService', 'ResourceControlService', 'RegistryService', 'ImageService', 'HttpRequestHelper', 'Authentication',
|
||||
function ($q, $scope, $state, $transition$, $filter, Commit, ContainerHelper, ContainerService, ImageHelper, NetworkService, Notifications, ModalService, ResourceControlService, RegistryService, ImageService, HttpRequestHelper, Authentication) {
|
||||
$scope.activityTime = 0;
|
||||
$scope.portBindings = [];
|
||||
|
||||
|
@ -205,7 +205,7 @@ function ($q, $scope, $state, $transition$, $filter, Commit, ContainerHelper, Co
|
|||
.then(setMainNetworkAndCreateContainer)
|
||||
.then(connectContainerToOtherNetworks)
|
||||
.then(startContainerIfNeeded)
|
||||
.then(createResourceControlIfNeeded)
|
||||
.then(createResourceControl)
|
||||
.then(deleteOldContainer)
|
||||
.then(notifyAndChangeView)
|
||||
.catch(notifyOnError);
|
||||
|
@ -276,19 +276,11 @@ function ($q, $scope, $state, $transition$, $filter, Commit, ContainerHelper, Co
|
|||
);
|
||||
}
|
||||
|
||||
function createResourceControlIfNeeded(newContainer) {
|
||||
if (!container.ResourceControl) {
|
||||
return $q.when();
|
||||
}
|
||||
var containerIdentifier = newContainer.Id;
|
||||
var resourceControl = container.ResourceControl;
|
||||
var users = resourceControl.UserAccesses.map(function(u) {
|
||||
return u.UserId;
|
||||
});
|
||||
var teams = resourceControl.TeamAccesses.map(function(t) {
|
||||
return t.TeamId;
|
||||
});
|
||||
return ResourceControlService.createResourceControl(resourceControl.Public, users, teams, containerIdentifier, 'container', []);
|
||||
function createResourceControl(newContainer) {
|
||||
const userId = Authentication.getUserDetails().ID;
|
||||
const oldResourceControl = container.ResourceControl;
|
||||
const newResourceControl = newContainer.Portainer.ResourceControl;
|
||||
return ResourceControlService.duplicateResourceControl(userId, oldResourceControl, newResourceControl);
|
||||
}
|
||||
|
||||
function notifyAndChangeView() {
|
||||
|
|
|
@ -134,9 +134,10 @@ angular.module('portainer.docker')
|
|||
$scope.state.actionInProgress = true;
|
||||
NetworkService.create(context.networkConfiguration)
|
||||
.then(function success(data) {
|
||||
var networkIdentifier = data.Id;
|
||||
var userId = context.userDetails.ID;
|
||||
return ResourceControlService.applyResourceControl('network', networkIdentifier, userId, context.accessControlData, []);
|
||||
const userId = context.userDetails.ID;
|
||||
const accessControlData = context.accessControlData;
|
||||
const resourceControl = data.Portainer.ResourceControl;
|
||||
return ResourceControlService.applyResourceControl(userId, accessControlData, resourceControl);
|
||||
})
|
||||
.then(function success() {
|
||||
Notifications.success('Network successfully created');
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<td>ID</td>
|
||||
<td>
|
||||
{{ network.Id }}
|
||||
<button authorization="DockerNetworkDelete" ng-if="allowRemove(network)" class="btn btn-xs btn-danger" ng-click="removeNetwork(network.Id)"><i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Delete this network</button>
|
||||
<button authorization="DockerNetworkDelete" ng-if="allowRemove()" class="btn btn-xs btn-danger" ng-click="removeNetwork(network.Id)"><i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Delete this network</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -55,7 +55,8 @@
|
|||
ng-if="network && applicationState.application.authentication"
|
||||
resource-id="network.Id"
|
||||
resource-control="network.ResourceControl"
|
||||
resource-type="'network'">
|
||||
resource-type="'network'"
|
||||
disable-ownership-change="isSystemNetwork()">
|
||||
</por-access-control-panel>
|
||||
<!-- !access-control-panel -->
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
angular.module('portainer.docker')
|
||||
.controller('NetworkController', ['$scope', '$state', '$transition$', '$filter', 'NetworkService', 'Container', 'Notifications', 'HttpRequestHelper', 'PREDEFINED_NETWORKS',
|
||||
function ($scope, $state, $transition$, $filter, NetworkService, Container, Notifications, HttpRequestHelper, PREDEFINED_NETWORKS) {
|
||||
|
||||
$scope.network = {};
|
||||
.controller('NetworkController', ['$scope', '$state', '$transition$', '$filter', 'NetworkService', 'Container', 'Notifications', 'HttpRequestHelper', 'NetworkHelper',
|
||||
function ($scope, $state, $transition$, $filter, NetworkService, Container, Notifications, HttpRequestHelper, NetworkHelper) {
|
||||
|
||||
$scope.removeNetwork = function removeNetwork() {
|
||||
NetworkService.remove($transition$.params().id, $transition$.params().id)
|
||||
|
@ -27,8 +25,12 @@ function ($scope, $state, $transition$, $filter, NetworkService, Container, Noti
|
|||
});
|
||||
};
|
||||
|
||||
$scope.allowRemove = function allowRemove(item) {
|
||||
return !PREDEFINED_NETWORKS.includes(item.Name);
|
||||
$scope.isSystemNetwork = function() {
|
||||
return $scope.network && NetworkHelper.isSystemNetwork($scope.network);
|
||||
}
|
||||
|
||||
$scope.allowRemove = function() {
|
||||
return !$scope.isSystemNetwork();
|
||||
};
|
||||
|
||||
function filterContainersInNetwork(network, containers) {
|
||||
|
|
|
@ -59,9 +59,9 @@ function ($scope, $state, Notifications, SecretService, LabelHelper, Authenticat
|
|||
|
||||
$scope.create = function () {
|
||||
|
||||
var accessControlData = $scope.formValues.AccessControlData;
|
||||
var userDetails = Authentication.getUserDetails();
|
||||
var isAdmin = Authentication.isAdmin();
|
||||
const accessControlData = $scope.formValues.AccessControlData;
|
||||
const userDetails = Authentication.getUserDetails();
|
||||
const isAdmin = Authentication.isAdmin();
|
||||
|
||||
if (!validateForm(accessControlData, isAdmin)) {
|
||||
return;
|
||||
|
@ -71,9 +71,9 @@ function ($scope, $state, Notifications, SecretService, LabelHelper, Authenticat
|
|||
var secretConfiguration = prepareConfiguration();
|
||||
SecretService.create(secretConfiguration)
|
||||
.then(function success(data) {
|
||||
var secretIdentifier = data.ID;
|
||||
var userId = userDetails.ID;
|
||||
return ResourceControlService.applyResourceControl('secret', secretIdentifier, userId, accessControlData, []);
|
||||
const userId = userDetails.ID;
|
||||
const resourceControl = data.Portainer.ResourceControl;
|
||||
return ResourceControlService.applyResourceControl(userId, accessControlData, resourceControl);
|
||||
})
|
||||
.then(function success() {
|
||||
Notifications.success('Secret successfully created');
|
||||
|
|
|
@ -432,15 +432,14 @@ function ($q, $scope, $state, $timeout, Service, ServiceHelper, ConfigService, C
|
|||
var authenticationDetails = registry.Authentication ? RegistryService.encodedCredentials(registry) : '';
|
||||
HttpRequestHelper.setRegistryAuthenticationHeader(authenticationDetails);
|
||||
|
||||
var serviceIdentifier;
|
||||
Service.create(config).$promise
|
||||
.then(function success(data) {
|
||||
serviceIdentifier = data.ID;
|
||||
return $q.when($scope.formValues.Webhook && WebhookService.createServiceWebhook(serviceIdentifier, EndpointProvider.endpointID()));
|
||||
})
|
||||
.then(function success() {
|
||||
var userId = Authentication.getUserDetails().ID;
|
||||
return ResourceControlService.applyResourceControl('service', serviceIdentifier, userId, accessControlData, []);
|
||||
const serviceId = data.ID;
|
||||
const resourceControl = data.Portainer.ResourceControl;
|
||||
const userId = Authentication.getUserDetails().ID;
|
||||
const rcPromise = ResourceControlService.applyResourceControl(userId, accessControlData, resourceControl);
|
||||
const webhookPromise = $q.when($scope.formValues.Webhook && WebhookService.createServiceWebhook(serviceId, EndpointProvider.endpointID()));
|
||||
return $q.all([rcPromise, webhookPromise]);
|
||||
})
|
||||
.then(function success() {
|
||||
Notifications.success('Service successfully created');
|
||||
|
|
|
@ -81,9 +81,9 @@ function ($q, $scope, $state, VolumeService, PluginService, ResourceControlServi
|
|||
$scope.state.actionInProgress = true;
|
||||
VolumeService.createVolume(volumeConfiguration)
|
||||
.then(function success(data) {
|
||||
var volumeIdentifier = data.Id;
|
||||
var userId = userDetails.ID;
|
||||
return ResourceControlService.applyResourceControl('volume', volumeIdentifier, userId, accessControlData, []);
|
||||
const userId = userDetails.ID;
|
||||
const resourceControl = data.ResourceControl;
|
||||
return ResourceControlService.applyResourceControl(userId, accessControlData, resourceControl);
|
||||
})
|
||||
.then(function success() {
|
||||
Notifications.success('Volume successfully created');
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
</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="form-group" ng-if="$ctrl.formData.AccessControlEnabled && $ctrl.formData.Ownership === $ctrl.RCO.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
|
||||
|
@ -97,7 +97,7 @@
|
|||
</div>
|
||||
<!-- !authorized-teams -->
|
||||
<!-- authorized-users -->
|
||||
<div class="form-group" ng-if="$ctrl.formData.AccessControlEnabled && $ctrl.formData.Ownership === 'restricted' && $ctrl.isAdmin">
|
||||
<div class="form-group" ng-if="$ctrl.formData.AccessControlEnabled && $ctrl.formData.Ownership === $ctrl.RCO.RESTRICTED && $ctrl.isAdmin">
|
||||
<div class="col-sm-12">
|
||||
<label for="group-access" class="control-label text-left">
|
||||
Authorized users
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
import _ from 'lodash-es';
|
||||
import { ResourceControlOwnership as RCO } from 'Portainer/models/resourceControl/resourceControlOwnership';
|
||||
|
||||
angular.module('portainer.app')
|
||||
.controller('porAccessControlFormController', ['$q', 'UserService', 'TeamService', 'Notifications', 'Authentication', 'ResourceControlService',
|
||||
function ($q, UserService, TeamService, Notifications, Authentication, ResourceControlService) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.RCO = RCO;
|
||||
|
||||
ctrl.availableTeams = [];
|
||||
ctrl.availableUsers = [];
|
||||
|
||||
function setOwnership(resourceControl, isAdmin) {
|
||||
if (isAdmin && resourceControl.Ownership === 'private') {
|
||||
ctrl.formData.Ownership = 'restricted';
|
||||
if (isAdmin && resourceControl.Ownership === RCO.PRIVATE) {
|
||||
ctrl.formData.Ownership = RCO.RESTRICTED;
|
||||
} else {
|
||||
ctrl.formData.Ownership = resourceControl.Ownership;
|
||||
}
|
||||
|
@ -37,7 +40,7 @@ function ($q, UserService, TeamService, Notifications, Authentication, ResourceC
|
|||
ctrl.isAdmin = isAdmin;
|
||||
|
||||
if (isAdmin) {
|
||||
ctrl.formData.Ownership = 'administrators';
|
||||
ctrl.formData.Ownership = ctrl.RCO.ADMINISTRATORS;
|
||||
}
|
||||
|
||||
$q.all({
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { ResourceControlOwnership as RCO } from 'Portainer/models/resourceControl/resourceControlOwnership';
|
||||
|
||||
export function AccessControlFormData() {
|
||||
this.AccessControlEnabled = true;
|
||||
this.Ownership = 'private';
|
||||
this.Ownership = RCO.PRIVATE;
|
||||
this.AuthorizedUsers = [];
|
||||
this.AuthorizedTeams = [];
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ angular.module('portainer.app').component('porAccessControlPanel', {
|
|||
// 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: '<'
|
||||
resourceType: '<',
|
||||
// Allow to disable the Ownership edition based on non resource control data
|
||||
disableOwnershipChange: '<'
|
||||
}
|
||||
});
|
||||
|
|
|
@ -16,28 +16,28 @@
|
|||
</span>
|
||||
<span ng-if="$ctrl.resourceControl">
|
||||
{{ $ctrl.resourceControl.Ownership }}
|
||||
<portainer-tooltip ng-if="$ctrl.resourceControl.Ownership === 'public'" message="This resource can be managed by any user with access to this endpoint." 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>
|
||||
<portainer-tooltip ng-if="$ctrl.resourceControl.Ownership === $ctrl.RCO.PUBLIC" message="This resource can be managed by any user with access to this endpoint." position="bottom" style="margin-left: -3px;"></portainer-tooltip>
|
||||
<portainer-tooltip ng-if="$ctrl.resourceControl.Ownership === $ctrl.RCO.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 === $ctrl.RCO.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="$ctrl.resourceControl.Type === 2 && $ctrl.resourceType === 'container'">
|
||||
<tr ng-if="$ctrl.resourceControl.Type === $ctrl.RCTI.SERVICE && $ctrl.resourceType === $ctrl.RCTS.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="docker.services.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="$ctrl.resourceControl.Type === 1 && $ctrl.resourceType === 'volume'">
|
||||
<tr ng-if="$ctrl.resourceControl.Type === $ctrl.RCTI.CONTAINER && $ctrl.resourceType === $ctrl.RCTS.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="docker.containers.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>
|
||||
<tr ng-if="$ctrl.resourceControl.Type === 6 && $ctrl.resourceType !== 'stack'">
|
||||
<tr ng-if="$ctrl.resourceControl.Type === $ctrl.RCTI.STACK && $ctrl.resourceType !== $ctrl.RCTS.STACK">
|
||||
<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 stack: {{ $ctrl.resourceControl.ResourceId }}
|
||||
|
@ -61,11 +61,7 @@
|
|||
</tr>
|
||||
<!-- !authorized-teams -->
|
||||
<!-- edit-ownership -->
|
||||
<tr ng-if="!($ctrl.resourceControl.Type === 1 && $ctrl.resourceType === 'volume')
|
||||
&& !($ctrl.resourceControl.Type === 2 && $ctrl.resourceType === 'container')
|
||||
&& !($ctrl.resourceControl.Type === 6 && $ctrl.resourceType !== 'stack')
|
||||
&& !$ctrl.state.editOwnership
|
||||
&& ($ctrl.isAdmin || $ctrl.state.canEditOwnership)">
|
||||
<tr ng-if="$ctrl.canEditOwnership();">
|
||||
<td colspan="2">
|
||||
<a ng-click="$ctrl.state.editOwnership = true"><i class="fa fa-edit space-right" aria-hidden="true"></i>Change ownership</a>
|
||||
</td>
|
||||
|
@ -76,7 +72,7 @@
|
|||
<td colspan="2" style="white-space: inherit;">
|
||||
<div class="boxselector_wrapper">
|
||||
<div ng-if="$ctrl.isAdmin">
|
||||
<input type="radio" id="access_administrators" ng-model="$ctrl.formValues.Ownership" value="administrators">
|
||||
<input type="radio" id="access_administrators" ng-model="$ctrl.formValues.Ownership" ng-value="$ctrl.RCO.ADMINISTRATORS">
|
||||
<label for="access_administrators">
|
||||
<div class="boxselector_header">
|
||||
<i ng-class="'administrators' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
|
@ -86,7 +82,7 @@
|
|||
</label>
|
||||
</div>
|
||||
<div ng-if="$ctrl.isAdmin">
|
||||
<input type="radio" id="access_restricted" ng-model="$ctrl.formValues.Ownership" value="restricted">
|
||||
<input type="radio" id="access_restricted" ng-model="$ctrl.formValues.Ownership" ng-value="$ctrl.RCO.RESTRICTED">
|
||||
<label for="access_restricted">
|
||||
<div class="boxselector_header">
|
||||
<i ng-class="'restricted' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
|
@ -98,7 +94,7 @@
|
|||
</label>
|
||||
</div>
|
||||
<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">
|
||||
<input type="radio" id="access_restricted" ng-model="$ctrl.formValues.Ownership" ng-value="$ctrl.RCO.RESTRICTED">
|
||||
<label for="access_restricted">
|
||||
<div class="boxselector_header">
|
||||
<i ng-class="'restricted' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
|
@ -113,7 +109,7 @@
|
|||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="access_public" ng-model="$ctrl.formValues.Ownership" value="public">
|
||||
<input type="radio" id="access_public" ng-model="$ctrl.formValues.Ownership" ng-value="$ctrl.RCO.PUBLIC">
|
||||
<label for="access_public">
|
||||
<div class="boxselector_header">
|
||||
<i ng-class="'public' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
|
@ -127,7 +123,7 @@
|
|||
</tr>
|
||||
<!-- edit-ownership-choices -->
|
||||
<!-- select-teams -->
|
||||
<tr ng-if="$ctrl.state.editOwnership && $ctrl.formValues.Ownership === 'restricted' && ($ctrl.isAdmin || !$ctrl.isAdmin && $ctrl.availableTeams.length > 1)">
|
||||
<tr ng-if="$ctrl.state.editOwnership && $ctrl.formValues.Ownership === $ctrl.RCO.RESTRICTED && ($ctrl.isAdmin || !$ctrl.isAdmin && $ctrl.availableTeams.length > 1)">
|
||||
<td colspan="2">
|
||||
<span>Teams</span>
|
||||
<span ng-if="$ctrl.isAdmin && $ctrl.availableTeams.length === 0" class="small text-muted" style="margin-left: 10px;">
|
||||
|
@ -149,7 +145,7 @@
|
|||
</tr>
|
||||
<!-- !select-teams -->
|
||||
<!-- select-users -->
|
||||
<tr ng-if="$ctrl.isAdmin && $ctrl.state.editOwnership && $ctrl.formValues.Ownership === 'restricted'">
|
||||
<tr ng-if="$ctrl.isAdmin && $ctrl.state.editOwnership && $ctrl.formValues.Ownership === $ctrl.RCO.RESTRICTED">
|
||||
<td colspan="2">
|
||||
<span>Users</span>
|
||||
<span ng-if="$ctrl.availableUsers.length === 0" class="small text-muted" style="margin-left: 10px;">
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
import _ from 'lodash-es';
|
||||
import { AccessControlPanelData } from './porAccessControlPanelModel';
|
||||
import { ResourceControlOwnership as RCO } from 'Portainer/models/resourceControl/resourceControlOwnership';
|
||||
import { ResourceControlTypeString as RCTS, ResourceControlTypeInt as RCTI} from 'Portainer/models/resourceControl/resourceControlTypes';
|
||||
|
||||
angular.module('portainer.app')
|
||||
.controller('porAccessControlPanelController', ['$q', '$state', 'UserService', 'TeamService', 'ResourceControlService', 'Notifications', 'Authentication', 'ModalService', 'FormValidator',
|
||||
function ($q, $state, UserService, TeamService, ResourceControlService, Notifications, Authentication, ModalService, FormValidator) {
|
||||
.controller('porAccessControlPanelController', ['$q', '$state', 'UserService', 'TeamService', 'ResourceControlHelper', 'ResourceControlService', 'Notifications', 'Authentication', 'ModalService', 'FormValidator',
|
||||
function ($q, $state, UserService, TeamService, ResourceControlHelper, ResourceControlService, Notifications, Authentication, ModalService, FormValidator) {
|
||||
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.RCO = RCO;
|
||||
ctrl.RCTS = RCTS;
|
||||
ctrl.RCTI = RCTI;
|
||||
ctrl.state = {
|
||||
displayAccessControlPanel: false,
|
||||
canEditOwnership: false,
|
||||
|
@ -13,17 +19,24 @@ function ($q, $state, UserService, TeamService, ResourceControlService, Notifica
|
|||
formValidationError: ''
|
||||
};
|
||||
|
||||
ctrl.formValues = {
|
||||
Ownership: 'administrators',
|
||||
Ownership_Users: [],
|
||||
Ownership_Teams: []
|
||||
};
|
||||
ctrl.formValues = new AccessControlPanelData();
|
||||
|
||||
ctrl.authorizedUsers = [];
|
||||
ctrl.availableUsers = [];
|
||||
ctrl.authorizedTeams = [];
|
||||
ctrl.availableTeams = [];
|
||||
|
||||
ctrl.canEditOwnership = function() {
|
||||
const hasRC = ctrl.resourceControl;
|
||||
const inheritedVolume = hasRC && ctrl.resourceControl.Type === RCTI.CONTAINER && ctrl.resourceType === RCTS.VOLUME;
|
||||
const inheritedContainer = hasRC && ctrl.resourceControl.Type === RCTI.SERVICE && ctrl.resourceType === RCTS.CONTAINER;
|
||||
const inheritedFromStack = hasRC && ctrl.resourceControl.Type === RCTI.STACK && ctrl.resourceType !== RCTS.STACK;
|
||||
const hasSpecialDisable = ctrl.disableOwnershipChange;
|
||||
|
||||
return !inheritedVolume && !inheritedContainer && !inheritedFromStack && !hasSpecialDisable
|
||||
&& !ctrl.state.editOwnership && (ctrl.isAdmin || ctrl.state.canEditOwnership);
|
||||
}
|
||||
|
||||
ctrl.confirmUpdateOwnership = function () {
|
||||
if (!validateForm()) {
|
||||
return;
|
||||
|
@ -39,7 +52,7 @@ function ($q, $state, UserService, TeamService, ResourceControlService, Notifica
|
|||
var error = '';
|
||||
|
||||
var accessControlData = {
|
||||
AccessControlEnabled: ctrl.formValues.Ownership === 'public' ? false : true,
|
||||
AccessControlEnabled: ctrl.formValues.Ownership === RCO.PUBLIC ? false : true,
|
||||
Ownership: ctrl.formValues.Ownership,
|
||||
AuthorizedUsers: ctrl.formValues.Ownership_Users,
|
||||
AuthorizedTeams: ctrl.formValues.Ownership_Teams
|
||||
|
@ -53,32 +66,8 @@ function ($q, $state, UserService, TeamService, ResourceControlService, Notifica
|
|||
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 publicOnly = ctrl.formValues.Ownership === 'public' ? true : false;
|
||||
|
||||
return {
|
||||
ownership: ctrl.formValues.Ownership,
|
||||
authorizedUserIds: publicOnly ? [] : userIds,
|
||||
authorizedTeamIds: publicOnly ? [] : teamIds,
|
||||
publicOnly: publicOnly
|
||||
};
|
||||
}
|
||||
|
||||
function updateOwnership() {
|
||||
var resourceId = ctrl.resourceId;
|
||||
var ownershipParameters = processOwnershipFormValues();
|
||||
|
||||
ResourceControlService.applyResourceControlChange(ctrl.resourceType, resourceId,
|
||||
ctrl.resourceControl, ownershipParameters)
|
||||
ResourceControlService.applyResourceControlChange(ctrl.resourceType, ctrl.resourceId, ctrl.resourceControl, ctrl.formValues)
|
||||
.then(function success() {
|
||||
Notifications.success('Access control successfully updated');
|
||||
$state.reload();
|
||||
|
@ -95,17 +84,12 @@ function ($q, $state, UserService, TeamService, ResourceControlService, Notifica
|
|||
ctrl.isAdmin = isAdmin;
|
||||
var resourceControl = ctrl.resourceControl;
|
||||
|
||||
if (isAdmin) {
|
||||
if (resourceControl) {
|
||||
ctrl.formValues.Ownership = resourceControl.Ownership === 'private' ? 'restricted' : resourceControl.Ownership;
|
||||
} else {
|
||||
ctrl.formValues.Ownership = 'administrators';
|
||||
}
|
||||
if (isAdmin && resourceControl) {
|
||||
ctrl.formValues.Ownership = resourceControl.Ownership === RCO.PRIVATE ? RCO.RESTRICTED : resourceControl.Ownership;
|
||||
} else {
|
||||
ctrl.formValues.Ownership = 'administrators';
|
||||
ctrl.formValues.Ownership = RCO.ADMINISTRATORS;
|
||||
}
|
||||
|
||||
|
||||
ResourceControlService.retrieveOwnershipDetails(resourceControl)
|
||||
.then(function success(data) {
|
||||
ctrl.authorizedUsers = data.authorizedUsers;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import { ResourceControlOwnership } from 'Portainer/models/resourceControl/resourceControlOwnership';
|
||||
|
||||
export function AccessControlPanelData() {
|
||||
this.Ownership = ResourceControlOwnership.ADMINISTRATORS;
|
||||
this.Ownership_Users = [];
|
||||
this.Ownership_Teams = [];
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import _ from 'lodash-es';
|
||||
import './datatable.css';
|
||||
import { ResourceControlOwnership as RCO } from 'Portainer/models/resourceControl/resourceControlOwnership';
|
||||
|
||||
function isBetween(value, a, b) {
|
||||
return (value >= a && value <= b) || (value >= b && value <= a) ;
|
||||
|
@ -9,6 +10,8 @@ angular.module('portainer.app')
|
|||
.controller('GenericDatatableController', ['$interval', 'PaginationService', 'DatatableService', 'PAGINATION_MAX_ITEMS',
|
||||
function ($interval, PaginationService, DatatableService, PAGINATION_MAX_ITEMS) {
|
||||
|
||||
this.RCO = RCO;
|
||||
|
||||
this.state = {
|
||||
selectAll: false,
|
||||
orderBy: this.orderBy,
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
<td ng-if="$ctrl.showOwnershipColumn">
|
||||
<span>
|
||||
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
|
||||
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = 'administrators' }}
|
||||
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -2,6 +2,8 @@ import moment from 'moment';
|
|||
import _ from 'lodash-es';
|
||||
import filesize from 'filesize';
|
||||
|
||||
import { ResourceControlOwnership as RCO } from 'Portainer/models/resourceControl/resourceControlOwnership';
|
||||
|
||||
angular.module('portainer.app')
|
||||
.filter('truncate', function () {
|
||||
'use strict';
|
||||
|
@ -145,11 +147,11 @@ angular.module('portainer.app')
|
|||
'use strict';
|
||||
return function (ownership) {
|
||||
switch (ownership) {
|
||||
case 'private':
|
||||
case RCO.PRIVATE:
|
||||
return 'fa fa-eye-slash';
|
||||
case 'administrators':
|
||||
case RCO.ADMINISTRATORS:
|
||||
return 'fa fa-eye-slash';
|
||||
case 'restricted':
|
||||
case RCO.RESTRICTED:
|
||||
return 'fa fa-users';
|
||||
default:
|
||||
return 'fa fa-eye';
|
||||
|
|
15
app/portainer/helpers/networkHelper.js
Normal file
15
app/portainer/helpers/networkHelper.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import _ from 'lodash-es';
|
||||
import angular from 'angular';
|
||||
|
||||
class NetworkHelper {
|
||||
constructor(PREDEFINED_NETWORKS) {
|
||||
this.PREDEFINED_NETWORKS = PREDEFINED_NETWORKS;
|
||||
}
|
||||
|
||||
isSystemNetwork(item) {
|
||||
return _.includes(this.PREDEFINED_NETWORKS, item.Name);
|
||||
}
|
||||
}
|
||||
|
||||
export default NetworkHelper;
|
||||
angular.module('portainer.app').service('NetworkHelper', NetworkHelper);
|
|
@ -1,44 +1,121 @@
|
|||
import _ from 'lodash-es';
|
||||
import angular from 'angular';
|
||||
import { ResourceControlOwnership as RCO } from 'Portainer/models/resourceControl/resourceControlOwnership';
|
||||
import { ResourceControlOwnershipParameters } from '../models/resourceControl/resourceControlOwnershipParameters';
|
||||
|
||||
angular.module('portainer.app')
|
||||
.factory('ResourceControlHelper', [function ResourceControlHelperFactory() {
|
||||
'use strict';
|
||||
var helper = {};
|
||||
class ResourceControlHelper {
|
||||
|
||||
helper.retrieveAuthorizedUsers = function(resourceControl, users) {
|
||||
var authorizedUsers = [];
|
||||
angular.forEach(resourceControl.UserAccesses, function(access) {
|
||||
var user = _.find(users, { Id: access.UserId });
|
||||
/**
|
||||
* Transform ResourceControlViewModel to ResourceControlOwnershipParameters
|
||||
* @param {int} userId ID of User performing the action
|
||||
* @param {ResourceControlViewModel} resourceControl ResourceControl view model
|
||||
*/
|
||||
RCViewModelToOwnershipParameters(userId, resourceControl) {
|
||||
if (!resourceControl) {
|
||||
return new ResourceControlOwnershipParameters(true);
|
||||
}
|
||||
let adminOnly = false;
|
||||
let publicOnly = false;
|
||||
let users = [];
|
||||
let teams = [];
|
||||
switch (resourceControl.Ownership) {
|
||||
case RCO.PUBLIC:
|
||||
publicOnly = true;
|
||||
break;
|
||||
case RCO.PRIVATE:
|
||||
users = [userId];
|
||||
break;
|
||||
case RCO.RESTRICTED:
|
||||
users = _.map(resourceControl.UserAccesses, (user) => user.UserId);
|
||||
teams = _.map(resourceControl.TeamAccesses, (team) => team.TeamId);
|
||||
break;
|
||||
default:
|
||||
adminOnly = true;
|
||||
break;
|
||||
}
|
||||
return new ResourceControlOwnershipParameters(adminOnly, publicOnly, users, teams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform AccessControlFormData to ResourceControlOwnershipParameters
|
||||
* @param {int} userId ID of user performing the operation
|
||||
* @param {AccessControlFormData} formValues Form data (generated by AccessControlForm)
|
||||
* @param {int[]} subResources Sub Resources restricted by the ResourceControl
|
||||
*/
|
||||
RCFormDataToOwnershipParameters(userId, formValues, subResources=[]) {
|
||||
if (!formValues.AccessControlEnabled) {
|
||||
formValues.Ownership = RCO.PUBLIC;
|
||||
}
|
||||
|
||||
let adminOnly = false;
|
||||
let publicOnly = false;
|
||||
let users = [];
|
||||
let teams = [];
|
||||
switch (formValues.Ownership) {
|
||||
case RCO.PUBLIC:
|
||||
publicOnly = true;
|
||||
break;
|
||||
case RCO.PRIVATE:
|
||||
users.push(userId);
|
||||
break;
|
||||
case RCO.RESTRICTED:
|
||||
users = _.map(formValues.AuthorizedUsers, (user) => user.Id);
|
||||
teams = _.map(formValues.AuthorizedTeams, (team) => team.Id);
|
||||
break;
|
||||
default:
|
||||
adminOnly = true;
|
||||
break;
|
||||
}
|
||||
return new ResourceControlOwnershipParameters(adminOnly, publicOnly, users, teams, subResources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform AccessControlPanelData to ResourceControlOwnershipParameters
|
||||
* @param {AccessControlPanelData} formValues Form data (generated by AccessControlPanel)
|
||||
*/
|
||||
RCPanelDataToOwnershipParameters(formValues) {
|
||||
const adminOnly = formValues.Ownership === RCO.ADMINISTRATORS;
|
||||
const publicOnly = formValues.Ownership === RCO.PUBLIC;
|
||||
const userIds = publicOnly || adminOnly ? [] : _.map(formValues.Ownership_Users, (user) => user.Id);
|
||||
const teamIds = publicOnly || adminOnly ? [] : _.map(formValues.Ownership_Teams, (team) => team.Id);
|
||||
|
||||
return new ResourceControlOwnershipParameters(adminOnly, publicOnly, userIds, teamIds);
|
||||
}
|
||||
|
||||
retrieveAuthorizedUsers(resourceControl, users) {
|
||||
const authorizedUsers = [];
|
||||
_.forEach(resourceControl.UserAccesses, (access) => {
|
||||
const user = _.find(users, { Id: access.UserId });
|
||||
if (user) {
|
||||
authorizedUsers.push(user);
|
||||
}
|
||||
});
|
||||
return authorizedUsers;
|
||||
};
|
||||
}
|
||||
|
||||
helper.retrieveAuthorizedTeams = function(resourceControl, teams) {
|
||||
var authorizedTeams = [];
|
||||
angular.forEach(resourceControl.TeamAccesses, function(access) {
|
||||
var team = _.find(teams, { Id: access.TeamId });
|
||||
retrieveAuthorizedTeams(resourceControl, teams) {
|
||||
const authorizedTeams = [];
|
||||
_.forEach(resourceControl.TeamAccesses, (access) => {
|
||||
const team = _.find(teams, { Id: access.TeamId });
|
||||
if (team) {
|
||||
authorizedTeams.push(team);
|
||||
}
|
||||
});
|
||||
return authorizedTeams;
|
||||
};
|
||||
}
|
||||
|
||||
helper.isLeaderOfAnyRestrictedTeams = function(userMemberships, resourceControl) {
|
||||
var isTeamLeader = false;
|
||||
for (var i = 0; i < userMemberships.length; i++) {
|
||||
var membership = userMemberships[i];
|
||||
var found = _.find(resourceControl.TeamAccesses, { TeamId :membership.TeamId });
|
||||
isLeaderOfAnyRestrictedTeams(userMemberships, resourceControl) {
|
||||
let isTeamLeader = false;
|
||||
_.forEach(userMemberships, (membership) => {
|
||||
const found = _.find(resourceControl.TeamAccesses, { TeamId: membership.TeamId });
|
||||
if (found && membership.Role === 1) {
|
||||
isTeamLeader = true;
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
return isTeamLeader;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return helper;
|
||||
}]);
|
||||
export default ResourceControlHelper;
|
||||
angular.module('portainer.app').service('ResourceControlHelper', ResourceControlHelper);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { ResourceControlOwnership as RCO } from 'Portainer/models/resourceControl/resourceControlOwnership';
|
||||
|
||||
export function ResourceControlViewModel(data) {
|
||||
this.Id = data.Id;
|
||||
this.Type = data.Type;
|
||||
|
@ -5,17 +7,18 @@ export function ResourceControlViewModel(data) {
|
|||
this.UserAccesses = data.UserAccesses;
|
||||
this.TeamAccesses = data.TeamAccesses;
|
||||
this.Public = data.Public;
|
||||
this.System = data.System;
|
||||
this.Ownership = determineOwnership(this);
|
||||
}
|
||||
|
||||
function determineOwnership(resourceControl) {
|
||||
if (resourceControl.Public) {
|
||||
return 'public';
|
||||
return RCO.PUBLIC;
|
||||
} else if (resourceControl.UserAccesses.length === 1 && resourceControl.TeamAccesses.length === 0) {
|
||||
return 'private';
|
||||
return RCO.PRIVATE;
|
||||
} else if (resourceControl.UserAccesses.length > 1 || resourceControl.TeamAccesses.length > 0) {
|
||||
return 'restricted';
|
||||
return RCO.RESTRICTED;
|
||||
} else {
|
||||
return 'administrators';
|
||||
return RCO.ADMINISTRATORS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* Payload for resourceControleCreate operation
|
||||
* @param {string} resourceId ID of involved Resource
|
||||
* @param {ResourceControlResourceType} resourceType Type of involved Resource
|
||||
* @param {ResourceControlOwnershipParameters} data Transcient type from view data to payload
|
||||
*/
|
||||
export function ResourceControlCreatePayload(resourceId, resourceType, data) {
|
||||
void data;
|
||||
|
||||
this.ResourceID = resourceId;
|
||||
this.Type = resourceType;
|
||||
this.Public = data.Public;
|
||||
this.AdministratorsOnly = data.AdministratorsOnly;
|
||||
this.Users = data.Users;
|
||||
this.Teams = data.Teams;
|
||||
this.SubResourceIDs = data.SubResourceIDs;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export const ResourceControlOwnership = Object.freeze({
|
||||
PUBLIC: 'public',
|
||||
PRIVATE: 'private',
|
||||
RESTRICTED: 'restricted',
|
||||
ADMINISTRATORS: 'administrators'
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Transcient type from view data to payload
|
||||
* @param {bool} adminOnly is ResourceControl restricted to admin only
|
||||
* @param {bool} publicOnly is ResourceControl exposed to public
|
||||
* @param {[]int} users Authorized UserIDs array
|
||||
* @param {[]int} teams Authorized TeamIDs array
|
||||
* @param {[]int} subResources subResourceIDs array
|
||||
*/
|
||||
export function ResourceControlOwnershipParameters(adminOnly=false, publicOnly=false, users=[], teams=[], subResources=[]) {
|
||||
this.AdministratorsOnly = adminOnly;
|
||||
this.Public = publicOnly;
|
||||
this.Users = users;
|
||||
this.Teams = teams;
|
||||
this.SubResourceIDs = subResources;
|
||||
}
|
22
app/portainer/models/resourceControl/resourceControlTypes.js
Normal file
22
app/portainer/models/resourceControl/resourceControlTypes.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
export const ResourceControlTypeString = Object.freeze({
|
||||
CONFIG: 'config',
|
||||
CONTAINER: 'container',
|
||||
NETWORK:'network',
|
||||
SECRET:'secret',
|
||||
SERVICE:'service',
|
||||
STACK:'stack',
|
||||
VOLUME:'volume'
|
||||
});
|
||||
|
||||
/**
|
||||
* ResourceType int defined in portainer.go as ResourceControlType
|
||||
*/
|
||||
export const ResourceControlTypeInt = Object.freeze({
|
||||
CONTAINER: 1,
|
||||
SERVICE: 2,
|
||||
VOLUME: 3,
|
||||
NETWORK: 4,
|
||||
SECRET: 5,
|
||||
STACK: 6,
|
||||
CONFIG: 7
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* Payload for resourceControlUpdate operation
|
||||
* @param {ResourceControlOwnershipParameters} data
|
||||
*/
|
||||
export function ResourceControlUpdatePayload(data) {
|
||||
this.Public = data.Public;
|
||||
this.AdministratorsOnly = data.AdministratorsOnly;
|
||||
this.Users = data.Users;
|
||||
this.Teams = data.Teams;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { ResourceControlViewModel } from '../../portainer/models/resourceControl';
|
||||
import { ResourceControlViewModel } from 'Portainer/models/resourceControl/resourceControl';
|
||||
|
||||
export function StackViewModel(data) {
|
||||
this.Id = data.Id;
|
||||
|
|
|
@ -1,80 +1,108 @@
|
|||
import _ from 'lodash-es';
|
||||
|
||||
angular.module('portainer.app')
|
||||
.factory('ResourceControlService', ['$q', 'ResourceControl', 'UserService', 'TeamService', 'ResourceControlHelper', function ResourceControlServiceFactory($q, ResourceControl, UserService, TeamService, ResourceControlHelper) {
|
||||
.factory('ResourceControlService', ['$q', 'ResourceControl', 'UserService', 'TeamService', 'ResourceControlHelper',
|
||||
function ResourceControlServiceFactory($q, ResourceControl, UserService, TeamService, ResourceControlHelper) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
const service = {};
|
||||
|
||||
service.createResourceControl = function(publicOnly, userIDs, teamIDs, resourceID, type, subResourceIDs) {
|
||||
service.duplicateResourceControl = duplicateResourceControl;
|
||||
service.applyResourceControlChange = applyResourceControlChange;
|
||||
service.applyResourceControl = applyResourceControl;
|
||||
service.retrieveOwnershipDetails = retrieveOwnershipDetails;
|
||||
service.retrieveUserPermissionsOnResource =retrieveUserPermissionsOnResource;
|
||||
|
||||
/**
|
||||
* PRIVATE SECTION
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a ResourceControl
|
||||
* @param {ResourceControlTypeString} rcType Type of ResourceControl
|
||||
* @param {string} rcID ID of involved resource
|
||||
* @param {ResourceControlOwnershipParameters} ownershipParameters Transcient type from view data to payload
|
||||
*/
|
||||
function createResourceControl(rcType, resourceID, ownershipParameters) {
|
||||
var payload = {
|
||||
Type: type,
|
||||
Public: publicOnly,
|
||||
Type: rcType,
|
||||
Public: ownershipParameters.Public,
|
||||
AdministratorsOnly: ownershipParameters.AdministratorsOnly,
|
||||
ResourceID: resourceID,
|
||||
Users: userIDs,
|
||||
Teams: teamIDs,
|
||||
SubResourceIDs: subResourceIDs
|
||||
Users: ownershipParameters.Users,
|
||||
Teams: ownershipParameters.Teams,
|
||||
SubResourceIds: ownershipParameters.SubResourceIDs
|
||||
};
|
||||
return ResourceControl.create({}, payload).$promise;
|
||||
};
|
||||
}
|
||||
|
||||
service.deleteResourceControl = function(rcID) {
|
||||
return ResourceControl.remove({id: rcID}).$promise;
|
||||
};
|
||||
|
||||
service.updateResourceControl = function(publicOnly, userIDs, teamIDs, resourceControlId) {
|
||||
var payload = {
|
||||
Public: publicOnly,
|
||||
Users: userIDs,
|
||||
Teams: teamIDs
|
||||
/**
|
||||
* Update a ResourceControl
|
||||
* @param {String} rcID ID of involved resource
|
||||
* @param {ResourceControlOwnershipParameters} ownershipParameters Transcient type from view data to payload
|
||||
*/
|
||||
function updateResourceControl(rcID, ownershipParameters) {
|
||||
const payload = {
|
||||
AdministratorsOnly: ownershipParameters.AdministratorsOnly,
|
||||
Public: ownershipParameters.Public,
|
||||
Users: ownershipParameters.Users,
|
||||
Teams: ownershipParameters.Teams
|
||||
};
|
||||
return ResourceControl.update({id: resourceControlId}, payload).$promise;
|
||||
};
|
||||
|
||||
service.applyResourceControl = function(resourceControlType, resourceIdentifier, userId, accessControlData, subResources) {
|
||||
if (!accessControlData.AccessControlEnabled) {
|
||||
accessControlData.Ownership = 'public';
|
||||
}
|
||||
return ResourceControl.update({id: rcID}, payload).$promise;
|
||||
}
|
||||
|
||||
var authorizedUserIds = [];
|
||||
var authorizedTeamIds = [];
|
||||
var publicOnly = false;
|
||||
switch (accessControlData.Ownership) {
|
||||
case 'public':
|
||||
publicOnly = true;
|
||||
break;
|
||||
case 'private':
|
||||
authorizedUserIds.push(userId);
|
||||
break;
|
||||
case 'restricted':
|
||||
angular.forEach(accessControlData.AuthorizedUsers, function(user) {
|
||||
authorizedUserIds.push(user.Id);
|
||||
});
|
||||
angular.forEach(accessControlData.AuthorizedTeams, function(team) {
|
||||
authorizedTeamIds.push(team.Id);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
return service.createResourceControl(publicOnly, authorizedUserIds,
|
||||
authorizedTeamIds, resourceIdentifier, resourceControlType, subResources);
|
||||
};
|
||||
/**
|
||||
* END PRIVATE SECTION
|
||||
*/
|
||||
|
||||
service.applyResourceControlChange = function(resourceControlType, resourceId, resourceControl, ownershipParameters) {
|
||||
/**
|
||||
* PUBLIC SECTION
|
||||
*/
|
||||
|
||||
/**
|
||||
* Apply a ResourceControl after Resource creation
|
||||
* @param {int} userId ID of User performing the action
|
||||
* @param {AccessControlFormData} accessControlData ResourceControl to apply
|
||||
* @param {ResourceControlViewModel} resourceControl ResourceControl to update
|
||||
* @param {[]int} subResources SubResources managed by the ResourceControl
|
||||
*/
|
||||
function applyResourceControl(userId, accessControlData, resourceControl, subResources=[]) {
|
||||
const ownershipParameters = ResourceControlHelper.RCFormDataToOwnershipParameters(userId, accessControlData, subResources);
|
||||
return updateResourceControl(resourceControl.Id, ownershipParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate an existing ResourceControl (default to AdministratorsOnly if undefined)
|
||||
* @param {int} userId ID of User performing the action
|
||||
* @param {ResourceControlViewModel} oldResourceControl ResourceControl to duplicate
|
||||
* @param {ResourceControlViewModel} newResourceControl ResourceControl to apply duplication to
|
||||
*/
|
||||
function duplicateResourceControl(userId, oldResourceControl, newResourceControl) {
|
||||
const ownershipParameters = ResourceControlHelper.RCViewModelToOwnershipParameters(userId, oldResourceControl);
|
||||
return updateResourceControl(newResourceControl.Id, ownershipParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing ResourceControl or create a new one on existing resource without RC
|
||||
* @param {ResourceControlTypeString} rcType Type of ResourceControl
|
||||
* @param {String} resourceId ID of involved Resource
|
||||
* @param {ResourceControlViewModel} resourceControl Previous ResourceControl (can be undefined)
|
||||
* @param {AccessControlPanelData} formValues View data generated by AccessControlPanel
|
||||
*/
|
||||
function applyResourceControlChange(rcType, resourceId, resourceControl, formValues) {
|
||||
const ownershipParameters = ResourceControlHelper.RCPanelDataToOwnershipParameters(formValues);
|
||||
if (resourceControl) {
|
||||
if (ownershipParameters.ownership === 'administrators') {
|
||||
return service.deleteResourceControl(resourceControl.Id);
|
||||
} else {
|
||||
return service.updateResourceControl(ownershipParameters.publicOnly, ownershipParameters.authorizedUserIds,
|
||||
ownershipParameters.authorizedTeamIds, resourceControl.Id);
|
||||
}
|
||||
return updateResourceControl(resourceControl.Id, ownershipParameters);
|
||||
} else {
|
||||
return service.createResourceControl(ownershipParameters.publicOnly, ownershipParameters.authorizedUserIds,
|
||||
ownershipParameters.authorizedTeamIds, resourceId, resourceControlType);
|
||||
return createResourceControl(rcType, resourceId, ownershipParameters);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
service.retrieveOwnershipDetails = function(resourceControl) {
|
||||
/**
|
||||
* Retrive users and team details for ResourceControlViewModel
|
||||
* @param {ResourceControlViewModel} resourceControl ResourceControl view model
|
||||
*/
|
||||
function retrieveOwnershipDetails(resourceControl) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
if (!resourceControl) {
|
||||
|
@ -96,9 +124,9 @@ angular.module('portainer.app')
|
|||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
}
|
||||
|
||||
service.retrieveUserPermissionsOnResource = function(userID, isAdministrator, resourceControl) {
|
||||
function retrieveUserPermissionsOnResource(userID, isAdministrator, resourceControl) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
if (!resourceControl || isAdministrator) {
|
||||
|
@ -123,7 +151,11 @@ angular.module('portainer.app')
|
|||
}
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* END PUBLIC SECTION
|
||||
*/
|
||||
|
||||
return service;
|
||||
}]);
|
||||
|
|
|
@ -208,11 +208,6 @@ function StackServiceFactory($q, Stack, ResourceControlService, FileUploadServic
|
|||
var deferred = $q.defer();
|
||||
|
||||
Stack.remove({ id: stack.Id ? stack.Id : stack.Name, external: external, endpointId: endpointId }).$promise
|
||||
.then(function success() {
|
||||
if (stack.ResourceControl && stack.ResourceControl.Id) {
|
||||
return ResourceControlService.deleteResourceControl(stack.ResourceControl.Id);
|
||||
}
|
||||
})
|
||||
.then(function success() {
|
||||
deferred.resolve();
|
||||
})
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { ResourceControlOwnership as RCO } from 'Portainer/models/resourceControl/resourceControlOwnership';
|
||||
|
||||
angular.module('portainer.app')
|
||||
.factory('FormValidator', [function FormValidatorFactory() {
|
||||
'use strict';
|
||||
|
@ -9,11 +11,11 @@ angular.module('portainer.app')
|
|||
return '';
|
||||
}
|
||||
|
||||
if (isAdmin && accessControlData.Ownership === 'restricted' &&
|
||||
if (isAdmin && accessControlData.Ownership === RCO.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' &&
|
||||
} else if (!isAdmin && accessControlData.Ownership === RCO.RESTRICTED &&
|
||||
accessControlData.AuthorizedTeams.length === 0) {
|
||||
return 'You must specify at least a team.';
|
||||
}
|
||||
|
|
|
@ -98,7 +98,6 @@ function ($scope, $state, StackService, Authentication, Notifications, FormValid
|
|||
var accessControlData = $scope.formValues.AccessControlData;
|
||||
var userDetails = Authentication.getUserDetails();
|
||||
var isAdmin = Authentication.isAdmin();
|
||||
var userId = userDetails.ID;
|
||||
|
||||
if (method === 'editor' && $scope.formValues.StackFileContent === '') {
|
||||
$scope.state.formValidationError = 'Stack file content must not be empty';
|
||||
|
@ -116,8 +115,13 @@ function ($scope, $state, StackService, Authentication, Notifications, FormValid
|
|||
}
|
||||
$scope.state.actionInProgress = true;
|
||||
action(name, method)
|
||||
.then(function success() {
|
||||
return ResourceControlService.applyResourceControl('stack', name, userId, accessControlData, []);
|
||||
.then(function success(data) {
|
||||
if (data.data) {
|
||||
data = data.data;
|
||||
}
|
||||
const userId = userDetails.ID;
|
||||
const resourceControl = data.ResourceControl;
|
||||
return ResourceControlService.applyResourceControl(userId, accessControlData, resourceControl);
|
||||
})
|
||||
.then(function success() {
|
||||
Notifications.success('Stack successfully deployed');
|
||||
|
|
|
@ -79,8 +79,8 @@ function ($scope, $q, $state, $transition$, $anchorScroll, ContainerService, Ima
|
|||
return ContainerService.createAndStartContainer(templateConfiguration);
|
||||
})
|
||||
.then(function success(data) {
|
||||
var containerIdentifier = data.Id;
|
||||
return ResourceControlService.applyResourceControl('container', containerIdentifier, userId, accessControlData, generatedVolumeIds);
|
||||
const resourceControl = data.Portainer.ResourceControl;
|
||||
return ResourceControlService.applyResourceControl(userId, accessControlData, resourceControl, generatedVolumeIds);
|
||||
})
|
||||
.then(function success() {
|
||||
Notifications.success('Container successfully created');
|
||||
|
@ -111,8 +111,9 @@ function ($scope, $q, $state, $transition$, $anchorScroll, ContainerService, Ima
|
|||
|
||||
var endpointId = EndpointProvider.endpointID();
|
||||
StackService.createComposeStackFromGitRepository(stackName, repositoryOptions, template.Env, endpointId)
|
||||
.then(function success() {
|
||||
return ResourceControlService.applyResourceControl('stack', stackName, userId, accessControlData, []);
|
||||
.then(function success(data) {
|
||||
const resourceControl = data.Portainer.ResourceControl;
|
||||
return ResourceControlService.applyResourceControl(userId, accessControlData, resourceControl);
|
||||
})
|
||||
.then(function success() {
|
||||
Notifications.success('Stack successfully deployed');
|
||||
|
@ -148,8 +149,9 @@ function ($scope, $q, $state, $transition$, $anchorScroll, ContainerService, Ima
|
|||
|
||||
var endpointId = EndpointProvider.endpointID();
|
||||
StackService.createSwarmStackFromGitRepository(stackName, repositoryOptions, env, endpointId)
|
||||
.then(function success() {
|
||||
return ResourceControlService.applyResourceControl('stack', stackName, userId, accessControlData, []);
|
||||
.then(function success(data) {
|
||||
const resourceControl = data.Portainer.ResourceControl;
|
||||
return ResourceControlService.applyResourceControl(userId, accessControlData, resourceControl);
|
||||
})
|
||||
.then(function success() {
|
||||
Notifications.success('Stack successfully deployed');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue