mirror of
https://github.com/portainer/portainer.git
synced 2025-08-02 20:35:25 +02:00
feat(uac): add multi user management and UAC (#647)
This commit is contained in:
parent
f28f223624
commit
80d50378c5
91 changed files with 3973 additions and 866 deletions
45
app/app.js
45
app/app.js
|
@ -30,6 +30,7 @@ angular.module('portainer', [
|
|||
'createVolume',
|
||||
'docker',
|
||||
'endpoint',
|
||||
'endpointAccess',
|
||||
'endpointInit',
|
||||
'endpoints',
|
||||
'events',
|
||||
|
@ -47,6 +48,8 @@ angular.module('portainer', [
|
|||
'swarm',
|
||||
'task',
|
||||
'templates',
|
||||
'user',
|
||||
'users',
|
||||
'volumes'])
|
||||
.config(['$stateProvider', '$urlRouterProvider', '$httpProvider', 'localStorageServiceProvider', 'jwtOptionsProvider', 'AnalyticsProvider', function ($stateProvider, $urlRouterProvider, $httpProvider, localStorageServiceProvider, jwtOptionsProvider, AnalyticsProvider) {
|
||||
'use strict';
|
||||
|
@ -292,6 +295,19 @@ angular.module('portainer', [
|
|||
}
|
||||
}
|
||||
})
|
||||
.state('endpoint.access', {
|
||||
url: '^/endpoints/:id/access',
|
||||
views: {
|
||||
"content@": {
|
||||
templateUrl: 'app/components/endpointAccess/endpointAccess.html',
|
||||
controller: 'EndpointAccessController'
|
||||
},
|
||||
"sidebar@": {
|
||||
templateUrl: 'app/components/sidebar/sidebar.html',
|
||||
controller: 'SidebarController'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('endpointInit', {
|
||||
url: '/init/endpoint',
|
||||
views: {
|
||||
|
@ -457,6 +473,32 @@ angular.module('portainer', [
|
|||
}
|
||||
}
|
||||
})
|
||||
.state('users', {
|
||||
url: '/users/',
|
||||
views: {
|
||||
"content@": {
|
||||
templateUrl: 'app/components/users/users.html',
|
||||
controller: 'UsersController'
|
||||
},
|
||||
"sidebar@": {
|
||||
templateUrl: 'app/components/sidebar/sidebar.html',
|
||||
controller: 'SidebarController'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('user', {
|
||||
url: '^/users/:id',
|
||||
views: {
|
||||
"content@": {
|
||||
templateUrl: 'app/components/user/user.html',
|
||||
controller: 'UserController'
|
||||
},
|
||||
"sidebar@": {
|
||||
templateUrl: 'app/components/sidebar/sidebar.html',
|
||||
controller: 'SidebarController'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('swarm', {
|
||||
url: '/swarm/',
|
||||
views: {
|
||||
|
@ -488,7 +530,8 @@ angular.module('portainer', [
|
|||
};
|
||||
});
|
||||
}])
|
||||
.run(['$rootScope', '$state', 'Authentication', 'authManager', 'StateManager', 'Messages', 'Analytics', function ($rootScope, $state, Authentication, authManager, StateManager, Messages, Analytics) {
|
||||
.run(['$rootScope', '$state', 'Authentication', 'authManager', 'StateManager', 'EndpointProvider', 'Messages', 'Analytics', function ($rootScope, $state, Authentication, authManager, StateManager, EndpointProvider, Messages, Analytics) {
|
||||
EndpointProvider.initialize();
|
||||
StateManager.initialize().then(function success(state) {
|
||||
if (state.application.authentication) {
|
||||
authManager.checkAuthOnRefresh();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('auth', [])
|
||||
.controller('AuthenticationController', ['$scope', '$state', '$stateParams', '$window', '$timeout', '$sanitize', 'Config', 'Authentication', 'Users', 'EndpointService', 'StateManager', 'Messages',
|
||||
function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Authentication, Users, EndpointService, StateManager, Messages) {
|
||||
.controller('AuthenticationController', ['$scope', '$state', '$stateParams', '$window', '$timeout', '$sanitize', 'Config', 'Authentication', 'Users', 'EndpointService', 'StateManager', 'EndpointProvider', 'Messages',
|
||||
function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Authentication, Users, EndpointService, StateManager, EndpointProvider, Messages) {
|
||||
|
||||
$scope.authData = {
|
||||
username: 'admin',
|
||||
|
@ -14,18 +14,34 @@ function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Au
|
|||
};
|
||||
|
||||
if (!$scope.applicationState.application.authentication) {
|
||||
EndpointService.getActive().then(function success(data) {
|
||||
StateManager.updateEndpointState(true)
|
||||
.then(function success() {
|
||||
$state.go('dashboard');
|
||||
}, function error(err) {
|
||||
Messages.error("Failure", err, 'Unable to connect to the Docker endpoint');
|
||||
});
|
||||
}, function error(err) {
|
||||
if (err.status === 404) {
|
||||
EndpointService.endpoints()
|
||||
.then(function success(data) {
|
||||
if (data.length > 0) {
|
||||
endpointID = EndpointProvider.endpointID();
|
||||
if (!endpointID) {
|
||||
endpointID = data[0].Id;
|
||||
EndpointProvider.setEndpointID(endpointID);
|
||||
}
|
||||
StateManager.updateEndpointState(true)
|
||||
.then(function success() {
|
||||
$state.go('dashboard');
|
||||
}, function error(err) {
|
||||
Messages.error("Failure", err, 'Unable to connect to the Docker endpoint');
|
||||
});
|
||||
}
|
||||
else {
|
||||
$state.go('endpointInit');
|
||||
}
|
||||
}, function error(err) {
|
||||
Messages.error("Failure", err, 'Unable to retrieve endpoints');
|
||||
});
|
||||
} else {
|
||||
Users.checkAdminUser({}, function () {},
|
||||
function (e) {
|
||||
if (e.status === 404) {
|
||||
$scope.initPassword = true;
|
||||
} else {
|
||||
Messages.error("Failure", err, 'Unable to verify Docker endpoint existence');
|
||||
Messages.error("Failure", e, 'Unable to verify administrator account existence');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -47,15 +63,6 @@ function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Au
|
|||
$scope.logo = c.logo;
|
||||
});
|
||||
|
||||
Users.checkAdminUser({}, function (d) {},
|
||||
function (e) {
|
||||
if (e.status === 404) {
|
||||
$scope.initPassword = true;
|
||||
} else {
|
||||
Messages.error("Failure", e, 'Unable to verify administrator account existence');
|
||||
}
|
||||
});
|
||||
|
||||
$scope.createAdminUser = function() {
|
||||
var password = $sanitize($scope.initPasswordData.password);
|
||||
Users.initAdminUser({password: password}, function (d) {
|
||||
|
@ -75,23 +82,33 @@ function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Au
|
|||
$scope.authenticationError = false;
|
||||
var username = $sanitize($scope.authData.username);
|
||||
var password = $sanitize($scope.authData.password);
|
||||
Authentication.login(username, password).then(function success() {
|
||||
EndpointService.getActive().then(function success(data) {
|
||||
Authentication.login(username, password)
|
||||
.then(function success(data) {
|
||||
return EndpointService.endpoints();
|
||||
})
|
||||
.then(function success(data) {
|
||||
var userDetails = Authentication.getUserDetails();
|
||||
if (data.length > 0) {
|
||||
endpointID = EndpointProvider.endpointID();
|
||||
if (!endpointID) {
|
||||
endpointID = data[0].Id;
|
||||
EndpointProvider.setEndpointID(endpointID);
|
||||
}
|
||||
StateManager.updateEndpointState(true)
|
||||
.then(function success() {
|
||||
$state.go('dashboard');
|
||||
}, function error(err) {
|
||||
Messages.error("Failure", err, 'Unable to connect to the Docker endpoint');
|
||||
});
|
||||
}, function error(err) {
|
||||
if (err.status === 404) {
|
||||
$state.go('endpointInit');
|
||||
} else {
|
||||
Messages.error("Failure", err, 'Unable to verify Docker endpoint existence');
|
||||
}
|
||||
});
|
||||
}, function error() {
|
||||
$scope.authData.error = 'Invalid credentials';
|
||||
}
|
||||
else if (data.length === 0 && userDetails.role === 1) {
|
||||
$state.go('endpointInit');
|
||||
} else if (data.length === 0 && userDetails.role === 2) {
|
||||
$scope.authData.error = 'User not allowed. Please contact your administrator.';
|
||||
}
|
||||
})
|
||||
.catch(function error(err) {
|
||||
$scope.authData.error = 'Authentication error';
|
||||
});
|
||||
};
|
||||
}]);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('containerConsole', [])
|
||||
.controller('ContainerConsoleController', ['$scope', '$stateParams', 'Settings', 'Container', 'Image', 'Exec', '$timeout', 'Messages',
|
||||
function ($scope, $stateParams, Settings, Container, Image, Exec, $timeout, Messages) {
|
||||
.controller('ContainerConsoleController', ['$scope', '$stateParams', 'Settings', 'Container', 'Image', 'Exec', '$timeout', 'EndpointProvider', 'Messages',
|
||||
function ($scope, $stateParams, Settings, Container, Image, Exec, $timeout, EndpointProvider, Messages) {
|
||||
$scope.state = {};
|
||||
$scope.state.loaded = false;
|
||||
$scope.state.connected = false;
|
||||
|
@ -55,7 +55,7 @@ function ($scope, $stateParams, Settings, Container, Image, Exec, $timeout, Mess
|
|||
} else {
|
||||
var execId = d.Id;
|
||||
resizeTTY(execId, termHeight, termWidth);
|
||||
var url = window.location.href.split('#')[0] + 'api/websocket/exec?id=' + execId;
|
||||
var url = window.location.href.split('#')[0] + 'api/websocket/exec?id=' + execId + '&endpointId=' + EndpointProvider.endpointID();
|
||||
if (url.indexOf('https') > -1) {
|
||||
url = url.replace('https://', 'wss://');
|
||||
} else {
|
||||
|
|
|
@ -90,6 +90,13 @@
|
|||
<span ng-show="sortType == 'Ports' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th ng-if="applicationState.application.authentication">
|
||||
<a ui-sref="containers" ng-click="order('Metadata.ResourceControl.OwnerId')">
|
||||
Ownership
|
||||
<span ng-show="sortType == 'Metadata.ResourceControl.OwnerId' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortType == 'Metadata.ResourceControl.OwnerId' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -107,12 +114,43 @@
|
|||
</a>
|
||||
<span ng-if="container.Ports.length == 0" >-</span>
|
||||
</td>
|
||||
<td ng-if="applicationState.application.authentication">
|
||||
<span ng-if="!container.Metadata.ResourceControl">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
<span ng-if="container.Labels['com.docker.swarm.service.id']">
|
||||
Public service
|
||||
</span>
|
||||
<span ng-if="!container.Labels['com.docker.swarm.service.id']">
|
||||
Public
|
||||
</span>
|
||||
</span>
|
||||
<span ng-if="container.Metadata.ResourceControl.OwnerId === user.ID">
|
||||
<i class="fa fa-eye-slash" aria-hidden="true"></i>
|
||||
<span ng-if="container.Labels['com.docker.swarm.service.id']">
|
||||
Private service
|
||||
</span>
|
||||
<span ng-if="!container.Labels['com.docker.swarm.service.id']">
|
||||
Private
|
||||
<a ng-click="switchOwnership(container)" class="interactive"><i class="fa fa-eye" aria-hidden="true" style="margin-left: 7px;"></i> Switch to public</a>
|
||||
</span>
|
||||
</span>
|
||||
<span ng-if="container.Metadata.ResourceControl && container.Metadata.ResourceControl.OwnerId !== user.ID">
|
||||
<i class="fa fa-eye-slash" aria-hidden="true"></i>
|
||||
<span ng-if="container.Labels['com.docker.swarm.service.id']">
|
||||
Private service <span ng-if="container.Owner">(owner: {{ container.Owner }})</span>
|
||||
</span>
|
||||
<span ng-if="!container.Labels['com.docker.swarm.service.id']">
|
||||
Private <span ng-if="container.Owner">(owner: {{ container.Owner }})</span>
|
||||
<a ng-click="switchOwnership(container)" class="interactive"><i class="fa fa-eye" aria-hidden="true" style="margin-left: 7px;"></i> Switch to public</a>
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!containers">
|
||||
<td colspan="8" class="text-center text-muted">Loading...</td>
|
||||
<td colspan="9" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="containers.length == 0">
|
||||
<td colspan="8" class="text-center text-muted">No containers available.</td>
|
||||
<td colspan="9" class="text-center text-muted">No containers available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('containers', [])
|
||||
.controller('ContainersController', ['$scope', '$filter', 'Container', 'ContainerHelper', 'Info', 'Settings', 'Messages', 'Config', 'Pagination', 'EntityListService',
|
||||
function ($scope, $filter, Container, ContainerHelper, Info, Settings, Messages, Config, Pagination, EntityListService) {
|
||||
.controller('ContainersController', ['$q', '$scope', '$filter', 'Container', 'ContainerHelper', 'Info', 'Settings', 'Messages', 'Config', 'Pagination', 'EntityListService', 'ModalService', 'Authentication', 'ResourceControlService', 'UserService',
|
||||
function ($q, $scope, $filter, Container, ContainerHelper, Info, Settings, Messages, Config, Pagination, EntityListService, ModalService, Authentication, ResourceControlService, UserService) {
|
||||
$scope.state = {};
|
||||
$scope.state.pagination_count = Pagination.getPaginationCount('containers');
|
||||
$scope.state.displayAll = Settings.displayAll;
|
||||
|
@ -17,8 +17,51 @@ function ($scope, $filter, Container, ContainerHelper, Info, Settings, Messages,
|
|||
Pagination.setPaginationCount('containers', $scope.state.pagination_count);
|
||||
};
|
||||
|
||||
function removeContainerResourceControl(container) {
|
||||
volumeResourceControlQueries = [];
|
||||
angular.forEach(container.Mounts, function (volume) {
|
||||
volumeResourceControlQueries.push(ResourceControlService.removeVolumeResourceControl(container.Metadata.ResourceControl.OwnerId, volume.Name));
|
||||
});
|
||||
|
||||
$q.all(volumeResourceControlQueries)
|
||||
.then(function success() {
|
||||
return ResourceControlService.removeContainerResourceControl(container.Metadata.ResourceControl.OwnerId, container.Id);
|
||||
})
|
||||
.then(function success() {
|
||||
delete container.Metadata.ResourceControl;
|
||||
Messages.send('Ownership changed to public', container.Id);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to change container ownership");
|
||||
});
|
||||
}
|
||||
|
||||
$scope.switchOwnership = function(container) {
|
||||
ModalService.confirmContainerOwnershipChange(function (confirmed) {
|
||||
if(!confirmed) { return; }
|
||||
removeContainerResourceControl(container);
|
||||
});
|
||||
};
|
||||
|
||||
function mapUsersToContainers(users) {
|
||||
angular.forEach($scope.containers, function (container) {
|
||||
if (container.Metadata) {
|
||||
var containerRC = container.Metadata.ResourceControl;
|
||||
if (containerRC && containerRC.OwnerId != $scope.user.ID) {
|
||||
angular.forEach(users, function (user) {
|
||||
if (containerRC.OwnerId === user.Id) {
|
||||
container.Owner = user.Username;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var update = function (data) {
|
||||
$('#loadContainersSpinner').show();
|
||||
var userDetails = Authentication.getUserDetails();
|
||||
$scope.user = userDetails;
|
||||
$scope.state.selectedItemCount = 0;
|
||||
Container.query(data, function (d) {
|
||||
var containers = d;
|
||||
|
@ -41,7 +84,20 @@ function ($scope, $filter, Container, ContainerHelper, Info, Settings, Messages,
|
|||
}
|
||||
return model;
|
||||
});
|
||||
$('#loadContainersSpinner').hide();
|
||||
if (userDetails.role === 1) {
|
||||
UserService.users()
|
||||
.then(function success(data) {
|
||||
mapUsersToContainers(data);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to retrieve users");
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadContainersSpinner').hide();
|
||||
});
|
||||
} else {
|
||||
$('#loadContainersSpinner').hide();
|
||||
}
|
||||
}, function (e) {
|
||||
$('#loadContainersSpinner').hide();
|
||||
Messages.error("Failure", e, "Unable to retrieve containers");
|
||||
|
@ -77,7 +133,17 @@ function ($scope, $filter, Container, ContainerHelper, Info, Settings, Messages,
|
|||
Messages.send("Error", d.message);
|
||||
}
|
||||
else {
|
||||
Messages.send("Container " + msg, c.Id);
|
||||
if (c.Metadata && c.Metadata.ResourceControl) {
|
||||
ResourceControlService.removeContainerResourceControl(c.Metadata.ResourceControl.OwnerId, c.Id)
|
||||
.then(function success() {
|
||||
Messages.send("Container " + msg, c.Id);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to remove container ownership");
|
||||
});
|
||||
} else {
|
||||
Messages.send("Container " + msg, c.Id);
|
||||
}
|
||||
}
|
||||
complete();
|
||||
}, function (e) {
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
// @@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', ['$scope', '$state', '$stateParams', '$filter', 'Config', 'Info', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'Network', 'Messages',
|
||||
function ($scope, $state, $stateParams, $filter, Config, Info, Container, ContainerHelper, Image, ImageHelper, Volume, Network, Messages) {
|
||||
.controller('CreateContainerController', ['$scope', '$state', '$stateParams', '$filter', 'Config', 'Info', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'Network', 'ResourceControlService', 'Authentication', 'Messages',
|
||||
function ($scope, $state, $stateParams, $filter, Config, Info, Container, ContainerHelper, Image, ImageHelper, Volume, Network, ResourceControlService, Authentication, Messages) {
|
||||
|
||||
$scope.formValues = {
|
||||
Ownership: $scope.applicationState.application.authentication ? 'private' : '',
|
||||
alwaysPull: true,
|
||||
Console: 'none',
|
||||
Volumes: [],
|
||||
|
@ -116,26 +119,40 @@ function ($scope, $state, $stateParams, $filter, Config, Info, Container, Contai
|
|||
});
|
||||
});
|
||||
|
||||
// TODO: centralize, already present in templatesController
|
||||
function startContainer(containerID) {
|
||||
Container.start({id: containerID}, {}, function (cd) {
|
||||
if (cd.message) {
|
||||
$('#createContainerSpinner').hide();
|
||||
Messages.error('Error', {}, cd.message);
|
||||
} else {
|
||||
$('#createContainerSpinner').hide();
|
||||
Messages.send('Container Started', containerID);
|
||||
$state.go('containers', {}, {reload: true});
|
||||
}
|
||||
}, function (e) {
|
||||
$('#createContainerSpinner').hide();
|
||||
Messages.error("Failure", e, 'Unable to start container');
|
||||
});
|
||||
}
|
||||
|
||||
function createContainer(config) {
|
||||
Container.create(config, function (d) {
|
||||
if (d.message) {
|
||||
$('#createContainerSpinner').hide();
|
||||
Messages.error('Error', {}, d.message);
|
||||
} else {
|
||||
Container.start({id: d.Id}, {}, function (cd) {
|
||||
if (cd.message) {
|
||||
if ($scope.formValues.Ownership === 'private') {
|
||||
ResourceControlService.setContainerResourceControl(Authentication.getUserDetails().ID, d.Id)
|
||||
.then(function success() {
|
||||
startContainer(d.Id);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
$('#createContainerSpinner').hide();
|
||||
Messages.error('Error', {}, cd.message);
|
||||
} else {
|
||||
$('#createContainerSpinner').hide();
|
||||
Messages.send('Container Started', d.Id);
|
||||
$state.go('containers', {}, {reload: true});
|
||||
}
|
||||
}, function (e) {
|
||||
$('#createContainerSpinner').hide();
|
||||
Messages.error("Failure", e, 'Unable to start container');
|
||||
});
|
||||
Messages.error("Failure", err, 'Unable to apply resource control on container');
|
||||
});
|
||||
} else {
|
||||
startContainer(d.Id);
|
||||
}
|
||||
}
|
||||
}, function (e) {
|
||||
$('#createContainerSpinner').hide();
|
||||
|
@ -143,7 +160,6 @@ function ($scope, $state, $stateParams, $filter, Config, Info, Container, Contai
|
|||
});
|
||||
}
|
||||
|
||||
// TODO: centralize, already present in templatesController
|
||||
function pullImageAndCreateContainer(config) {
|
||||
Image.create($scope.imageConfig, function (data) {
|
||||
createContainer(config);
|
||||
|
@ -242,7 +258,7 @@ function ($scope, $state, $stateParams, $filter, Config, Info, Container, Contai
|
|||
networkMode += ':' + containerName;
|
||||
}
|
||||
config.HostConfig.NetworkMode = networkMode;
|
||||
|
||||
|
||||
$scope.formValues.ExtraHosts.forEach(function (v) {
|
||||
if (v.value) {
|
||||
config.HostConfig.ExtraHosts.push(v.value);
|
||||
|
|
|
@ -100,6 +100,26 @@
|
|||
<!-- !port-mapping-input-list -->
|
||||
</div>
|
||||
<!-- !port-mapping -->
|
||||
<!-- ownership -->
|
||||
<div class="form-group" ng-if="applicationState.application.authentication">
|
||||
<div class="col-sm-12">
|
||||
<label for="ownership" class="control-label text-left">
|
||||
Ownership
|
||||
<portainer-tooltip position="bottom" message="When setting the ownership value to private, only you and the administrators will be able to see and manage this object. When choosing public, everybody will be able to access it."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="btn-group btn-group-sm" style="margin-left: 20px;">
|
||||
<label class="btn btn-default" ng-model="formValues.Ownership" uib-btn-radio="'private'">
|
||||
<i class="fa fa-eye-slash" aria-hidden="true"></i>
|
||||
Private
|
||||
</label>
|
||||
<label class="btn btn-default" ng-model="formValues.Ownership" uib-btn-radio="'public'">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
Public
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !ownership -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
|
@ -333,7 +353,7 @@
|
|||
</div>
|
||||
<!-- !extra-hosts-input-list -->
|
||||
</div>
|
||||
<!-- !extra-hosts-variables -->
|
||||
<!-- !extra-hosts-variables -->
|
||||
</form>
|
||||
</div>
|
||||
<!-- !tab-network -->
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
// @@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', ['$scope', '$state', 'Service', 'Volume', 'Network', 'ImageHelper', 'Messages',
|
||||
function ($scope, $state, Service, Volume, Network, ImageHelper, Messages) {
|
||||
.controller('CreateServiceController', ['$scope', '$state', 'Service', 'Volume', 'Network', 'ImageHelper', 'Authentication', 'ResourceControlService', 'Messages',
|
||||
function ($scope, $state, Service, Volume, Network, ImageHelper, Authentication, ResourceControlService, Messages) {
|
||||
|
||||
$scope.formValues = {
|
||||
Ownership: $scope.applicationState.application.authentication ? 'private' : '',
|
||||
Name: '',
|
||||
Image: '',
|
||||
Registry: '',
|
||||
|
@ -205,9 +208,22 @@ function ($scope, $state, Service, Volume, Network, ImageHelper, Messages) {
|
|||
|
||||
function createNewService(config) {
|
||||
Service.create(config, function (d) {
|
||||
$('#createServiceSpinner').hide();
|
||||
Messages.send('Service created', d.ID);
|
||||
$state.go('services', {}, {reload: true});
|
||||
if ($scope.formValues.Ownership === 'private') {
|
||||
ResourceControlService.setServiceResourceControl(Authentication.getUserDetails().ID, d.ID)
|
||||
.then(function success() {
|
||||
$('#createServiceSpinner').hide();
|
||||
Messages.send('Service created', d.ID);
|
||||
$state.go('services', {}, {reload: true});
|
||||
})
|
||||
.catch(function error(err) {
|
||||
$('#createContainerSpinner').hide();
|
||||
Messages.error("Failure", err, 'Unable to apply resource control on service');
|
||||
});
|
||||
} else {
|
||||
$('#createServiceSpinner').hide();
|
||||
Messages.send('Service created', d.ID);
|
||||
$state.go('services', {}, {reload: true});
|
||||
}
|
||||
}, function (e) {
|
||||
$('#createServiceSpinner').hide();
|
||||
Messages.error("Failure", e, 'Unable to create service');
|
||||
|
|
|
@ -87,6 +87,26 @@
|
|||
<!-- !port-mapping-input-list -->
|
||||
</div>
|
||||
<!-- !port-mapping -->
|
||||
<!-- ownership -->
|
||||
<div class="form-group" ng-if="applicationState.application.authentication">
|
||||
<div class="col-sm-12">
|
||||
<label for="ownership" class="control-label text-left">
|
||||
Ownership
|
||||
<portainer-tooltip position="bottom" message="When setting the ownership value to private, only you and the administrators will be able to see and manage this object. When choosing public, everybody will be able to access it."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="btn-group btn-group-sm" style="margin-left: 20px;">
|
||||
<label class="btn btn-default" ng-model="formValues.Ownership" uib-btn-radio="'private'">
|
||||
<i class="fa fa-eye-slash" aria-hidden="true"></i>
|
||||
Private
|
||||
</label>
|
||||
<label class="btn btn-default" ng-model="formValues.Ownership" uib-btn-radio="'public'">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
Public
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !ownership -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
angular.module('createVolume', [])
|
||||
.controller('CreateVolumeController', ['$scope', '$state', 'Volume', 'Messages',
|
||||
function ($scope, $state, Volume, Messages) {
|
||||
.controller('CreateVolumeController', ['$scope', '$state', 'Volume', 'ResourceControlService', 'Authentication', 'Messages',
|
||||
function ($scope, $state, Volume, ResourceControlService, Authentication, Messages) {
|
||||
|
||||
$scope.formValues = {
|
||||
Ownership: $scope.applicationState.application.authentication ? 'private' : '',
|
||||
DriverOptions: []
|
||||
};
|
||||
|
||||
|
@ -25,9 +26,22 @@ function ($scope, $state, Volume, Messages) {
|
|||
$('#createVolumeSpinner').hide();
|
||||
Messages.error('Unable to create volume', {}, d.message);
|
||||
} else {
|
||||
Messages.send("Volume created", d.Name);
|
||||
$('#createVolumeSpinner').hide();
|
||||
$state.go('volumes', {}, {reload: true});
|
||||
if ($scope.formValues.Ownership === 'private') {
|
||||
ResourceControlService.setVolumeResourceControl(Authentication.getUserDetails().ID, d.Name)
|
||||
.then(function success() {
|
||||
Messages.send("Volume created", d.Name);
|
||||
$('#createVolumeSpinner').hide();
|
||||
$state.go('volumes', {}, {reload: true});
|
||||
})
|
||||
.catch(function error(err) {
|
||||
$('#createVolumeSpinner').hide();
|
||||
Messages.error("Failure", err, 'Unable to apply resource control on volume');
|
||||
});
|
||||
} else {
|
||||
Messages.send("Volume created", d.Name);
|
||||
$('#createVolumeSpinner').hide();
|
||||
$state.go('volumes', {}, {reload: true});
|
||||
}
|
||||
}
|
||||
}, function (e) {
|
||||
$('#createVolumeSpinner').hide();
|
||||
|
|
|
@ -55,6 +55,26 @@
|
|||
<!-- !driver-options-input-list -->
|
||||
</div>
|
||||
<!-- !driver-options -->
|
||||
<!-- ownership -->
|
||||
<div class="form-group" ng-if="applicationState.application.authentication">
|
||||
<div class="col-sm-12">
|
||||
<label for="ownership" class="control-label text-left">
|
||||
Ownership
|
||||
<portainer-tooltip position="bottom" message="When setting the ownership value to private, only you and the administrators will be able to see and manage this object. When choosing public, everybody will be able to access it."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="btn-group btn-group-sm" style="margin-left: 20px;">
|
||||
<label class="btn btn-default" ng-model="formValues.Ownership" uib-btn-radio="'private'">
|
||||
<i class="fa fa-eye-slash" aria-hidden="true"></i>
|
||||
Private
|
||||
</label>
|
||||
<label class="btn btn-default" ng-model="formValues.Ownership" uib-btn-radio="'public'">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
Public
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !ownership -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
|
|
|
@ -18,14 +18,18 @@ function ($scope, $state, $stateParams, $filter, EndpointService, Messages) {
|
|||
|
||||
$scope.updateEndpoint = function() {
|
||||
var ID = $scope.endpoint.Id;
|
||||
var name = $scope.endpoint.Name;
|
||||
var URL = $scope.endpoint.URL;
|
||||
var TLS = $scope.endpoint.TLS;
|
||||
var TLSCACert = $scope.formValues.TLSCACert !== $scope.endpoint.TLSCACert ? $scope.formValues.TLSCACert : null;
|
||||
var TLSCert = $scope.formValues.TLSCert !== $scope.endpoint.TLSCert ? $scope.formValues.TLSCert : null;
|
||||
var TLSKey = $scope.formValues.TLSKey !== $scope.endpoint.TLSKey ? $scope.formValues.TLSKey : null;
|
||||
var type = $scope.endpointType;
|
||||
EndpointService.updateEndpoint(ID, name, URL, TLS, TLSCACert, TLSCert, TLSKey, type).then(function success(data) {
|
||||
var endpointParams = {
|
||||
name: $scope.endpoint.Name,
|
||||
URL: $scope.endpoint.URL,
|
||||
TLS: $scope.endpoint.TLS,
|
||||
TLSCACert: $scope.formValues.TLSCACert !== $scope.endpoint.TLSCACert ? $scope.formValues.TLSCACert : null,
|
||||
TLSCert: $scope.formValues.TLSCert !== $scope.endpoint.TLSCert ? $scope.formValues.TLSCert : null,
|
||||
TLSKey: $scope.formValues.TLSKey !== $scope.endpoint.TLSKey ? $scope.formValues.TLSKey : null,
|
||||
type: $scope.endpointType
|
||||
};
|
||||
|
||||
EndpointService.updateEndpoint(ID, endpointParams)
|
||||
.then(function success(data) {
|
||||
Messages.send("Endpoint updated", $scope.endpoint.Name);
|
||||
$state.go('endpoints');
|
||||
}, function error(err) {
|
||||
|
|
177
app/components/endpointAccess/endpointAccess.html
Normal file
177
app/components/endpointAccess/endpointAccess.html
Normal file
|
@ -0,0 +1,177 @@
|
|||
<rd-header>
|
||||
<rd-header-title title="Endpoint access">
|
||||
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
|
||||
</rd-header-title>
|
||||
<rd-header-content>
|
||||
<a ui-sref="endpoints">Endpoints</a> > <a ui-sref="endpoint({id: endpoint.Id})">{{ endpoint.Name }}</a> > Access management
|
||||
</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row" ng-if="endpoint">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-plug" title="Endpoint"></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>
|
||||
{{ endpoint.Name }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>
|
||||
{{ endpoint.URL | stripprotocol }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<span class="small text-muted">
|
||||
You can select which user can access this endpoint by moving them to the authorized users table. Simply click
|
||||
on a user entry to move it from one table to the other.
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-if="endpoint">
|
||||
<div class="col-sm-6">
|
||||
<rd-widget>
|
||||
<rd-widget-header classes="col-sm-12 col-md-6 nopadding" icon="fa-users" title="Users">
|
||||
<div class="pull-md-right pull-lg-right">
|
||||
Items per page:
|
||||
<select ng-model="state.pagination_count_users" ng-change="changePaginationCountUsers()">
|
||||
<option value="0">All</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</div>
|
||||
</rd-widget-header>
|
||||
<rd-widget-taskbar classes="col-sm-12 nopadding">
|
||||
<div class="col-sm-12 col-md-6 nopadding">
|
||||
<button class="btn btn-primary btn-sm" ng-click="authorizeAllUsers()" ng-disabled="users.length === 0 || filteredUsers.length === 0"><i class="fa fa-user-plus space-right" aria-hidden="true"></i>Authorize all users</button>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6 nopadding">
|
||||
<input type="text" id="filter" ng-model="state.filterUsers" placeholder="Filter..." class="form-control input-sm" />
|
||||
</div>
|
||||
</rd-widget-taskbar>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a ui-sref="endpoint.access({id: endpoint.Id})" ng-click="orderUsers('Username')">
|
||||
Name
|
||||
<span ng-show="sortTypeUsers == 'Username' && !sortReverseUsers" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortTypeUsers == 'Username' && sortReverseUsers" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ui-sref="endpoint.access({id: endpoint.Id})" ng-click="orderUsers('Role')">
|
||||
Role
|
||||
<span ng-show="sortTypeUsers == 'Role' && !sortReverseUsers" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortTypeUsers == 'Role' && sortReverseUsers" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-click="authorizeUser(user)" class="interactive" dir-paginate="user in (state.filteredUsers = (users | filter:state.filterUsers | orderBy:sortTypeUsers:sortReverseUsers | itemsPerPage: state.pagination_count_users))">
|
||||
<td>{{ user.Username }}</td>
|
||||
<td>
|
||||
{{ user.RoleName }}
|
||||
<i class="fa" ng-class="user.RoleId === 1 ? 'fa-user-circle-o' : 'fa-user'" aria-hidden="true" style="margin-left: 2px;"></i>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!users">
|
||||
<td colspan="2" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="users.length === 0 || state.filteredUsers.length === 0">
|
||||
<td colspan="2" class="text-center text-muted">No users.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div ng-if="users" class="pull-left pagination-controls">
|
||||
<dir-pagination-controls></dir-pagination-controls>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<rd-widget>
|
||||
<rd-widget-header classes="col-sm-12 col-md-6 nopadding" icon="fa-users" title="Authorized users">
|
||||
<div class="pull-md-right pull-lg-right">
|
||||
Items per page:
|
||||
<select ng-model="state.pagination_count_authorizedUsers" ng-change="changePaginationCountAuthorizedUsers()">
|
||||
<option value="0">All</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</div>
|
||||
</rd-widget-header>
|
||||
<rd-widget-taskbar classes="col-sm-12 nopadding">
|
||||
<div class="col-sm-12 col-md-6 nopadding">
|
||||
<button class="btn btn-primary btn-sm" ng-click="unauthorizeAllUsers()" ng-disabled="authorizedUsers.length === 0 || filteredAuthorizedUsers.length === 0"><i class="fa fa-user-times space-right" aria-hidden="true"></i>Deny all users</button>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6 nopadding">
|
||||
<input type="text" id="filter" ng-model="state.filterAuthorizedUsers" placeholder="Filter..." class="form-control input-sm" />
|
||||
</div>
|
||||
</rd-widget-taskbar>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a ui-sref="endpoint.access({id: endpoint.Id})" ng-click="orderAuthorizedUsers('Username')">
|
||||
Name
|
||||
<span ng-show="sortTypeAuthorizedUsers == 'Username' && !sortReverseAuthorizedUsers" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortTypeAuthorizedUsers == 'Username' && sortReverseAuthorizedUsers" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ui-sref="endpoint.access({id: endpoint.Id})" ng-click="orderAuthorizedUsers('Role')">
|
||||
Role
|
||||
<span ng-show="sortTypeAuthorizedUsers == 'Role' && !sortReverseAuthorizedUsers" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortTypeAuthorizedUsers == 'Role' && sortReverseAuthorizedUsers" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-click="unauthorizeUser(user)" class="interactive" dir-paginate="user in (state.filteredAuthorizedUsers = (authorizedUsers | filter:state.filterAuthorizedUsers | orderBy:sortTypeAuthorizedUsers:sortReverseAuthorizedUsers | itemsPerPage: state.pagination_count_authorizedUsers))">
|
||||
<td>{{ user.Username }}</td>
|
||||
<td>
|
||||
{{ user.RoleName }}
|
||||
<i class="fa" ng-class="user.RoleId === 1 ? 'fa-user-circle-o' : 'fa-user'" aria-hidden="true" style="margin-left: 2px;"></i>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!authorizedUsers">
|
||||
<td colspan="2" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="authorizedUsers.length === 0 || state.filteredAuthorizedUsers.length === 0">
|
||||
<td colspan="2" class="text-center text-muted">No authorized users.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div ng-if="authorizedUsers" class="pull-left pagination-controls">
|
||||
<dir-pagination-controls></dir-pagination-controls>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
148
app/components/endpointAccess/endpointAccessController.js
Normal file
148
app/components/endpointAccess/endpointAccessController.js
Normal file
|
@ -0,0 +1,148 @@
|
|||
angular.module('endpointAccess', [])
|
||||
.controller('EndpointAccessController', ['$q', '$scope', '$state', '$stateParams', '$filter', 'EndpointService', 'UserService', 'Pagination', 'Messages',
|
||||
function ($q, $scope, $state, $stateParams, $filter, EndpointService, UserService, Pagination, Messages) {
|
||||
|
||||
$scope.state = {
|
||||
pagination_count_users: Pagination.getPaginationCount('endpoint_access_users'),
|
||||
pagination_count_authorizedUsers: Pagination.getPaginationCount('endpoint_access_authorizedUsers')
|
||||
};
|
||||
|
||||
$scope.sortTypeUsers = 'Username';
|
||||
$scope.sortReverseUsers = true;
|
||||
|
||||
$scope.orderUsers = function(sortType) {
|
||||
$scope.sortReverseUsers = ($scope.sortTypeUsers === sortType) ? !$scope.sortReverseUsers : false;
|
||||
$scope.sortTypeUsers = sortType;
|
||||
};
|
||||
|
||||
$scope.changePaginationCountUsers = function() {
|
||||
Pagination.setPaginationCount('endpoint_access_users', $scope.state.pagination_count_users);
|
||||
};
|
||||
|
||||
$scope.sortTypeAuthorizedUsers = 'Username';
|
||||
$scope.sortReverseAuthorizedUsers = true;
|
||||
|
||||
$scope.orderAuthorizedUsers = function(sortType) {
|
||||
$scope.sortReverseAuthorizedUsers = ($scope.sortTypeAuthorizedUsers === sortType) ? !$scope.sortReverseAuthorizedUsers : false;
|
||||
$scope.sortTypeAuthorizedUsers = sortType;
|
||||
};
|
||||
|
||||
$scope.changePaginationCountAuthorizedUsers = function() {
|
||||
Pagination.setPaginationCount('endpoint_access_authorizedUsers', $scope.state.pagination_count_authorizedUsers);
|
||||
};
|
||||
|
||||
$scope.authorizeAllUsers = function() {
|
||||
var authorizedUserIDs = [];
|
||||
angular.forEach($scope.authorizedUsers, function (user) {
|
||||
authorizedUserIDs.push(user.Id);
|
||||
});
|
||||
angular.forEach($scope.users, function (user) {
|
||||
authorizedUserIDs.push(user.Id);
|
||||
});
|
||||
EndpointService.updateAuthorizedUsers($stateParams.id, authorizedUserIDs)
|
||||
.then(function success(data) {
|
||||
$scope.authorizedUsers = $scope.authorizedUsers.concat($scope.users);
|
||||
$scope.users = [];
|
||||
Messages.send('Access granted for all users');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to update endpoint permissions");
|
||||
});
|
||||
};
|
||||
|
||||
$scope.unauthorizeAllUsers = function() {
|
||||
EndpointService.updateAuthorizedUsers($stateParams.id, [])
|
||||
.then(function success(data) {
|
||||
$scope.users = $scope.users.concat($scope.authorizedUsers);
|
||||
$scope.authorizedUsers = [];
|
||||
Messages.send('Access removed for all users');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to update endpoint permissions");
|
||||
});
|
||||
};
|
||||
|
||||
$scope.authorizeUser = function(user) {
|
||||
var authorizedUserIDs = [];
|
||||
angular.forEach($scope.authorizedUsers, function (u) {
|
||||
authorizedUserIDs.push(u.Id);
|
||||
});
|
||||
authorizedUserIDs.push(user.Id);
|
||||
EndpointService.updateAuthorizedUsers($stateParams.id, authorizedUserIDs)
|
||||
.then(function success(data) {
|
||||
removeUserFromArray(user.Id, $scope.users);
|
||||
$scope.authorizedUsers.push(user);
|
||||
Messages.send('Access granted for user', user.Username);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to update endpoint permissions");
|
||||
});
|
||||
};
|
||||
|
||||
$scope.unauthorizeUser = function(user) {
|
||||
var authorizedUserIDs = $scope.authorizedUsers.filter(function (u) {
|
||||
if (u.Id !== user.Id) {
|
||||
return u;
|
||||
}
|
||||
}).map(function (u) {
|
||||
return u.Id;
|
||||
});
|
||||
EndpointService.updateAuthorizedUsers($stateParams.id, authorizedUserIDs)
|
||||
.then(function success(data) {
|
||||
removeUserFromArray(user.Id, $scope.authorizedUsers);
|
||||
$scope.users.push(user);
|
||||
Messages.send('Access removed for user', user.Username);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to update endpoint permissions");
|
||||
});
|
||||
};
|
||||
|
||||
function getEndpointAndUsers(endpointID) {
|
||||
$('#loadingViewSpinner').show();
|
||||
$q.all({
|
||||
endpoint: EndpointService.endpoint($stateParams.id),
|
||||
users: UserService.users(),
|
||||
})
|
||||
.then(function success(data) {
|
||||
$scope.endpoint = data.endpoint;
|
||||
$scope.users = data.users.filter(function (user) {
|
||||
if (user.Role !== 1) {
|
||||
return user;
|
||||
}
|
||||
}).map(function (user) {
|
||||
return new UserViewModel(user);
|
||||
});
|
||||
$scope.authorizedUsers = [];
|
||||
angular.forEach($scope.endpoint.AuthorizedUsers, function(userID) {
|
||||
for (var i = 0, l = $scope.users.length; i < l; i++) {
|
||||
if ($scope.users[i].Id === userID) {
|
||||
$scope.authorizedUsers.push($scope.users[i]);
|
||||
$scope.users.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(function error(err) {
|
||||
$scope.templates = [];
|
||||
$scope.users = [];
|
||||
$scope.authorizedUsers = [];
|
||||
Messages.error("Failure", err, "Unable to retrieve endpoint details");
|
||||
})
|
||||
.finally(function final(){
|
||||
$('#loadingViewSpinner').hide();
|
||||
});
|
||||
}
|
||||
|
||||
function removeUserFromArray(id, users) {
|
||||
for (var i = 0, l = users.length; i < l; i++) {
|
||||
if (users[i].Id === id) {
|
||||
users.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getEndpointAndUsers($stateParams.id);
|
||||
}]);
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('endpointInit', [])
|
||||
.controller('EndpointInitController', ['$scope', '$state', 'EndpointService', 'StateManager', 'Messages',
|
||||
function ($scope, $state, EndpointService, StateManager, Messages) {
|
||||
.controller('EndpointInitController', ['$scope', '$state', 'EndpointService', 'StateManager', 'EndpointProvider', 'Messages',
|
||||
function ($scope, $state, EndpointService, StateManager, EndpointProvider, Messages) {
|
||||
$scope.state = {
|
||||
error: '',
|
||||
uploadInProgress: false
|
||||
|
@ -29,20 +29,28 @@ function ($scope, $state, EndpointService, StateManager, Messages) {
|
|||
var name = "local";
|
||||
var URL = "unix:///var/run/docker.sock";
|
||||
var TLS = false;
|
||||
EndpointService.createLocalEndpoint(name, URL, TLS, true).then(function success(data) {
|
||||
StateManager.updateEndpointState(false)
|
||||
.then(function success() {
|
||||
|
||||
EndpointService.createLocalEndpoint(name, URL, TLS, true)
|
||||
.then(
|
||||
function success(data) {
|
||||
var endpointID = data.Id;
|
||||
EndpointProvider.setEndpointID(endpointID);
|
||||
StateManager.updateEndpointState(false).then(
|
||||
function success() {
|
||||
$state.go('dashboard');
|
||||
}, function error(err) {
|
||||
EndpointService.deleteEndpoint(0)
|
||||
},
|
||||
function error(err) {
|
||||
EndpointService.deleteEndpoint(endpointID)
|
||||
.then(function success() {
|
||||
$('#initEndpointSpinner').hide();
|
||||
$scope.state.error = 'Unable to connect to the Docker endpoint';
|
||||
});
|
||||
});
|
||||
}, function error(err) {
|
||||
$('#initEndpointSpinner').hide();
|
||||
},
|
||||
function error() {
|
||||
$scope.state.error = 'Unable to create endpoint';
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#initEndpointSpinner').hide();
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -57,11 +65,13 @@ function ($scope, $state, EndpointService, StateManager, Messages) {
|
|||
var TLSKeyFile = $scope.formValues.TLSKey;
|
||||
EndpointService.createRemoteEndpoint(name, URL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile, TLS ? false : true)
|
||||
.then(function success(data) {
|
||||
var endpointID = data.Id;
|
||||
EndpointProvider.setEndpointID(endpointID);
|
||||
StateManager.updateEndpointState(false)
|
||||
.then(function success() {
|
||||
$state.go('dashboard');
|
||||
}, function error(err) {
|
||||
EndpointService.deleteEndpoint(0)
|
||||
EndpointService.deleteEndpoint(endpointID)
|
||||
.then(function success() {
|
||||
$('#initEndpointSpinner').hide();
|
||||
$scope.state.error = 'Unable to connect to the Docker endpoint';
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<rd-widget-header icon="fa-exclamation-triangle" title="Endpoint management is not available">
|
||||
</rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<span class="small text-muted">Portainer has been started using the <code>--external-endpoints</code> flag. Endpoint management via the UI is disabled.</span>
|
||||
<span class="small text-muted">Portainer has been started using the <code>--external-endpoints</code> flag. Endpoint management via the UI is disabled. You can still manage endpoint access.</span>
|
||||
</rd-wigdet-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
|
@ -152,14 +152,7 @@
|
|||
<span ng-show="sortType == 'URL' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ui-sref="endpoints" ng-click="order('TLS')">
|
||||
TLS
|
||||
<span ng-show="sortType == 'TLS' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortType == 'TLS' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th ng-if="applicationState.application.endpointManagement"></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -167,13 +160,17 @@
|
|||
<td ng-if="applicationState.application.endpointManagement"><input type="checkbox" ng-model="endpoint.Checked" ng-change="selectItem(endpoint)" /></td>
|
||||
<td><i class="fa fa-star" aria-hidden="true" ng-if="endpoint.Id === activeEndpoint.Id"></i> {{ endpoint.Name }}</td>
|
||||
<td>{{ endpoint.URL | stripprotocol }}</td>
|
||||
<td><i class="fa fa-shield" aria-hidden="true" ng-if="endpoint.TLS"></i></td>
|
||||
<td ng-if="applicationState.application.endpointManagement">
|
||||
<span ng-if="endpoint.Id !== activeEndpoint.Id">
|
||||
<a ui-sref="endpoint({id: endpoint.Id})"><i class="fa fa-pencil-square-o" aria-hidden="true"></i> Edit</a>
|
||||
<td>
|
||||
<span ng-if="applicationState.application.endpointManagement">
|
||||
<span ng-if="endpoint.Id !== activeEndpointID">
|
||||
<a ui-sref="endpoint({id: endpoint.Id})"><i class="fa fa-pencil-square-o" aria-hidden="true"></i> Edit</a>
|
||||
</span>
|
||||
<span class="small text-muted" ng-if="endpoint.Id === activeEndpointID">
|
||||
<i class="fa fa-lock" aria-hidden="true"></i> You cannot edit the active endpoint
|
||||
</span>
|
||||
</span>
|
||||
<span class="small text-muted" ng-if="endpoint.Id === activeEndpoint.Id">
|
||||
<i class="fa fa-lock" aria-hidden="true"></i> You cannot edit the active endpoint
|
||||
<span ng-if="applicationState.application.authentication">
|
||||
<a ui-sref="endpoint.access({id: endpoint.Id})"><i class="fa fa-users" aria-hidden="true" style="margin-left: 7px;"></i> Manage access</a>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('endpoints', [])
|
||||
.controller('EndpointsController', ['$scope', '$state', 'EndpointService', 'Messages', 'Pagination',
|
||||
function ($scope, $state, EndpointService, Messages, Pagination) {
|
||||
.controller('EndpointsController', ['$scope', '$state', 'EndpointService', 'EndpointProvider', 'Messages', 'Pagination',
|
||||
function ($scope, $state, EndpointService, EndpointProvider, Messages, Pagination) {
|
||||
$scope.state = {
|
||||
error: '',
|
||||
uploadInProgress: false,
|
||||
|
@ -84,19 +84,17 @@ function ($scope, $state, EndpointService, Messages, Pagination) {
|
|||
|
||||
function fetchEndpoints() {
|
||||
$('#loadEndpointsSpinner').show();
|
||||
EndpointService.endpoints().then(function success(data) {
|
||||
EndpointService.endpoints()
|
||||
.then(function success(data) {
|
||||
$scope.endpoints = data;
|
||||
EndpointService.getActive().then(function success(data) {
|
||||
$scope.activeEndpoint = data;
|
||||
$('#loadEndpointsSpinner').hide();
|
||||
}, function error(err) {
|
||||
$('#loadEndpointsSpinner').hide();
|
||||
Messages.error("Failure", err, "Unable to retrieve active endpoint");
|
||||
});
|
||||
}, function error(err) {
|
||||
$('#loadEndpointsSpinner').hide();
|
||||
$scope.activeEndpointID = EndpointProvider.endpointID();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to retrieve endpoints");
|
||||
$scope.endpoints = [];
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadEndpointsSpinner').hide();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -60,21 +60,9 @@ function ($scope, $state, Config, Image, ImageHelper, Messages, Pagination, Moda
|
|||
};
|
||||
|
||||
$scope.confirmRemovalAction = function (force) {
|
||||
ModalService.confirm({
|
||||
title: "Are you sure?",
|
||||
message: "Forcing the removal of the image will remove the image even if it has multiple tags or if it is used by stopped containers.",
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Remove the image',
|
||||
},
|
||||
cancel: {
|
||||
label: 'Cancel'
|
||||
}
|
||||
},
|
||||
callback: function (confirmed) {
|
||||
if(!confirmed) { return; }
|
||||
$scope.removeAction(force);
|
||||
}
|
||||
ModalService.confirmImageForceRemoval(function (confirmed) {
|
||||
if(!confirmed) { return; }
|
||||
$scope.removeAction(force);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -58,6 +58,13 @@
|
|||
<span ng-show="sortType == 'Mode' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th ng-if="applicationState.application.authentication">
|
||||
<a ui-sref="containers" ng-click="order('Metadata.ResourceControl.OwnerId')">
|
||||
Ownership
|
||||
<span ng-show="sortType == 'Metadata.ResourceControl.OwnerId' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortType == 'Metadata.ResourceControl.OwnerId' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="service in (state.filteredServices = ( services | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))">
|
||||
|
@ -76,12 +83,33 @@
|
|||
<a class="interactive" ng-click="scaleService(service)"><i class="fa fa-check-square-o"></i></a>
|
||||
</span>
|
||||
</td>
|
||||
<td ng-if="applicationState.application.authentication">
|
||||
<span ng-if="user.role === 1 && service.Metadata.ResourceControl">
|
||||
<i class="fa fa-eye-slash" aria-hidden="true"></i>
|
||||
<span ng-if="service.Metadata.ResourceControl.OwnerId === user.ID">
|
||||
Private
|
||||
</span>
|
||||
<span ng-if="service.Metadata.ResourceControl.OwnerId !== user.ID">
|
||||
Private <span ng-if="service.Owner">(owner: {{ service.Owner }})</span>
|
||||
</span>
|
||||
<a ng-click="switchOwnership(service)" class="interactive"><i class="fa fa-eye" aria-hidden="true" style="margin-left: 7px;"></i> Switch to public</a>
|
||||
</span>
|
||||
<span ng-if="user.role !== 1 && service.Metadata.ResourceControl.OwnerId === user.ID">
|
||||
<i class="fa fa-eye-slash" aria-hidden="true"></i>
|
||||
Private
|
||||
<a ng-click="switchOwnership(service)" class="interactive"><i class="fa fa-eye" aria-hidden="true" style="margin-left: 7px;"></i> Switch to public</a>
|
||||
</span>
|
||||
<span ng-if="!service.Metadata.ResourceControl">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
Public
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!services">
|
||||
<td colspan="4" class="text-center text-muted">Loading...</td>
|
||||
<td colspan="5" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="services.length == 0">
|
||||
<td colspan="4" class="text-center text-muted">No services available.</td>
|
||||
<td colspan="5" class="text-center text-muted">No services available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,12 +1,40 @@
|
|||
angular.module('services', [])
|
||||
.controller('ServicesController', ['$scope', '$stateParams', '$state', 'Service', 'ServiceHelper', 'Messages', 'Pagination',
|
||||
function ($scope, $stateParams, $state, Service, ServiceHelper, Messages, Pagination) {
|
||||
.controller('ServicesController', ['$q', '$scope', '$stateParams', '$state', 'Service', 'ServiceHelper', 'Messages', 'Pagination', 'Authentication', 'UserService', 'ModalService', 'ResourceControlService',
|
||||
function ($q, $scope, $stateParams, $state, Service, ServiceHelper, Messages, Pagination, Authentication, UserService, ModalService, ResourceControlService) {
|
||||
$scope.state = {};
|
||||
$scope.state.selectedItemCount = 0;
|
||||
$scope.state.pagination_count = Pagination.getPaginationCount('services');
|
||||
$scope.sortType = 'Name';
|
||||
$scope.sortReverse = false;
|
||||
|
||||
function removeServiceResourceControl(service) {
|
||||
volumeResourceControlQueries = [];
|
||||
angular.forEach(service.Mounts, function (mount) {
|
||||
if (mount.Type === 'volume') {
|
||||
volumeResourceControlQueries.push(ResourceControlService.removeVolumeResourceControl(service.Metadata.ResourceControl.OwnerId, mount.Source));
|
||||
}
|
||||
});
|
||||
|
||||
$q.all(volumeResourceControlQueries)
|
||||
.then(function success() {
|
||||
return ResourceControlService.removeServiceResourceControl(service.Metadata.ResourceControl.OwnerId, service.Id);
|
||||
})
|
||||
.then(function success() {
|
||||
delete service.Metadata.ResourceControl;
|
||||
Messages.send('Ownership changed to public', service.Id);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to change service ownership");
|
||||
});
|
||||
}
|
||||
|
||||
$scope.switchOwnership = function(volume) {
|
||||
ModalService.confirmServiceOwnershipChange(function (confirmed) {
|
||||
if(!confirmed) { return; }
|
||||
removeServiceResourceControl(volume);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.changePaginationCount = function() {
|
||||
Pagination.setPaginationCount('services', $scope.state.pagination_count);
|
||||
};
|
||||
|
@ -57,9 +85,21 @@ function ($scope, $stateParams, $state, Service, ServiceHelper, Messages, Pagina
|
|||
$('#loadServicesSpinner').hide();
|
||||
Messages.error("Unable to remove service", {}, d[0].message);
|
||||
} else {
|
||||
Messages.send("Service deleted", service.Id);
|
||||
var index = $scope.services.indexOf(service);
|
||||
$scope.services.splice(index, 1);
|
||||
if (service.Metadata && service.Metadata.ResourceControl) {
|
||||
ResourceControlService.removeServiceResourceControl(service.Metadata.ResourceControl.OwnerId, service.Id)
|
||||
.then(function success() {
|
||||
Messages.send("Service deleted", service.Id);
|
||||
var index = $scope.services.indexOf(service);
|
||||
$scope.services.splice(index, 1);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to remove service ownership");
|
||||
});
|
||||
} else {
|
||||
Messages.send("Service deleted", service.Id);
|
||||
var index = $scope.services.indexOf(service);
|
||||
$scope.services.splice(index, 1);
|
||||
}
|
||||
}
|
||||
complete();
|
||||
}, function (e) {
|
||||
|
@ -70,12 +110,42 @@ function ($scope, $stateParams, $state, Service, ServiceHelper, Messages, Pagina
|
|||
});
|
||||
};
|
||||
|
||||
function mapUsersToServices(users) {
|
||||
angular.forEach($scope.services, function (service) {
|
||||
if (service.Metadata) {
|
||||
var serviceRC = service.Metadata.ResourceControl;
|
||||
if (serviceRC && serviceRC.OwnerId != $scope.user.ID) {
|
||||
angular.forEach(users, function (user) {
|
||||
if (serviceRC.OwnerId === user.Id) {
|
||||
service.Owner = user.Username;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fetchServices() {
|
||||
$('#loadServicesSpinner').show();
|
||||
var userDetails = Authentication.getUserDetails();
|
||||
$scope.user = userDetails;
|
||||
|
||||
Service.query({}, function (d) {
|
||||
$scope.services = d.map(function (service) {
|
||||
return new ServiceViewModel(service);
|
||||
});
|
||||
if (userDetails.role === 1) {
|
||||
UserService.users()
|
||||
.then(function success(data) {
|
||||
mapUsersToServices(data);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to retrieve users");
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadServicesSpinner').hide();
|
||||
});
|
||||
}
|
||||
$('#loadServicesSpinner').hide();
|
||||
}, function(e) {
|
||||
$('#loadServicesSpinner').hide();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('settings', [])
|
||||
.controller('SettingsController', ['$scope', '$state', '$sanitize', 'Users', 'Messages',
|
||||
function ($scope, $state, $sanitize, Users, Messages) {
|
||||
.controller('SettingsController', ['$scope', '$state', '$sanitize', 'Authentication', 'UserService', 'Messages',
|
||||
function ($scope, $state, $sanitize, Authentication, UserService, Messages) {
|
||||
$scope.formValues = {
|
||||
currentPassword: '',
|
||||
newPassword: '',
|
||||
|
@ -9,22 +9,21 @@ function ($scope, $state, $sanitize, Users, Messages) {
|
|||
|
||||
$scope.updatePassword = function() {
|
||||
$scope.invalidPassword = false;
|
||||
$scope.error = false;
|
||||
var userID = Authentication.getUserDetails().ID;
|
||||
var currentPassword = $sanitize($scope.formValues.currentPassword);
|
||||
Users.checkPassword({ username: $scope.username, password: currentPassword }, function (d) {
|
||||
if (d.valid) {
|
||||
var newPassword = $sanitize($scope.formValues.newPassword);
|
||||
Users.update({ username: $scope.username, password: newPassword }, function (d) {
|
||||
Messages.send("Success", "Password successfully updated");
|
||||
$state.reload();
|
||||
}, function (e) {
|
||||
Messages.error("Failure", e, "Unable to update password");
|
||||
});
|
||||
} else {
|
||||
var newPassword = $sanitize($scope.formValues.newPassword);
|
||||
|
||||
UserService.updateUserPassword(userID, currentPassword, newPassword)
|
||||
.then(function success() {
|
||||
Messages.send("Success", "Password successfully updated");
|
||||
$state.reload();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
if (err.invalidPassword) {
|
||||
$scope.invalidPassword = true;
|
||||
} else {
|
||||
Messages.error("Failure", err, err.msg);
|
||||
}
|
||||
}, function (e) {
|
||||
Messages.error("Failure", e, "Unable to check password validity");
|
||||
});
|
||||
};
|
||||
}]);
|
||||
|
|
|
@ -50,7 +50,10 @@
|
|||
<li class="sidebar-list" ng-if="applicationState.application.authentication">
|
||||
<a ui-sref="settings" ui-sref-active="active">Password <span class="menu-icon fa fa-lock"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<li class="sidebar-list" ng-if="applicationState.application.authentication && userRole === 1">
|
||||
<a ui-sref="users" ui-sref-active="active">Users <span class="menu-icon fa fa-user"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="!applicationState.application.authentication || userRole === 1">
|
||||
<a ui-sref="endpoints" ui-sref-active="active">Endpoints <span class="menu-icon fa fa-plug"></span></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -1,40 +1,41 @@
|
|||
angular.module('sidebar', [])
|
||||
.controller('SidebarController', ['$scope', '$state', 'Settings', 'Config', 'EndpointService', 'StateManager', 'Messages',
|
||||
function ($scope, $state, Settings, Config, EndpointService, StateManager, Messages) {
|
||||
.controller('SidebarController', ['$scope', '$state', 'Settings', 'Config', 'EndpointService', 'StateManager', 'EndpointProvider', 'Messages', 'Authentication',
|
||||
function ($scope, $state, Settings, Config, EndpointService, StateManager, EndpointProvider, Messages, Authentication) {
|
||||
|
||||
Config.$promise.then(function (c) {
|
||||
$scope.logo = c.logo;
|
||||
});
|
||||
|
||||
$scope.uiVersion = Settings.uiVersion;
|
||||
$scope.userRole = Authentication.getUserDetails().role;
|
||||
|
||||
$scope.switchEndpoint = function(endpoint) {
|
||||
EndpointService.setActive(endpoint.Id)
|
||||
.then(function success(data) {
|
||||
var activeEndpointID = EndpointProvider.endpointID();
|
||||
EndpointProvider.setEndpointID(endpoint.Id);
|
||||
StateManager.updateEndpointState(true)
|
||||
.then(function success() {
|
||||
$state.go('dashboard');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to connect to the Docker endpoint");
|
||||
EndpointProvider.setEndpointID(activeEndpointID);
|
||||
StateManager.updateEndpointState(true)
|
||||
.then(function success() {
|
||||
$state.go('dashboard');
|
||||
}, function error(err) {
|
||||
Messages.error("Failure", err, "Unable to connect to the Docker endpoint");
|
||||
});
|
||||
}, function error(err) {
|
||||
Messages.error("Failure", err, "Unable to switch to new endpoint");
|
||||
.then(function success() {});
|
||||
});
|
||||
};
|
||||
|
||||
function fetchEndpoints() {
|
||||
EndpointService.endpoints().then(function success(data) {
|
||||
EndpointService.endpoints()
|
||||
.then(function success(data) {
|
||||
$scope.endpoints = data;
|
||||
EndpointService.getActive().then(function success(data) {
|
||||
angular.forEach($scope.endpoints, function (endpoint) {
|
||||
if (endpoint.Id === data.Id) {
|
||||
$scope.activeEndpoint = endpoint;
|
||||
}
|
||||
});
|
||||
}, function error(err) {
|
||||
Messages.error("Failure", err, "Unable to retrieve active endpoint");
|
||||
var activeEndpointID = EndpointProvider.endpointID();
|
||||
angular.forEach($scope.endpoints, function (endpoint) {
|
||||
if (endpoint.Id === activeEndpointID) {
|
||||
$scope.activeEndpoint = endpoint;
|
||||
}
|
||||
});
|
||||
}, function error(err) {
|
||||
})
|
||||
.catch(function error(err) {
|
||||
$scope.endpoints = [];
|
||||
});
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- !name-and-network-inputs -->
|
||||
<!-- env -->
|
||||
<div ng-repeat="var in state.selectedTemplate.Env" ng-if="!var.set" class="form-group">
|
||||
<label for="field_{{ $index }}" class="col-sm-2 control-label text-left">{{ var.label }}</label>
|
||||
<div class="col-sm-10">
|
||||
|
@ -51,6 +52,21 @@
|
|||
<input ng-if="!var.type || !var.type === 'container'" type="text" class="form-control" ng-model="var.value" id="field_{{ $index }}">
|
||||
</div>
|
||||
</div>
|
||||
<!-- !env -->
|
||||
<!-- ownership -->
|
||||
<div class="form-group" ng-if="applicationState.application.authentication">
|
||||
<div class="col-sm-12">
|
||||
<label for="ownership" class="control-label text-left">
|
||||
Ownership
|
||||
<portainer-tooltip position="bottom" message="When setting the ownership value to private, only you and the administrators will be able to see and manage this object. When choosing public, everybody will be able to access it."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="btn-group btn-group-sm" style="margin-left: 20px;">
|
||||
<label class="btn btn-default" ng-model="formValues.Ownership" uib-btn-radio="'private'">Private</label>
|
||||
<label class="btn btn-default" ng-model="formValues.Ownership" uib-btn-radio="'public'">Public</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !ownership -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<a class="small interactive" ng-if="!state.showAdvancedOptions" ng-click="state.showAdvancedOptions = true;">
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
angular.module('templates', [])
|
||||
.controller('TemplatesController', ['$scope', '$q', '$state', '$anchorScroll', 'Config', 'ContainerService', 'ContainerHelper', 'ImageService', 'NetworkService', 'TemplateService', 'TemplateHelper', 'VolumeService', 'Messages', 'Pagination',
|
||||
function ($scope, $q, $state, $anchorScroll, Config, ContainerService, ContainerHelper, ImageService, NetworkService, TemplateService, TemplateHelper, VolumeService, Messages, Pagination) {
|
||||
.controller('TemplatesController', ['$scope', '$q', '$state', '$anchorScroll', 'Config', 'ContainerService', 'ContainerHelper', 'ImageService', 'NetworkService', 'TemplateService', 'TemplateHelper', 'VolumeService', 'Messages', 'Pagination', 'ResourceControlService', 'Authentication',
|
||||
function ($scope, $q, $state, $anchorScroll, Config, ContainerService, ContainerHelper, ImageService, NetworkService, TemplateService, TemplateHelper, VolumeService, Messages, Pagination, ResourceControlService, Authentication) {
|
||||
$scope.state = {
|
||||
selectedTemplate: null,
|
||||
showAdvancedOptions: false,
|
||||
pagination_count: Pagination.getPaginationCount('templates')
|
||||
};
|
||||
$scope.formValues = {
|
||||
Ownership: $scope.applicationState.application.authentication ? 'private' : '',
|
||||
network: "",
|
||||
name: "",
|
||||
};
|
||||
|
@ -36,18 +37,30 @@ function ($scope, $q, $state, $anchorScroll, Config, ContainerService, Container
|
|||
var template = $scope.state.selectedTemplate;
|
||||
var templateConfiguration = createTemplateConfiguration(template);
|
||||
var generatedVolumeCount = TemplateHelper.determineRequiredGeneratedVolumeCount(template.Volumes);
|
||||
|
||||
VolumeService.createXAutoGeneratedLocalVolumes(generatedVolumeCount)
|
||||
.then(function success(data) {
|
||||
var volumeResourceControlQueries = [];
|
||||
if ($scope.formValues.Ownership === 'private') {
|
||||
angular.forEach(data, function (volume) {
|
||||
volumeResourceControlQueries.push(ResourceControlService.setVolumeResourceControl(Authentication.getUserDetails().ID, volume.Name));
|
||||
});
|
||||
}
|
||||
TemplateService.updateContainerConfigurationWithVolumes(templateConfiguration.container, template, data);
|
||||
return ImageService.pullImage(templateConfiguration.image);
|
||||
return $q.all(volumeResourceControlQueries).then(ImageService.pullImage(templateConfiguration.image));
|
||||
})
|
||||
.then(function success(data) {
|
||||
return ContainerService.createAndStartContainer(templateConfiguration.container);
|
||||
})
|
||||
.then(function success(data) {
|
||||
Messages.send('Container Started', data.Id);
|
||||
$state.go('containers', {}, {reload: true});
|
||||
if ($scope.formValues.Ownership === 'private') {
|
||||
ResourceControlService.setContainerResourceControl(Authentication.getUserDetails().ID, data.Id)
|
||||
.then(function success(data) {
|
||||
$state.go('containers', {}, {reload: true});
|
||||
});
|
||||
} else {
|
||||
$state.go('containers', {}, {reload: true});
|
||||
}
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error('Failure', err, err.msg);
|
||||
|
|
88
app/components/user/user.html
Normal file
88
app/components/user/user.html
Normal file
|
@ -0,0 +1,88 @@
|
|||
<rd-header>
|
||||
<rd-header-title title="User details">
|
||||
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
|
||||
</rd-header-title>
|
||||
<rd-header-content>
|
||||
<a ui-sref="users">Users</a> > <a ui-sref="user({id: user.Id})">{{ user.Username }}</a>
|
||||
</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-user" title="User details"></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>
|
||||
{{ user.Username }}
|
||||
<button class="btn btn-xs btn-danger" ng-click="deleteUser()"><i class="fa fa-trash space-right" aria-hidden="true"></i>Delete this user</button>
|
||||
</td>
|
||||
</tr>
|
||||
<td>Permissions</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<label class="btn btn-default" ng-model="user.RoleId" uib-btn-radio="2" ng-change="updatePermissions()">
|
||||
<i class="fa fa-user" aria-hidden="true"></i>
|
||||
User
|
||||
</label>
|
||||
<label class="btn btn-default" ng-model="user.RoleId" uib-btn-radio="1" ng-change="updatePermissions()">
|
||||
<i class="fa fa-user-circle-o" aria-hidden="true"></i>
|
||||
Administrator
|
||||
</label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-lock" title="Change user password"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" style="margin-top: 15px;">
|
||||
<!-- new-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="new_password" class="col-sm-2 control-label text-left">New password</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input type="password" class="form-control" ng-model="formValues.newPassword" id="new_password">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !new-password-input -->
|
||||
<!-- confirm-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="confirm_password" class="col-sm-2 control-label text-left">Confirm password</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input type="password" class="form-control" ng-model="formValues.confirmPassword" id="confirm_password">
|
||||
<span class="input-group-addon"><i ng-class="{true: 'fa fa-check green-icon', false: 'fa fa-times red-icon'}[formValues.newPassword !== '' && formValues.newPassword === formValues.confirmPassword]" aria-hidden="true"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !confirm-password-input -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-2">
|
||||
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="formValues.newPassword === '' || formValues.newPassword !== formValues.confirmPassword" ng-click="updatePassword()">Update password</button>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<p class="pull-left text-danger" ng-if="state.updatePasswordError" style="margin: 5px;">
|
||||
<i class="fa fa-exclamation-circle" aria-hidden="true"></i> {{ state.updatePasswordError }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
85
app/components/user/userController.js
Normal file
85
app/components/user/userController.js
Normal file
|
@ -0,0 +1,85 @@
|
|||
angular.module('user', [])
|
||||
.controller('UserController', ['$scope', '$state', '$stateParams', 'UserService', 'ModalService', 'Messages',
|
||||
function ($scope, $state, $stateParams, UserService, ModalService, Messages) {
|
||||
|
||||
$scope.state = {
|
||||
updatePasswordError: '',
|
||||
};
|
||||
|
||||
$scope.formValues = {
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
};
|
||||
|
||||
$scope.deleteUser = function() {
|
||||
ModalService.confirmDeletion(
|
||||
'Do you want to delete this user? This user will not be able to login into Portainer anymore.',
|
||||
function onConfirm(confirmed) {
|
||||
if(!confirmed) { return; }
|
||||
deleteUser();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
$scope.updatePermissions = function() {
|
||||
$('#loadingViewSpinner').show();
|
||||
UserService.updateUser($scope.user.Id, undefined, $scope.user.RoleId)
|
||||
.then(function success(data) {
|
||||
var newRole = $scope.user.RoleId === 1 ? 'administrator' : 'user';
|
||||
Messages.send('Permissions successfully updated', $scope.user.Username + ' is now ' + newRole);
|
||||
$state.reload();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, 'Unable to update user permissions');
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadingViewSpinner').hide();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.updatePassword = function() {
|
||||
$('#loadingViewSpinner').show();
|
||||
UserService.updateUser($scope.user.Id, $scope.formValues.newPassword, undefined)
|
||||
.then(function success(data) {
|
||||
Messages.send('Password successfully updated');
|
||||
$state.reload();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
$scope.state.updatePasswordError = 'Unable to update password';
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadingViewSpinner').hide();
|
||||
});
|
||||
};
|
||||
|
||||
function deleteUser() {
|
||||
$('#loadingViewSpinner').show();
|
||||
UserService.deleteUser($scope.user.Id)
|
||||
.then(function success(data) {
|
||||
Messages.send('User successfully deleted', $scope.user.Username);
|
||||
$state.go('users');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, 'Unable to remove user');
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadingViewSpinner').hide();
|
||||
});
|
||||
}
|
||||
|
||||
function getUser() {
|
||||
$('#loadingViewSpinner').show();
|
||||
UserService.user($stateParams.id)
|
||||
.then(function success(data) {
|
||||
$scope.user = new UserViewModel(data);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, 'Unable to retrieve user information');
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadingViewSpinner').hide();
|
||||
});
|
||||
}
|
||||
|
||||
getUser();
|
||||
}]);
|
159
app/components/users/users.html
Normal file
159
app/components/users/users.html
Normal file
|
@ -0,0 +1,159 @@
|
|||
<rd-header>
|
||||
<rd-header-title title="Users">
|
||||
<a data-toggle="tooltip" title="Refresh" ui-sref="users" ui-sref-opts="{reload: true}">
|
||||
<i class="fa fa-refresh" aria-hidden="true"></i>
|
||||
</a>
|
||||
<i id="loadUsersSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
|
||||
</rd-header-title>
|
||||
<rd-header-content>User management</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-plus" title="Add a new user">
|
||||
</rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="username" class="col-sm-2 control-label text-left">Username</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="username" ng-model="formValues.Username" ng-change="checkUsernameValidity()" placeholder="e.g. jdoe">
|
||||
<span class="input-group-addon"><i ng-class="{true: 'fa fa-check green-icon', false: 'fa fa-times red-icon'}[state.validUsername]" aria-hidden="true"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<!-- new-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="password" class="col-sm-2 control-label text-left">Password</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input type="password" class="form-control" ng-model="formValues.Password" id="password">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !new-password-input -->
|
||||
<!-- confirm-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="confirm_password" class="col-sm-2 control-label text-left">Confirm password</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input type="password" class="form-control" ng-model="formValues.ConfirmPassword" id="confirm_password">
|
||||
<span class="input-group-addon"><i ng-class="{true: 'fa fa-check green-icon', false: 'fa fa-times red-icon'}[formValues.Password !== '' && formValues.Password === formValues.ConfirmPassword]" aria-hidden="true"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !confirm-password-input -->
|
||||
<!-- role-checkbox -->
|
||||
<div class="form-group">
|
||||
<label for="permissions" class="col-sm-2 control-label text-left">Permissions</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<label class="btn btn-default" ng-model="formValues.Role" uib-btn-radio="2">
|
||||
<i class="fa fa-user" aria-hidden="true"></i>
|
||||
User
|
||||
</label>
|
||||
<label class="btn btn-default" ng-model="formValues.Role" uib-btn-radio="1">
|
||||
<i class="fa fa-user-circle-o" aria-hidden="true"></i>
|
||||
Administrator
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !role-checkbox -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!state.validUsername || formValues.Username === '' || formValues.Password === '' || formValues.Password !== formValues.ConfirmPassword" ng-click="addUser()"><i class="fa fa-user-plus" aria-hidden="true"></i> Add user</button>
|
||||
<i id="createUserSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
|
||||
<span class="text-danger" ng-if="state.userCreationError" style="margin: 5px;">
|
||||
<i class="fa fa-exclamation-circle" aria-hidden="true"></i> {{ state.userCreationError }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-user" title="Users">
|
||||
<div class="pull-right">
|
||||
Items per page:
|
||||
<select ng-model="state.pagination_count" ng-change="changePaginationCount()">
|
||||
<option value="0">All</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</div>
|
||||
</rd-widget-header>
|
||||
<rd-widget-taskbar classes="col-lg-12">
|
||||
<div class="pull-left">
|
||||
<button type="button" class="btn btn-danger" ng-click="removeAction()" ng-disabled="!state.selectedItemCount"><i class="fa fa-trash space-right" aria-hidden="true"></i>Remove</button>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<input type="text" id="filter" ng-model="state.filter" placeholder="Filter..." class="form-control input-sm" />
|
||||
</div>
|
||||
</rd-widget-taskbar>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<input type="checkbox" ng-model="allSelected" ng-change="selectItems(allSelected)" />
|
||||
</th>
|
||||
<th>
|
||||
<a ui-sref="users" ng-click="order('Username')">
|
||||
Name
|
||||
<span ng-show="sortType == 'Username' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortType == 'Username' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ui-sref="users" ng-click="order('RoleName')">
|
||||
Role
|
||||
<span ng-show="sortType == 'RoleName' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortType == 'RoleName' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="user in (state.filteredUsers = (users | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))">
|
||||
<td><input type="checkbox" ng-model="user.Checked" ng-change="selectItem(user)" /></td>
|
||||
<td>{{ user.Username }}</td>
|
||||
<td>
|
||||
{{ user.RoleName }}
|
||||
<i class="fa" ng-class="user.RoleId === 1 ? 'fa-user-circle-o' : 'fa-user'" aria-hidden="true" style="margin-left: 2px;"></i>
|
||||
</td>
|
||||
<td>
|
||||
<a ui-sref="user({id: user.Id})"><i class="fa fa-pencil-square-o" aria-hidden="true"></i> Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!users">
|
||||
<td colspan="4" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="users.length == 0">
|
||||
<td colspan="4" class="text-center text-muted">No users available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div ng-if="users" class="pull-left pagination-controls">
|
||||
<dir-pagination-controls></dir-pagination-controls>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
<rd-widget>
|
||||
</div>
|
||||
</div>
|
132
app/components/users/usersController.js
Normal file
132
app/components/users/usersController.js
Normal file
|
@ -0,0 +1,132 @@
|
|||
angular.module('users', [])
|
||||
.controller('UsersController', ['$scope', '$state', 'UserService', 'ModalService', 'Messages', 'Pagination',
|
||||
function ($scope, $state, UserService, ModalService, Messages, Pagination) {
|
||||
$scope.state = {
|
||||
userCreationError: '',
|
||||
selectedItemCount: 0,
|
||||
validUsername: false,
|
||||
pagination_count: Pagination.getPaginationCount('users')
|
||||
};
|
||||
$scope.sortType = 'RoleName';
|
||||
$scope.sortReverse = false;
|
||||
|
||||
$scope.formValues = {
|
||||
Username: '',
|
||||
Password: '',
|
||||
ConfirmPassword: '',
|
||||
Role: 2,
|
||||
};
|
||||
|
||||
$scope.order = function(sortType) {
|
||||
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
||||
$scope.sortType = sortType;
|
||||
};
|
||||
|
||||
$scope.changePaginationCount = function() {
|
||||
Pagination.setPaginationCount('endpoints', $scope.state.pagination_count);
|
||||
};
|
||||
|
||||
$scope.selectItems = function (allSelected) {
|
||||
angular.forEach($scope.state.filteredUsers, function (user) {
|
||||
if (user.Checked !== allSelected) {
|
||||
user.Checked = allSelected;
|
||||
$scope.selectItem(user);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.selectItem = function (item) {
|
||||
if (item.Checked) {
|
||||
$scope.state.selectedItemCount++;
|
||||
} else {
|
||||
$scope.state.selectedItemCount--;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.checkUsernameValidity = function() {
|
||||
var valid = true;
|
||||
for (var i = 0; i < $scope.users.length; i++) {
|
||||
if ($scope.formValues.Username === $scope.users[i].Username) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$scope.state.validUsername = valid;
|
||||
$scope.state.userCreationError = valid ? '' : 'Username already taken';
|
||||
};
|
||||
|
||||
$scope.addUser = function() {
|
||||
$scope.state.userCreationError = '';
|
||||
var username = $scope.formValues.Username;
|
||||
var password = $scope.formValues.Password;
|
||||
var role = $scope.formValues.Role;
|
||||
UserService.createUser(username, password, role)
|
||||
.then(function success(data) {
|
||||
Messages.send("User created", username);
|
||||
$state.reload();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
$scope.state.userCreationError = err.msg;
|
||||
})
|
||||
.finally(function final() {
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
function deleteSelectedUsers() {
|
||||
$('#loadUsersSpinner').show();
|
||||
var counter = 0;
|
||||
var complete = function () {
|
||||
counter = counter - 1;
|
||||
if (counter === 0) {
|
||||
$('#loadUsersSpinner').hide();
|
||||
}
|
||||
};
|
||||
angular.forEach($scope.users, function (user) {
|
||||
if (user.Checked) {
|
||||
counter = counter + 1;
|
||||
UserService.deleteUser(user.Id)
|
||||
.then(function success(data) {
|
||||
var index = $scope.users.indexOf(user);
|
||||
$scope.users.splice(index, 1);
|
||||
Messages.send('User successfully deleted', user.Username);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, 'Unable to remove user');
|
||||
})
|
||||
.finally(function final() {
|
||||
complete();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.removeAction = function () {
|
||||
ModalService.confirmDeletion(
|
||||
'Do you want to delete the selected users? They will not be able to login into Portainer anymore.',
|
||||
function onConfirm(confirmed) {
|
||||
if(!confirmed) { return; }
|
||||
deleteSelectedUsers();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function fetchUsers() {
|
||||
$('#loadUsersSpinner').show();
|
||||
UserService.users()
|
||||
.then(function success(data) {
|
||||
$scope.users = data.map(function(user) {
|
||||
return new UserViewModel(user);
|
||||
});
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to retrieve users");
|
||||
$scope.users = [];
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadUsersSpinner').hide();
|
||||
});
|
||||
}
|
||||
|
||||
fetchUsers();
|
||||
}]);
|
|
@ -60,6 +60,13 @@
|
|||
<span ng-show="sortType == 'Mountpoint' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th ng-if="applicationState.application.authentication">
|
||||
<a ui-sref="volumes" ng-click="order('Metadata.ResourceControl.OwnerId')">
|
||||
Ownership
|
||||
<span ng-show="sortType == 'Metadata.ResourceControl.OwnerId' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortType == 'Metadata.ResourceControl.OwnerId' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -68,12 +75,33 @@
|
|||
<td>{{ volume.Name|truncate:50 }}</td>
|
||||
<td>{{ volume.Driver }}</td>
|
||||
<td>{{ volume.Mountpoint }}</td>
|
||||
<td ng-if="applicationState.application.authentication">
|
||||
<span ng-if="user.role === 1 && volume.Metadata.ResourceControl">
|
||||
<i class="fa fa-eye-slash" aria-hidden="true"></i>
|
||||
<span ng-if="volume.Metadata.ResourceControl.OwnerId === user.ID">
|
||||
Private
|
||||
</span>
|
||||
<span ng-if="volume.Metadata.ResourceControl.OwnerId !== user.ID">
|
||||
Private <span ng-if="volume.Owner">(owner: {{ volume.Owner }})</span>
|
||||
</span>
|
||||
<a ng-click="switchOwnership(volume)" class="interactive"><i class="fa fa-eye" aria-hidden="true" style="margin-left: 7px;"></i> Switch to public</a>
|
||||
</span>
|
||||
<span ng-if="user.role !== 1 && volume.Metadata.ResourceControl.OwnerId === user.ID">
|
||||
<i class="fa fa-eye-slash" aria-hidden="true"></i>
|
||||
Private
|
||||
<a ng-click="switchOwnership(volume)" class="interactive"><i class="fa fa-eye" aria-hidden="true" style="margin-left: 7px;"></i> Switch to public</a>
|
||||
</span>
|
||||
<span ng-if="!volume.Metadata.ResourceControl">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
Public
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!volumes">
|
||||
<td colspan="4" class="text-center text-muted">Loading...</td>
|
||||
<td colspan="6" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="volumes.length == 0">
|
||||
<td colspan="4" class="text-center text-muted">No volumes available.</td>
|
||||
<td colspan="6" class="text-center text-muted">No volumes available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('volumes', [])
|
||||
.controller('VolumesController', ['$scope', '$state', 'Volume', 'Messages', 'Pagination',
|
||||
function ($scope, $state, Volume, Messages, Pagination) {
|
||||
.controller('VolumesController', ['$scope', '$state', 'Volume', 'Messages', 'Pagination', 'ModalService', 'Authentication', 'ResourceControlService', 'UserService',
|
||||
function ($scope, $state, Volume, Messages, Pagination, ModalService, Authentication, ResourceControlService, UserService) {
|
||||
$scope.state = {};
|
||||
$scope.state.pagination_count = Pagination.getPaginationCount('volumes');
|
||||
$scope.state.selectedItemCount = 0;
|
||||
|
@ -10,6 +10,24 @@ function ($scope, $state, Volume, Messages, Pagination) {
|
|||
Name: ''
|
||||
};
|
||||
|
||||
function removeVolumeResourceControl(volume) {
|
||||
ResourceControlService.removeVolumeResourceControl(volume.Metadata.ResourceControl.OwnerId, volume.Name)
|
||||
.then(function success() {
|
||||
delete volume.Metadata.ResourceControl;
|
||||
Messages.send('Ownership changed to public', volume.Name);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to change volume ownership");
|
||||
});
|
||||
}
|
||||
|
||||
$scope.switchOwnership = function(volume) {
|
||||
ModalService.confirmVolumeOwnershipChange(function (confirmed) {
|
||||
if(!confirmed) { return; }
|
||||
removeVolumeResourceControl(volume);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.changePaginationCount = function() {
|
||||
Pagination.setPaginationCount('volumes', $scope.state.pagination_count);
|
||||
};
|
||||
|
@ -52,9 +70,21 @@ function ($scope, $state, Volume, Messages, Pagination) {
|
|||
if (d.message) {
|
||||
Messages.error("Unable to remove volume", {}, d.message);
|
||||
} else {
|
||||
Messages.send("Volume deleted", volume.Name);
|
||||
var index = $scope.volumes.indexOf(volume);
|
||||
$scope.volumes.splice(index, 1);
|
||||
if (volume.Metadata && volume.Metadata.ResourceControl) {
|
||||
ResourceControlService.removeVolumeResourceControl(volume.Metadata.ResourceControl.OwnerId, volume.Name)
|
||||
.then(function success() {
|
||||
Messages.send("Volume deleted", volume.Name);
|
||||
var index = $scope.volumes.indexOf(volume);
|
||||
$scope.volumes.splice(index, 1);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to remove volume ownership");
|
||||
});
|
||||
} else {
|
||||
Messages.send("Volume deleted", volume.Name);
|
||||
var index = $scope.volumes.indexOf(volume);
|
||||
$scope.volumes.splice(index, 1);
|
||||
}
|
||||
}
|
||||
complete();
|
||||
}, function (e) {
|
||||
|
@ -65,11 +95,45 @@ function ($scope, $state, Volume, Messages, Pagination) {
|
|||
});
|
||||
};
|
||||
|
||||
function mapUsersToVolumes(users) {
|
||||
angular.forEach($scope.volumes, function (volume) {
|
||||
if (volume.Metadata) {
|
||||
var volumeRC = volume.Metadata.ResourceControl;
|
||||
if (volumeRC && volumeRC.OwnerId != $scope.user.ID) {
|
||||
angular.forEach(users, function (user) {
|
||||
if (volumeRC.OwnerId === user.Id) {
|
||||
volume.Owner = user.Username;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fetchVolumes() {
|
||||
$('#loadVolumesSpinner').show();
|
||||
var userDetails = Authentication.getUserDetails();
|
||||
$scope.user = userDetails;
|
||||
|
||||
Volume.query({}, function (d) {
|
||||
$scope.volumes = d.Volumes || [];
|
||||
$('#loadVolumesSpinner').hide();
|
||||
var volumes = d.Volumes || [];
|
||||
$scope.volumes = volumes.map(function (v) {
|
||||
return new VolumeViewModel(v);
|
||||
});
|
||||
if (userDetails.role === 1) {
|
||||
UserService.users()
|
||||
.then(function success(data) {
|
||||
mapUsersToVolumes(data);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Messages.error("Failure", err, "Unable to retrieve users");
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadVolumesSpinner').hide();
|
||||
});
|
||||
} else {
|
||||
$('#loadVolumesSpinner').hide();
|
||||
}
|
||||
}, function (e) {
|
||||
$('#loadVolumesSpinner').hide();
|
||||
Messages.error("Failure", e, "Unable to retrieve volumes");
|
||||
|
|
|
@ -5,7 +5,7 @@ angular
|
|||
requires: '^rdHeader',
|
||||
transclude: true,
|
||||
link: function (scope, iElement, iAttrs) {
|
||||
scope.username = Authentication.getCredentials().username;
|
||||
scope.username = Authentication.getUserDetails().username;
|
||||
},
|
||||
template: '<div class="breadcrumb-links"><div class="pull-left" ng-transclude></div><div class="pull-right" ng-if="username"><a ui-sref="auth({logout: true})" class="text-danger" style="margin-right: 25px;"><u>log out <i class="fa fa-sign-out" aria-hidden="true"></i></u></a></div></div>',
|
||||
restrict: 'E'
|
||||
|
|
|
@ -7,7 +7,7 @@ angular
|
|||
title: '@'
|
||||
},
|
||||
link: function (scope, iElement, iAttrs) {
|
||||
scope.username = Authentication.getCredentials().username;
|
||||
scope.username = Authentication.getUserDetails().username;
|
||||
},
|
||||
transclude: true,
|
||||
template: '<div class="page white-space-normal">{{title}}<span class="header_title_content" ng-transclude></span><span class="pull-right user-box" ng-if="username"><i class="fa fa-user-circle-o" aria-hidden="true"></i> {{username}}</span></div>',
|
||||
|
|
13
app/directives/tooltip.js
Normal file
13
app/directives/tooltip.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
angular
|
||||
.module('portainer')
|
||||
.directive('portainerTooltip', [function portainerTooltip() {
|
||||
var directive = {
|
||||
scope: {
|
||||
message: '@',
|
||||
position: '@'
|
||||
},
|
||||
template: '<span class="interactive" tooltip-placement="{{position}}" tooltip-class="portainer-tooltip" uib-tooltip="{{message}}"><i class="fa fa-question-circle tooltip-icon" aria-hidden="true"></i></span>',
|
||||
restrict: 'E'
|
||||
};
|
||||
return directive;
|
||||
}]);
|
|
@ -5,10 +5,11 @@ angular
|
|||
requires: '^rdWidget',
|
||||
scope: {
|
||||
title: '@',
|
||||
icon: '@'
|
||||
icon: '@',
|
||||
classes: '@?'
|
||||
},
|
||||
transclude: true,
|
||||
template: '<div class="widget-header"><div class="row"><span class="pull-left"><i class="fa" ng-class="icon"></i> {{title}} </span><span class="pull-right col-xs-6 col-sm-4" ng-transclude></span></div></div>',
|
||||
template: '<div class="widget-header"><div class="row"><span ng-class="classes" class="pull-left"><i class="fa" ng-class="icon"></i> {{title}} </span><span ng-class="classes" class="pull-right" ng-transclude></span></div></div>',
|
||||
restrict: 'E'
|
||||
};
|
||||
return directive;
|
||||
|
|
|
@ -10,11 +10,21 @@ function ContainerViewModel(data) {
|
|||
this.Image = data.Image;
|
||||
this.Command = data.Command;
|
||||
this.Checked = false;
|
||||
this.Labels = data.Labels;
|
||||
this.Ports = [];
|
||||
this.Mounts = data.Mounts;
|
||||
for (var i = 0; i < data.Ports.length; ++i) {
|
||||
var p = data.Ports[i];
|
||||
if (p.PublicPort) {
|
||||
this.Ports.push({ host: p.IP, private: p.PrivatePort, public: p.PublicPort });
|
||||
}
|
||||
}
|
||||
if (data.Portainer) {
|
||||
this.Metadata = {};
|
||||
if (data.Portainer.ResourceControl) {
|
||||
this.Metadata.ResourceControl = {
|
||||
OwnerId: data.Portainer.ResourceControl.OwnerId
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,10 @@ function ServiceViewModel(data) {
|
|||
if (data.Spec.TaskTemplate.ContainerSpec.Env) {
|
||||
this.Env = data.Spec.TaskTemplate.ContainerSpec.Env;
|
||||
}
|
||||
this.Mounts = [];
|
||||
if (data.Spec.TaskTemplate.ContainerSpec.Mounts) {
|
||||
this.Mounts = data.Spec.TaskTemplate.ContainerSpec.Mounts;
|
||||
}
|
||||
if (data.Endpoint.Ports) {
|
||||
this.Ports = data.Endpoint.Ports;
|
||||
}
|
||||
|
@ -33,4 +37,13 @@ function ServiceViewModel(data) {
|
|||
this.Checked = false;
|
||||
this.Scale = false;
|
||||
this.EditName = false;
|
||||
|
||||
if (data.Portainer) {
|
||||
this.Metadata = {};
|
||||
if (data.Portainer.ResourceControl) {
|
||||
this.Metadata.ResourceControl = {
|
||||
OwnerId: data.Portainer.ResourceControl.OwnerId
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
11
app/models/user.js
Normal file
11
app/models/user.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
function UserViewModel(data) {
|
||||
this.Id = data.Id;
|
||||
this.Username = data.Username;
|
||||
this.RoleId = data.Role;
|
||||
if (data.Role === 1) {
|
||||
this.RoleName = "administrator";
|
||||
} else {
|
||||
this.RoleName = "user";
|
||||
}
|
||||
this.Checked = false;
|
||||
}
|
14
app/models/volume.js
Normal file
14
app/models/volume.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
function VolumeViewModel(data) {
|
||||
this.Id = data.Id;
|
||||
this.Name = data.Name;
|
||||
this.Driver = data.Driver;
|
||||
this.Mountpoint = data.Mountpoint;
|
||||
if (data.Portainer) {
|
||||
this.Metadata = {};
|
||||
if (data.Portainer.ResourceControl) {
|
||||
this.Metadata.ResourceControl = {
|
||||
OwnerId: data.Portainer.ResourceControl.OwnerId
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('Container', ['$resource', 'Settings', function ContainerFactory($resource, Settings) {
|
||||
.factory('Container', ['$resource', 'Settings', 'EndpointProvider', function ContainerFactory($resource, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return $resource(Settings.url + '/containers/:id/:action', {
|
||||
name: '@name'
|
||||
}, {
|
||||
return $resource(Settings.url + '/:endpointId/containers/:id/:action', {
|
||||
name: '@name',
|
||||
endpointId: EndpointProvider.endpointID
|
||||
},
|
||||
{
|
||||
query: {method: 'GET', params: {all: 0, action: 'json', filters: '@filters' }, isArray: true},
|
||||
get: {method: 'GET', params: {action: 'json'}},
|
||||
stop: {method: 'POST', params: {id: '@id', t: 5, action: 'stop'}},
|
||||
|
@ -11,7 +13,6 @@ angular.module('portainer.rest')
|
|||
kill: {method: 'POST', params: {id: '@id', action: 'kill'}},
|
||||
pause: {method: 'POST', params: {id: '@id', action: 'pause'}},
|
||||
unpause: {method: 'POST', params: {id: '@id', action: 'unpause'}},
|
||||
changes: {method: 'GET', params: {action: 'changes'}, isArray: true},
|
||||
stats: {method: 'GET', params: {id: '@id', stream: false, action: 'stats'}, timeout: 5000},
|
||||
start: {
|
||||
method: 'POST', params: {id: '@id', action: 'start'},
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('ContainerCommit', ['$resource', 'Settings', function ContainerCommitFactory($resource, Settings) {
|
||||
.factory('ContainerCommit', ['$resource', 'Settings', 'EndpointProvider', function ContainerCommitFactory($resource, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return $resource(Settings.url + '/commit', {}, {
|
||||
return $resource(Settings.url + '/:endpointId/commit', {
|
||||
endpointId: EndpointProvider.endpointID
|
||||
},
|
||||
{
|
||||
commit: {method: 'POST', params: {container: '@id', repo: '@repo', tag: '@tag'}}
|
||||
});
|
||||
}]);
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('ContainerLogs', ['$http', 'Settings', function ContainerLogsFactory($http, Settings) {
|
||||
.factory('ContainerLogs', ['$http', 'Settings', 'EndpointProvider', function ContainerLogsFactory($http, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return {
|
||||
get: function (id, params, callback) {
|
||||
$http({
|
||||
method: 'GET',
|
||||
url: Settings.url + '/containers/' + id + '/logs',
|
||||
url: Settings.url + '/' + EndpointProvider.endpointID() + '/containers/' + id + '/logs',
|
||||
params: {
|
||||
'stdout': params.stdout || 0,
|
||||
'stderr': params.stderr || 0,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('ContainerTop', ['$http', 'Settings', function ($http, Settings) {
|
||||
.factory('ContainerTop', ['$http', 'Settings', 'EndpointProvider', function ($http, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return {
|
||||
get: function (id, params, callback, errorCallback) {
|
||||
$http({
|
||||
method: 'GET',
|
||||
url: Settings.url + '/containers/' + id + '/top',
|
||||
url: Settings.url + '/' + EndpointProvider.endpointID() + '/containers/' + id + '/top',
|
||||
params: {
|
||||
ps_args: params.ps_args
|
||||
}
|
||||
|
|
|
@ -6,8 +6,7 @@ angular.module('portainer.rest')
|
|||
query: { method: 'GET', isArray: true },
|
||||
get: { method: 'GET', params: { id: '@id' } },
|
||||
update: { method: 'PUT', params: { id: '@id' } },
|
||||
updateAccess: { method: 'PUT', params: { id: '@id', action: 'access' } },
|
||||
remove: { method: 'DELETE', params: { id: '@id'} },
|
||||
getActiveEndpoint: { method: 'GET', params: { id: '0' } },
|
||||
setActiveEndpoint: { method: 'POST', params: { id: '@id', action: 'active' } }
|
||||
});
|
||||
}]);
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('Events', ['$resource', 'Settings', function EventFactory($resource, Settings) {
|
||||
.factory('Events', ['$resource', 'Settings', 'EndpointProvider', function EventFactory($resource, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return $resource(Settings.url + '/events', {}, {
|
||||
return $resource(Settings.url + '/:endpointId/events', {
|
||||
endpointId: EndpointProvider.endpointID
|
||||
},
|
||||
{
|
||||
query: {
|
||||
method: 'GET', params: {since: '@since', until: '@until'},
|
||||
isArray: true, transformResponse: jsonObjectsToArrayHandler
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('Exec', ['$resource', 'Settings', function ExecFactory($resource, Settings) {
|
||||
.factory('Exec', ['$resource', 'Settings', 'EndpointProvider', function ExecFactory($resource, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return $resource(Settings.url + '/exec/:id/:action', {}, {
|
||||
return $resource(Settings.url + '/:endpointId/exec/:id/:action', {
|
||||
endpointId: EndpointProvider.endpointID
|
||||
},
|
||||
{
|
||||
resize: {
|
||||
method: 'POST', params: {id: '@id', action: 'resize', h: '@height', w: '@width'},
|
||||
transformResponse: genericHandler
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('Image', ['$resource', 'Settings', function ImageFactory($resource, Settings) {
|
||||
.factory('Image', ['$resource', 'Settings', 'EndpointProvider', function ImageFactory($resource, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return $resource(Settings.url + '/images/:id/:action', {}, {
|
||||
return $resource(Settings.url + '/:endpointId/images/:id/:action', {
|
||||
endpointId: EndpointProvider.endpointID
|
||||
},
|
||||
{
|
||||
query: {method: 'GET', params: {all: 0, action: 'json'}, isArray: true},
|
||||
get: {method: 'GET', params: {action: 'json'}},
|
||||
search: {method: 'GET', params: {action: 'search'}},
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('Info', ['$resource', 'Settings', function InfoFactory($resource, Settings) {
|
||||
.factory('Info', ['$resource', 'Settings', 'EndpointProvider', function InfoFactory($resource, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return $resource(Settings.url + '/info', {});
|
||||
return $resource(Settings.url + '/:endpointId/info', {
|
||||
endpointId: EndpointProvider.endpointID
|
||||
});
|
||||
}]);
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('Network', ['$resource', 'Settings', function NetworkFactory($resource, Settings) {
|
||||
.factory('Network', ['$resource', 'Settings', 'EndpointProvider', function NetworkFactory($resource, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return $resource(Settings.url + '/networks/:id/:action', {id: '@id'}, {
|
||||
return $resource(Settings.url + '/:endpointId/networks/:id/:action', {
|
||||
id: '@id',
|
||||
endpointId: EndpointProvider.endpointID
|
||||
},
|
||||
{
|
||||
query: {method: 'GET', isArray: true},
|
||||
get: {method: 'GET'},
|
||||
create: {method: 'POST', params: {action: 'create'}, transformResponse: genericHandler},
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('Node', ['$resource', 'Settings', function NodeFactory($resource, Settings) {
|
||||
.factory('Node', ['$resource', 'Settings', 'EndpointProvider', function NodeFactory($resource, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return $resource(Settings.url + '/nodes/:id/:action', {}, {
|
||||
return $resource(Settings.url + '/:endpointId/nodes/:id/:action', {
|
||||
endpointId: EndpointProvider.endpointID
|
||||
},
|
||||
{
|
||||
query: {method: 'GET', isArray: true},
|
||||
get: {method: 'GET', params: {id: '@id'}},
|
||||
update: { method: 'POST', params: {id: '@id', action: 'update', version: '@version'} },
|
||||
|
|
8
app/rest/resourceControl.js
Normal file
8
app/rest/resourceControl.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('ResourceControl', ['$resource', 'USERS_ENDPOINT', function ResourceControlFactory($resource, USERS_ENDPOINT) {
|
||||
'use strict';
|
||||
return $resource(USERS_ENDPOINT + '/:userId/resources/:resourceType/:resourceId', {}, {
|
||||
create: { method: 'POST', params: { userId: '@userId', resourceType: '@resourceType' } },
|
||||
remove: { method: 'DELETE', params: { userId: '@userId', resourceId: '@resourceId', resourceType: '@resourceType' } },
|
||||
});
|
||||
}]);
|
|
@ -1,7 +1,10 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('Service', ['$resource', 'Settings', function ServiceFactory($resource, Settings) {
|
||||
.factory('Service', ['$resource', 'Settings', 'EndpointProvider', function ServiceFactory($resource, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return $resource(Settings.url + '/services/:id/:action', {}, {
|
||||
return $resource(Settings.url + '/:endpointId/services/:id/:action', {
|
||||
endpointId: EndpointProvider.endpointID
|
||||
},
|
||||
{
|
||||
get: { method: 'GET', params: {id: '@id'} },
|
||||
query: { method: 'GET', isArray: true },
|
||||
create: { method: 'POST', params: {action: 'create'} },
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('Swarm', ['$resource', 'Settings', function SwarmFactory($resource, Settings) {
|
||||
.factory('Swarm', ['$resource', 'Settings', 'EndpointProvider', function SwarmFactory($resource, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return $resource(Settings.url + '/swarm', {}, {
|
||||
return $resource(Settings.url + '/:endpointId/swarm', {
|
||||
endpointId: EndpointProvider.endpointID
|
||||
},
|
||||
{
|
||||
get: {method: 'GET'}
|
||||
});
|
||||
}]);
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('Task', ['$resource', 'Settings', function TaskFactory($resource, Settings) {
|
||||
.factory('Task', ['$resource', 'Settings', 'EndpointProvider', function TaskFactory($resource, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return $resource(Settings.url + '/tasks/:id', {}, {
|
||||
return $resource(Settings.url + '/:endpointId/tasks/:id', {
|
||||
endpointId: EndpointProvider.endpointID
|
||||
},
|
||||
{
|
||||
get: { method: 'GET', params: {id: '@id'} },
|
||||
query: { method: 'GET', isArray: true, params: {filters: '@filters'} }
|
||||
});
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('Users', ['$resource', 'USERS_ENDPOINT', function UsersFactory($resource, USERS_ENDPOINT) {
|
||||
'use strict';
|
||||
return $resource(USERS_ENDPOINT + '/:username/:action', {}, {
|
||||
return $resource(USERS_ENDPOINT + '/:id/:action', {}, {
|
||||
create: { method: 'POST' },
|
||||
get: { method: 'GET', params: { username: '@username' } },
|
||||
update: { method: 'PUT', params: { username: '@username' } },
|
||||
checkPassword: { method: 'POST', params: { username: '@username', action: 'passwd' } },
|
||||
checkAdminUser: { method: 'GET', params: { username: 'admin', action: 'check' } },
|
||||
initAdminUser: { method: 'POST', params: { username: 'admin', action: 'init' } }
|
||||
query: { method: 'GET', isArray: true },
|
||||
get: { method: 'GET', params: { id: '@id' } },
|
||||
update: { method: 'PUT', params: { id: '@id' } },
|
||||
remove: { method: 'DELETE', params: { id: '@id'} },
|
||||
// RPCs should be moved to a specific endpoint
|
||||
checkPassword: { method: 'POST', params: { id: '@id', action: 'passwd' } },
|
||||
checkAdminUser: { method: 'GET', params: { id: 'admin', action: 'check' }, isArray: true },
|
||||
initAdminUser: { method: 'POST', params: { id: 'admin', action: 'init' } }
|
||||
});
|
||||
}]);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('Version', ['$resource', 'Settings', function VersionFactory($resource, Settings) {
|
||||
.factory('Version', ['$resource', 'Settings', 'EndpointProvider', function VersionFactory($resource, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return $resource(Settings.url + '/version', {});
|
||||
return $resource(Settings.url + '/:endpointId/version', {
|
||||
endpointId: EndpointProvider.endpointID
|
||||
});
|
||||
}]);
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
angular.module('portainer.rest')
|
||||
.factory('Volume', ['$resource', 'Settings', function VolumeFactory($resource, Settings) {
|
||||
.factory('Volume', ['$resource', 'Settings', 'EndpointProvider', function VolumeFactory($resource, Settings, EndpointProvider) {
|
||||
'use strict';
|
||||
return $resource(Settings.url + '/volumes/:name/:action', {name: '@name'}, {
|
||||
return $resource(Settings.url + '/:endpointId/volumes/:name/:action',
|
||||
{
|
||||
name: '@name',
|
||||
endpointId: EndpointProvider.endpointID
|
||||
},
|
||||
{
|
||||
query: {method: 'GET'},
|
||||
get: {method: 'GET'},
|
||||
create: {method: 'POST', params: {action: 'create'}, transformResponse: genericHandler},
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
angular.module('portainer.services')
|
||||
.factory('Authentication', ['$q', 'Auth', 'jwtHelper', 'LocalStorage', 'StateManager', function AuthenticationFactory($q, Auth, jwtHelper, LocalStorage, StateManager) {
|
||||
.factory('Authentication', ['$q', 'Auth', 'jwtHelper', 'LocalStorage', 'StateManager', 'EndpointProvider', function AuthenticationFactory($q, Auth, jwtHelper, LocalStorage, StateManager, EndpointProvider) {
|
||||
'use strict';
|
||||
|
||||
var credentials = {};
|
||||
var user = {};
|
||||
return {
|
||||
init: function() {
|
||||
var jwt = LocalStorage.getJWT();
|
||||
if (jwt) {
|
||||
var tokenPayload = jwtHelper.decodeToken(jwt);
|
||||
credentials.username = tokenPayload.username;
|
||||
user.username = tokenPayload.username;
|
||||
user.ID = tokenPayload.id;
|
||||
user.role = tokenPayload.role;
|
||||
}
|
||||
},
|
||||
login: function(username, password) {
|
||||
|
@ -16,7 +18,10 @@ angular.module('portainer.services')
|
|||
Auth.login({username: username, password: password}).$promise
|
||||
.then(function(data) {
|
||||
LocalStorage.storeJWT(data.jwt);
|
||||
credentials.username = username;
|
||||
var tokenPayload = jwtHelper.decodeToken(data.jwt);
|
||||
user.username = username;
|
||||
user.ID = tokenPayload.id;
|
||||
user.role = tokenPayload.role;
|
||||
resolve();
|
||||
}, function() {
|
||||
reject();
|
||||
|
@ -25,14 +30,15 @@ angular.module('portainer.services')
|
|||
},
|
||||
logout: function() {
|
||||
StateManager.clean();
|
||||
EndpointProvider.clean();
|
||||
LocalStorage.clean();
|
||||
},
|
||||
isAuthenticated: function() {
|
||||
var jwt = LocalStorage.getJWT();
|
||||
return jwt && !jwtHelper.isTokenExpired(jwt);
|
||||
},
|
||||
getCredentials: function() {
|
||||
return credentials;
|
||||
getUserDetails: function() {
|
||||
return user;
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
|
23
app/services/endpointProvider.js
Normal file
23
app/services/endpointProvider.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
angular.module('portainer.services')
|
||||
.factory('EndpointProvider', ['LocalStorage', function EndpointProviderFactory(LocalStorage) {
|
||||
'use strict';
|
||||
var endpoint = {};
|
||||
var service = {};
|
||||
service.initialize = function() {
|
||||
var endpointID = LocalStorage.getEndpointID();
|
||||
if (endpointID) {
|
||||
endpoint.ID = endpointID;
|
||||
}
|
||||
};
|
||||
service.clean = function() {
|
||||
endpoint = {};
|
||||
};
|
||||
service.endpointID = function() {
|
||||
return endpoint.ID;
|
||||
};
|
||||
service.setEndpointID = function(id) {
|
||||
endpoint.ID = id;
|
||||
LocalStorage.storeEndpointID(id);
|
||||
};
|
||||
return service;
|
||||
}]);
|
|
@ -1,84 +1,92 @@
|
|||
angular.module('portainer.services')
|
||||
.factory('EndpointService', ['$q', 'Endpoints', 'FileUploadService', function EndpointServiceFactory($q, Endpoints, FileUploadService) {
|
||||
'use strict';
|
||||
return {
|
||||
getActive: function() {
|
||||
return Endpoints.getActiveEndpoint().$promise;
|
||||
},
|
||||
setActive: function(endpointID) {
|
||||
return Endpoints.setActiveEndpoint({id: endpointID}).$promise;
|
||||
},
|
||||
endpoint: function(endpointID) {
|
||||
return Endpoints.get({id: endpointID}).$promise;
|
||||
},
|
||||
endpoints: function() {
|
||||
return Endpoints.query({}).$promise;
|
||||
},
|
||||
updateEndpoint: function(ID, name, URL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile, type) {
|
||||
var endpoint = {
|
||||
id: ID,
|
||||
Name: name,
|
||||
URL: type === 'local' ? ("unix://" + URL) : ("tcp://" + URL),
|
||||
TLS: TLS
|
||||
};
|
||||
var deferred = $q.defer();
|
||||
Endpoints.update({}, endpoint, function success(data) {
|
||||
FileUploadService.uploadTLSFilesForEndpoint(ID, TLSCAFile, TLSCertFile, TLSKeyFile).then(function success(data) {
|
||||
var service = {};
|
||||
|
||||
service.endpoint = function(endpointID) {
|
||||
return Endpoints.get({id: endpointID}).$promise;
|
||||
};
|
||||
|
||||
service.endpoints = function() {
|
||||
return Endpoints.query({}).$promise;
|
||||
};
|
||||
|
||||
service.updateAuthorizedUsers = function(id, authorizedUserIDs) {
|
||||
return Endpoints.updateAccess({id: id}, {authorizedUsers: authorizedUserIDs}).$promise;
|
||||
};
|
||||
|
||||
service.updateEndpoint = function(id, endpointParams) {
|
||||
var query = {
|
||||
name: endpointParams.name,
|
||||
TLS: endpointParams.TLS,
|
||||
authorizedUsers: endpointParams.authorizedUsers
|
||||
};
|
||||
if (endpointParams.type && endpointParams.URL) {
|
||||
query.URL = endpointParams.type === 'local' ? ("unix://" + endpointParams.URL) : ("tcp://" + endpointParams.URL);
|
||||
}
|
||||
var deferred = $q.defer();
|
||||
Endpoints.update({id: id}, query).$promise
|
||||
.then(function success() {
|
||||
return FileUploadService.uploadTLSFilesForEndpoint(id, endpointParams.TLSCAFile, endpointParams.TLSCertFile, endpointParams.TLSKeyFile);
|
||||
})
|
||||
.then(function success(data) {
|
||||
deferred.notify({upload: false});
|
||||
deferred.resolve(data);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.notify({upload: false});
|
||||
deferred.reject({msg: 'Unable to update endpoint', err: err});
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.deleteEndpoint = function(endpointID) {
|
||||
return Endpoints.remove({id: endpointID}).$promise;
|
||||
};
|
||||
|
||||
service.createLocalEndpoint = function(name, URL, TLS, active) {
|
||||
var endpoint = {
|
||||
Name: "local",
|
||||
URL: "unix:///var/run/docker.sock",
|
||||
TLS: false
|
||||
};
|
||||
return Endpoints.create({}, endpoint).$promise;
|
||||
};
|
||||
|
||||
service.createRemoteEndpoint = function(name, URL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile, active) {
|
||||
var endpoint = {
|
||||
Name: name,
|
||||
URL: 'tcp://' + URL,
|
||||
TLS: TLS
|
||||
};
|
||||
var deferred = $q.defer();
|
||||
Endpoints.create({active: active}, endpoint, function success(data) {
|
||||
var endpointID = data.Id;
|
||||
if (TLS) {
|
||||
deferred.notify({upload: true});
|
||||
FileUploadService.uploadTLSFilesForEndpoint(endpointID, TLSCAFile, TLSCertFile, TLSKeyFile).then(function success(data) {
|
||||
deferred.notify({upload: false});
|
||||
deferred.resolve(data);
|
||||
if (active) {
|
||||
Endpoints.setActiveEndpoint({}, {id: endpointID}, function success(data) {
|
||||
deferred.resolve(data);
|
||||
}, function error(err) {
|
||||
deferred.reject({msg: 'Unable to create endpoint', err: err});
|
||||
});
|
||||
} else {
|
||||
deferred.resolve(data);
|
||||
}
|
||||
}, function error(err) {
|
||||
deferred.notify({upload: false});
|
||||
deferred.reject({msg: 'Unable to upload TLS certs', err: err});
|
||||
});
|
||||
}, function error(err) {
|
||||
deferred.reject({msg: 'Unable to update endpoint', err: err});
|
||||
});
|
||||
return deferred.promise;
|
||||
},
|
||||
deleteEndpoint: function(endpointID) {
|
||||
return Endpoints.remove({id: endpointID}).$promise;
|
||||
},
|
||||
createLocalEndpoint: function(name, URL, TLS, active) {
|
||||
var endpoint = {
|
||||
Name: "local",
|
||||
URL: "unix:///var/run/docker.sock",
|
||||
TLS: false
|
||||
};
|
||||
return Endpoints.create({active: active}, endpoint).$promise;
|
||||
},
|
||||
createRemoteEndpoint: function(name, URL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile, active) {
|
||||
var endpoint = {
|
||||
Name: name,
|
||||
URL: 'tcp://' + URL,
|
||||
TLS: TLS
|
||||
};
|
||||
var deferred = $q.defer();
|
||||
Endpoints.create({active: active}, endpoint, function success(data) {
|
||||
var endpointID = data.Id;
|
||||
if (TLS) {
|
||||
deferred.notify({upload: true});
|
||||
FileUploadService.uploadTLSFilesForEndpoint(endpointID, TLSCAFile, TLSCertFile, TLSKeyFile).then(function success(data) {
|
||||
deferred.notify({upload: false});
|
||||
if (active) {
|
||||
Endpoints.setActiveEndpoint({}, {id: endpointID}, function success(data) {
|
||||
deferred.resolve(data);
|
||||
}, function error(err) {
|
||||
deferred.reject({msg: 'Unable to create endpoint', err: err});
|
||||
});
|
||||
} else {
|
||||
deferred.resolve(data);
|
||||
}
|
||||
}, function error(err) {
|
||||
deferred.notify({upload: false});
|
||||
deferred.reject({msg: 'Unable to upload TLS certs', err: err});
|
||||
});
|
||||
} else {
|
||||
deferred.resolve(data);
|
||||
}
|
||||
}, function error(err) {
|
||||
deferred.reject({msg: 'Unable to create endpoint', err: err});
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
} else {
|
||||
deferred.resolve(data);
|
||||
}
|
||||
}, function error(err) {
|
||||
deferred.reject({msg: 'Unable to create endpoint', err: err});
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
return service;
|
||||
}]);
|
||||
|
|
|
@ -19,15 +19,15 @@ angular.module('portainer.services')
|
|||
var deferred = $q.defer();
|
||||
var queue = [];
|
||||
|
||||
if (TLSCAFile !== null) {
|
||||
if (TLSCAFile) {
|
||||
var uploadTLSCA = uploadFile('api/upload/tls/' + endpointID + '/ca', TLSCAFile);
|
||||
queue.push(uploadTLSCA);
|
||||
}
|
||||
if (TLSCertFile !== null) {
|
||||
if (TLSCertFile) {
|
||||
var uploadTLSCert = uploadFile('api/upload/tls/' + endpointID + '/cert', TLSCertFile);
|
||||
queue.push(uploadTLSCert);
|
||||
}
|
||||
if (TLSKeyFile !== null) {
|
||||
if (TLSKeyFile) {
|
||||
var uploadTLSKey = uploadFile('api/upload/tls/' + endpointID + '/key', TLSKeyFile);
|
||||
queue.push(uploadTLSKey);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,12 @@ angular.module('portainer.services')
|
|||
.factory('LocalStorage', ['localStorageService', function LocalStorageFactory(localStorageService) {
|
||||
'use strict';
|
||||
return {
|
||||
storeEndpointID: function(id) {
|
||||
localStorageService.set('ENDPOINT_ID', id);
|
||||
},
|
||||
getEndpointID: function() {
|
||||
return localStorageService.get('ENDPOINT_ID');
|
||||
},
|
||||
storeEndpointState: function(state) {
|
||||
localStorageService.set('ENDPOINT_STATE', state);
|
||||
},
|
||||
|
|
|
@ -2,6 +2,7 @@ angular.module('portainer.services')
|
|||
.factory('ModalService', [function ModalServiceFactory() {
|
||||
'use strict';
|
||||
var service = {};
|
||||
|
||||
service.confirm = function(options){
|
||||
var box = bootbox.confirm({
|
||||
title: options.title,
|
||||
|
@ -9,11 +10,11 @@ angular.module('portainer.services')
|
|||
buttons: {
|
||||
confirm: {
|
||||
label: options.buttons.confirm.label,
|
||||
className: 'btn-danger'
|
||||
className: options.buttons.confirm.className
|
||||
},
|
||||
cancel: {
|
||||
label: options.buttons.cancel.label
|
||||
}
|
||||
cancel: {
|
||||
label: options.buttons.cancel && options.buttons.cancel.label ? options.buttons.cancel.label : 'Cancel'
|
||||
}
|
||||
},
|
||||
callback: options.callback
|
||||
});
|
||||
|
@ -24,5 +25,62 @@ angular.module('portainer.services')
|
|||
}
|
||||
});
|
||||
};
|
||||
|
||||
service.confirmOwnershipChange = function(callback, msg) {
|
||||
service.confirm({
|
||||
title: 'Are you sure ?',
|
||||
message: msg,
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Change ownership',
|
||||
className: 'btn-primary'
|
||||
}
|
||||
},
|
||||
callback: callback,
|
||||
});
|
||||
};
|
||||
|
||||
service.confirmContainerOwnershipChange = function(callback) {
|
||||
var msg = 'You can change the ownership of a container one way only. You will not be able to make this container private again. <b>Changing ownership on this container will also change the ownership on any attached volume.</b>';
|
||||
service.confirmOwnershipChange(callback, msg);
|
||||
};
|
||||
|
||||
service.confirmServiceOwnershipChange = function(callback) {
|
||||
var msg = 'You can change the ownership of a service one way only. You will not be able to make this service private again. <b>Changing ownership on this service will also change the ownership on any attached volume.</b>';
|
||||
service.confirmOwnershipChange(callback, msg);
|
||||
};
|
||||
|
||||
service.confirmVolumeOwnershipChange = function(callback) {
|
||||
var msg = 'You can change the ownership of a volume one way only. You will not be able to make this volume private again.';
|
||||
service.confirmOwnershipChange(callback, msg);
|
||||
};
|
||||
|
||||
service.confirmImageForceRemoval = function(callback) {
|
||||
service.confirm({
|
||||
title: "Are you sure?",
|
||||
message: "Forcing the removal of the image will remove the image even if it has multiple tags or if it is used by stopped containers.",
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Remove the image',
|
||||
className: 'btn-danger'
|
||||
}
|
||||
},
|
||||
callback: callback,
|
||||
});
|
||||
};
|
||||
|
||||
service.confirmDeletion = function(message, callback) {
|
||||
service.confirm({
|
||||
title: 'Are you sure ?',
|
||||
message: message,
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Delete',
|
||||
className: 'btn-danger'
|
||||
}
|
||||
},
|
||||
callback: callback,
|
||||
});
|
||||
};
|
||||
return service;
|
||||
}]);
|
||||
|
|
31
app/services/resourceControlService.js
Normal file
31
app/services/resourceControlService.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
angular.module('portainer.services')
|
||||
.factory('ResourceControlService', ['$q', 'ResourceControl', function ResourceControlServiceFactory($q, ResourceControl) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
|
||||
service.setContainerResourceControl = function(userID, resourceID) {
|
||||
return ResourceControl.create({ userId: userID, resourceType: 'container' }, { ResourceID: resourceID }).$promise;
|
||||
};
|
||||
|
||||
service.removeContainerResourceControl = function(userID, resourceID) {
|
||||
return ResourceControl.remove({ userId: userID, resourceId: resourceID, resourceType: 'container' }).$promise;
|
||||
};
|
||||
|
||||
service.setServiceResourceControl = function(userID, resourceID) {
|
||||
return ResourceControl.create({ userId: userID, resourceType: 'service' }, { ResourceID: resourceID }).$promise;
|
||||
};
|
||||
|
||||
service.removeServiceResourceControl = function(userID, resourceID) {
|
||||
return ResourceControl.remove({ userId: userID, resourceId: resourceID, resourceType: 'service' }).$promise;
|
||||
};
|
||||
|
||||
service.setVolumeResourceControl = function(userID, resourceID) {
|
||||
return ResourceControl.create({ userId: userID, resourceType: 'volume' }, { ResourceID: resourceID }).$promise;
|
||||
};
|
||||
|
||||
service.removeVolumeResourceControl = function(userID, resourceID) {
|
||||
return ResourceControl.remove({ userId: userID, resourceId: resourceID, resourceType: 'volume' }).$promise;
|
||||
};
|
||||
|
||||
return service;
|
||||
}]);
|
48
app/services/userService.js
Normal file
48
app/services/userService.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
angular.module('portainer.services')
|
||||
.factory('UserService', ['$q', 'Users', function UserServiceFactory($q, Users) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
service.users = function() {
|
||||
return Users.query({}).$promise;
|
||||
};
|
||||
|
||||
service.user = function(id) {
|
||||
return Users.get({id: id}).$promise;
|
||||
};
|
||||
|
||||
service.createUser = function(username, password, role) {
|
||||
return Users.create({}, {username: username, password: password, role: role}).$promise;
|
||||
};
|
||||
|
||||
service.deleteUser = function(id) {
|
||||
return Users.remove({id: id}).$promise;
|
||||
};
|
||||
|
||||
service.updateUser = function(id, password, role) {
|
||||
var query = {
|
||||
password: password,
|
||||
role: role
|
||||
};
|
||||
return Users.update({id: id}, query).$promise;
|
||||
};
|
||||
|
||||
service.updateUserPassword = function(id, currentPassword, newPassword) {
|
||||
var deferred = $q.defer();
|
||||
Users.checkPassword({id: id}, {password: currentPassword}).$promise
|
||||
.then(function success(data) {
|
||||
if (!data.valid) {
|
||||
deferred.reject({invalidPassword: true});
|
||||
}
|
||||
return service.updateUser(id, newPassword, undefined);
|
||||
})
|
||||
.then(function success(data) {
|
||||
deferred.resolve();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({msg: 'Unable to update user password', err: err});
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
return service;
|
||||
}]);
|
Loading…
Add table
Add a link
Reference in a new issue