diff --git a/api/bolt/migrate_dbversion4.go b/api/bolt/migrate_dbversion4.go new file mode 100644 index 000000000..ace64f51e --- /dev/null +++ b/api/bolt/migrate_dbversion4.go @@ -0,0 +1,16 @@ +package bolt + +func (m *Migrator) updateSettingsToVersion5() error { + legacySettings, err := m.SettingsService.Settings() + if err != nil { + return err + } + legacySettings.AllowBindMountsForRegularUsers = true + + err = m.SettingsService.StoreSettings(legacySettings) + if err != nil { + return err + } + + return nil +} diff --git a/api/bolt/migrator.go b/api/bolt/migrator.go index faba56157..2730fe7e9 100644 --- a/api/bolt/migrator.go +++ b/api/bolt/migrator.go @@ -65,6 +65,14 @@ func (m *Migrator) Migrate() error { } } + // https://github.com/portainer/portainer/issues/1235 + if m.CurrentDBVersion < 5 { + err := m.updateSettingsToVersion5() + if err != nil { + return err + } + } + err := m.VersionService.StoreDBVersion(portainer.DBVersion) if err != nil { return err diff --git a/api/cmd/portainer/main.go b/api/cmd/portainer/main.go index 0b536c895..ac8c7f57d 100644 --- a/api/cmd/portainer/main.go +++ b/api/cmd/portainer/main.go @@ -125,6 +125,7 @@ func initSettings(settingsService portainer.SettingsService, flags *portainer.CL portainer.LDAPSearchSettings{}, }, }, + AllowBindMountsForRegularUsers: true, } if *flags.Templates != "" { diff --git a/api/http/handler/settings.go b/api/http/handler/settings.go index 52e957f6d..8e3679e6f 100644 --- a/api/http/handler/settings.go +++ b/api/http/handler/settings.go @@ -45,18 +45,20 @@ func NewSettingsHandler(bouncer *security.RequestBouncer) *SettingsHandler { type ( publicSettingsResponse struct { - LogoURL string `json:"LogoURL"` - DisplayExternalContributors bool `json:"DisplayExternalContributors"` - AuthenticationMethod portainer.AuthenticationMethod `json:"AuthenticationMethod"` + LogoURL string `json:"LogoURL"` + DisplayExternalContributors bool `json:"DisplayExternalContributors"` + AuthenticationMethod portainer.AuthenticationMethod `json:"AuthenticationMethod"` + AllowBindMountsForRegularUsers bool `json:"AllowBindMountsForRegularUsers"` } putSettingsRequest struct { - TemplatesURL string `valid:"required"` - LogoURL string `valid:""` - BlackListedLabels []portainer.Pair `valid:""` - DisplayExternalContributors bool `valid:""` - AuthenticationMethod int `valid:"required"` - LDAPSettings portainer.LDAPSettings `valid:""` + TemplatesURL string `valid:"required"` + LogoURL string `valid:""` + BlackListedLabels []portainer.Pair `valid:""` + DisplayExternalContributors bool `valid:""` + AuthenticationMethod int `valid:"required"` + LDAPSettings portainer.LDAPSettings `valid:""` + AllowBindMountsForRegularUsers bool `valid:""` } putSettingsLDAPCheckRequest struct { @@ -85,9 +87,10 @@ func (handler *SettingsHandler) handleGetPublicSettings(w http.ResponseWriter, r } publicSettings := &publicSettingsResponse{ - LogoURL: settings.LogoURL, - DisplayExternalContributors: settings.DisplayExternalContributors, - AuthenticationMethod: settings.AuthenticationMethod, + LogoURL: settings.LogoURL, + DisplayExternalContributors: settings.DisplayExternalContributors, + AuthenticationMethod: settings.AuthenticationMethod, + AllowBindMountsForRegularUsers: settings.AllowBindMountsForRegularUsers, } encodeJSON(w, publicSettings, handler.Logger) @@ -109,11 +112,12 @@ func (handler *SettingsHandler) handlePutSettings(w http.ResponseWriter, r *http } settings := &portainer.Settings{ - TemplatesURL: req.TemplatesURL, - LogoURL: req.LogoURL, - BlackListedLabels: req.BlackListedLabels, - DisplayExternalContributors: req.DisplayExternalContributors, - LDAPSettings: req.LDAPSettings, + TemplatesURL: req.TemplatesURL, + LogoURL: req.LogoURL, + BlackListedLabels: req.BlackListedLabels, + DisplayExternalContributors: req.DisplayExternalContributors, + LDAPSettings: req.LDAPSettings, + AllowBindMountsForRegularUsers: req.AllowBindMountsForRegularUsers, } if req.AuthenticationMethod == 1 { diff --git a/api/portainer.go b/api/portainer.go index 539f9745b..bfbac90a9 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -70,12 +70,13 @@ type ( // Settings represents the application settings. Settings struct { - TemplatesURL string `json:"TemplatesURL"` - LogoURL string `json:"LogoURL"` - BlackListedLabels []Pair `json:"BlackListedLabels"` - DisplayExternalContributors bool `json:"DisplayExternalContributors"` - AuthenticationMethod AuthenticationMethod `json:"AuthenticationMethod"` - LDAPSettings LDAPSettings `json:"LDAPSettings"` + TemplatesURL string `json:"TemplatesURL"` + LogoURL string `json:"LogoURL"` + BlackListedLabels []Pair `json:"BlackListedLabels"` + DisplayExternalContributors bool `json:"DisplayExternalContributors"` + AuthenticationMethod AuthenticationMethod `json:"AuthenticationMethod"` + LDAPSettings LDAPSettings `json:"LDAPSettings"` + AllowBindMountsForRegularUsers bool `json:"AllowBindMountsForRegularUsers"` } // User represents a user account. @@ -348,7 +349,7 @@ const ( // APIVersion is the version number of the Portainer API. APIVersion = "1.14.2" // DBVersion is the version number of the Portainer database. - DBVersion = 4 + DBVersion = 5 // DefaultTemplatesURL represents the default URL for the templates definitions. DefaultTemplatesURL = "https://raw.githubusercontent.com/portainer/templates/master/templates.json" ) diff --git a/app/components/createContainer/createContainerController.js b/app/components/createContainer/createContainerController.js index a9da83b59..77c44cff7 100644 --- a/app/components/createContainer/createContainerController.js +++ b/app/components/createContainer/createContainerController.js @@ -1,8 +1,8 @@ // @@OLD_SERVICE_CONTROLLER: this service should be rewritten to use services. // See app/components/templates/templatesController.js as a reference. angular.module('createContainer', []) -.controller('CreateContainerController', ['$q', '$scope', '$state', '$transition$', '$filter', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'NetworkService', 'ResourceControlService', 'Authentication', 'Notifications', 'ContainerService', 'ImageService', 'FormValidator', 'ModalService', 'RegistryService', -function ($q, $scope, $state, $transition$, $filter, Container, ContainerHelper, Image, ImageHelper, Volume, NetworkService, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, FormValidator, ModalService, RegistryService) { +.controller('CreateContainerController', ['$q', '$scope', '$state', '$transition$', '$filter', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'NetworkService', 'ResourceControlService', 'Authentication', 'Notifications', 'ContainerService', 'ImageService', 'FormValidator', 'ModalService', 'RegistryService', 'SettingsService', +function ($q, $scope, $state, $transition$, $filter, Container, ContainerHelper, Image, ImageHelper, Volume, NetworkService, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, FormValidator, ModalService, RegistryService, SettingsService) { $scope.formValues = { alwaysPull: true, @@ -482,6 +482,16 @@ function ($q, $scope, $state, $transition$, $filter, Container, ContainerHelper, Notifications.error('Failure', e, 'Unable to retrieve running containers'); }); + SettingsService.publicSettings() + .then(function success(data) { + $scope.allowBindMounts = data.AllowBindMountsForRegularUsers; + }) + .catch(function error(err) { + Notifications.error('Failure', err, 'Unable to retrieve application settings'); + }); + + var userDetails = Authentication.getUserDetails(); + $scope.isAdmin = userDetails.role === 1 ? true : false; } function validateForm(accessControlData, isAdmin) { diff --git a/app/components/createContainer/createcontainer.html b/app/components/createContainer/createcontainer.html index fd282d5b8..fe61cd720 100644 --- a/app/components/createContainer/createcontainer.html +++ b/app/components/createContainer/createcontainer.html @@ -235,7 +235,7 @@ -
+
diff --git a/app/components/createService/createServiceController.js b/app/components/createService/createServiceController.js index c521d0aae..5b0cb0a62 100644 --- a/app/components/createService/createServiceController.js +++ b/app/components/createService/createServiceController.js @@ -1,8 +1,8 @@ // @@OLD_SERVICE_CONTROLLER: this service should be rewritten to use services. // See app/components/templates/templatesController.js as a reference. angular.module('createService', []) -.controller('CreateServiceController', ['$q', '$scope', '$state', '$timeout', 'Service', 'ServiceHelper', 'SecretHelper', 'SecretService', 'VolumeService', 'NetworkService', 'ImageHelper', 'LabelHelper', 'Authentication', 'ResourceControlService', 'Notifications', 'FormValidator', 'RegistryService', 'HttpRequestHelper', 'NodeService', -function ($q, $scope, $state, $timeout, Service, ServiceHelper, SecretHelper, SecretService, VolumeService, NetworkService, ImageHelper, LabelHelper, Authentication, ResourceControlService, Notifications, FormValidator, RegistryService, HttpRequestHelper, NodeService) { +.controller('CreateServiceController', ['$q', '$scope', '$state', '$timeout', 'Service', 'ServiceHelper', 'SecretHelper', 'SecretService', 'VolumeService', 'NetworkService', 'ImageHelper', 'LabelHelper', 'Authentication', 'ResourceControlService', 'Notifications', 'FormValidator', 'RegistryService', 'HttpRequestHelper', 'NodeService', 'SettingsService', +function ($q, $scope, $state, $timeout, Service, ServiceHelper, SecretHelper, SecretService, VolumeService, NetworkService, ImageHelper, LabelHelper, Authentication, ResourceControlService, Notifications, FormValidator, RegistryService, HttpRequestHelper, NodeService, SettingsService) { $scope.formValues = { Name: '', @@ -361,7 +361,8 @@ function ($q, $scope, $state, $timeout, Service, ServiceHelper, SecretHelper, Se volumes: VolumeService.volumes(), secrets: apiVersion >= 1.25 ? SecretService.secrets() : [], networks: NetworkService.networks(true, true, false, false), - nodes: NodeService.nodes() + nodes: NodeService.nodes(), + settings: SettingsService.publicSettings() }) .then(function success(data) { $scope.availableVolumes = data.volumes; @@ -379,6 +380,10 @@ function ($q, $scope, $state, $timeout, Service, ServiceHelper, SecretHelper, Se } else { $scope.state.sliderMaxCpu = 32; } + var settings = data.settings; + $scope.allowBindMounts = settings.AllowBindMountsForRegularUsers; + var userDetails = Authentication.getUserDetails(); + $scope.isAdmin = userDetails.role === 1 ? true : false; }) .catch(function error(err) { Notifications.error('Failure', err, 'Unable to initialize view'); diff --git a/app/components/createService/createservice.html b/app/components/createService/createservice.html index ad3c254ed..c026df3de 100644 --- a/app/components/createService/createservice.html +++ b/app/components/createService/createservice.html @@ -223,7 +223,7 @@
-
+
diff --git a/app/components/settings/settings.html b/app/components/settings/settings.html index f5215edb1..d73f4eed6 100644 --- a/app/components/settings/settings.html +++ b/app/components/settings/settings.html @@ -11,6 +11,22 @@
+ +
+ Security +
+
+
+ + +
+
+
Logo diff --git a/app/components/settings/settingsController.js b/app/components/settings/settingsController.js index f74f7def7..c75f97084 100644 --- a/app/components/settings/settingsController.js +++ b/app/components/settings/settingsController.js @@ -6,6 +6,7 @@ function ($scope, $state, Notifications, SettingsService, StateManager, DEFAULT_ customLogo: false, customTemplates: false, externalContributions: false, + restrictBindMounts: false, labelName: '', labelValue: '' }; @@ -39,6 +40,7 @@ function ($scope, $state, Notifications, SettingsService, StateManager, DEFAULT_ settings.TemplatesURL = DEFAULT_TEMPLATES_URL; } settings.DisplayExternalContributors = !$scope.formValues.externalContributions; + settings.AllowBindMountsForRegularUsers = !$scope.formValues.restrictBindMounts; updateSettings(settings, false); }; @@ -81,6 +83,7 @@ function ($scope, $state, Notifications, SettingsService, StateManager, DEFAULT_ $scope.formValues.customTemplates = true; } $scope.formValues.externalContributions = !settings.DisplayExternalContributors; + $scope.formValues.restrictBindMounts = !settings.AllowBindMountsForRegularUsers; }) .catch(function error(err) { Notifications.error('Failure', err, 'Unable to retrieve application settings'); diff --git a/app/components/templates/templates.html b/app/components/templates/templates.html index bf05cbbfb..8b7172b17 100644 --- a/app/components/templates/templates.html +++ b/app/components/templates/templates.html @@ -154,7 +154,7 @@
- +