Compare commits

..

54 Commits
1.24.2 ... demo

Author SHA1 Message Date
Anthony Lapenna
df3886fd25 Merge tag '1.24.1' into demo
Release 1.24.1
2020-07-23 11:43:41 +12:00
Anthony Lapenna
f2682d82b6 merge branch master into demo 2020-06-05 11:55:22 +12:00
Anthony Lapenna
d3576fe8e6 merge branch master into demo 2020-06-05 11:55:14 +12:00
Anthony Lapenna
f34d27df0f Merge branch 'master' into demo 2020-03-20 11:43:39 +13:00
Anthony Lapenna
f228b28639 Merge branch 'master' into demo 2020-02-18 13:49:34 +13:00
Anthony Lapenna
d7b4e4aba1 Merge branch 'master' into demo 2019-12-04 17:30:43 +13:00
Anthony Lapenna
7234c443e8 Merge branch 'develop' into demo 2019-10-11 12:17:32 +13:00
Anthony Lapenna
5b844fd40a fix(http): allow endpoint update operation on demo environment 2019-07-26 15:25:13 +12:00
Anthony Lapenna
7f2ef8fb06 Merge branch 'develop' into demo
# Conflicts:
#	api/cmd/portainer/main.go
2019-07-26 14:43:10 +12:00
Anthony Lapenna
9f817749c1 Merge branch 'develop' into demo 2019-06-04 16:12:31 +12:00
Anthony Lapenna
36198b57a5 Merge branch 'develop' into demo 2019-03-05 18:48:33 +13:00
Anthony Lapenna
70f9e37eab fix(api): fix missing endpoint identifier 2019-02-14 11:37:54 +13:00
Anthony Lapenna
24f69f0185 Merge branch 'develop' into demo 2019-01-31 15:23:48 +13:00
Anthony Lapenna
4a2e9a892d fix(demo): fix invalid demo data and trigger snapshot at startup 2018-12-12 20:17:53 +13:00
Anthony Lapenna
1e4bedde4b Merge branch 'develop' into demo 2018-12-12 17:59:46 +13:00
Anthony Lapenna
9db07e7f4e Merge branch 'develop' into demo 2018-12-12 14:52:50 +13:00
Anthony Lapenna
d4b8d9947d Merge branch 'master' into demo 2018-09-15 19:30:58 +08:00
Anthony Lapenna
9194ddcd03 Merge branch 'develop' into demo 2018-07-28 20:35:14 +02:00
Anthony Lapenna
e1edf37770 Merge branch 'develop' into demo
Conflicts:
	app/portainer/views/settings/settings.html
2018-07-27 18:03:52 +02:00
Anthony Lapenna
82b73c06b4 Merge branch 'develop' into demo 2018-06-25 16:44:43 +03:00
Anthony Lapenna
ae286998ab Merge branch 'develop' into demo
Conflicts:
	api/cmd/portainer/main.go
	api/http/handler/endpoint.go
	api/http/handler/user.go
2018-06-21 16:47:13 +03:00
Anthony Lapenna
ffc3ed67e2 Merge branch 'develop' into demo
Conflicts:
	api/cmd/portainer/main.go
2018-05-21 13:45:45 +02:00
Anthony Lapenna
6252be4a08 Merge branch 'develop' into demo
Conflicts:
	api/cmd/portainer/main.go
	app/portainer/views/settings/settings.html
2018-05-10 18:14:48 +02:00
Anthony Lapenna
75481d928e Merge branch 'develop' into demo 2018-04-02 08:57:02 +10:00
Anthony Lapenna
d65d65803f Merge branch 'develop' into demo 2018-03-11 20:30:52 +10:00
Anthony Lapenna
6a8fc253bd fix(demo): fix demo init data 2018-03-03 10:31:09 +10:00
Anthony Lapenna
1fa8921c11 Merge branch 'develop' into demo 2018-03-03 10:16:10 +10:00
Anthony Lapenna
ba3892aebf Merge branch 'develop' into demo 2018-02-08 10:22:47 +01:00
Anthony Lapenna
27fc700d6f Merge branch 'develop' into demo 2018-01-21 18:29:26 +01:00
Anthony Lapenna
3187cb0ada feat(api): add demo.portainer.io as the Public URL of the endpoint 2018-01-16 15:53:49 +01:00
Anthony Lapenna
b462a15921 Merge branch 'develop' into demo 2017-12-11 17:29:33 +01:00
Anthony Lapenna
11a646a076 Merge branch 'develop' into demo
Conflicts:
	app/components/endpoints/endpoints.html
	app/components/users/users.html
2017-12-10 11:32:26 +01:00
Anthony Lapenna
4c1edaf251 Merge branch 'develop' into demo 2017-11-26 11:18:14 +01:00
Anthony Lapenna
b45c4f8bea Merge branch 'develop' into demo
Conflicts:
	app/components/settings/settings.html
2017-11-13 12:07:24 +01:00
Anthony Lapenna
968f070b0b Merge branch 'develop' into demo 2017-11-08 09:33:45 +01:00
Anthony Lapenna
e679483ffd Merge branch 'develop' into demo 2017-10-15 20:50:36 +02:00
Anthony Lapenna
494ef6c392 Merge branch 'develop' into 'demo' 2017-10-15 20:27:59 +02:00
Anthony Lapenna
7951c1d150 Merge branch 'develop' into demo 2017-09-28 19:03:22 +02:00
Anthony Lapenna
0083bdb6a2 Merge branch 'master' into demo 2017-09-21 17:49:21 +02:00
Anthony Lapenna
e65881223d Merge branch 'develop' into 'demo' 2017-09-20 16:15:23 +02:00
Anthony Lapenna
f0c9058568 Merge branch 'develop' into 'demo' 2017-08-13 21:03:39 +02:00
Anthony Lapenna
afd654e4a8 Merge branch 'develop' into demo 2017-07-17 16:01:00 +02:00
Anthony Lapenna
0ef116d6b5 Merge branch 'develop' into demo 2017-07-13 18:19:14 +02:00
Anthony Lapenna
ee64a53782 Merge branch 'develop' into demo 2017-06-29 16:41:40 +02:00
Anthony Lapenna
4a0c899df5 Merge branch 'develop' into 'demo' 2017-06-20 13:24:29 +02:00
Anthony Lapenna
840603bb8c feat(settings): prevent the display of the URL field for custom templates 2017-06-05 09:56:04 +02:00
Anthony Lapenna
5fe79621a6 feat(settings): set a default value for custom templates 2017-06-05 09:51:01 +02:00
Anthony Lapenna
d3b9822105 Merge branch 'develop' into 'demo' 2017-06-05 09:45:06 +02:00
Anthony Lapenna
9163bcae25 Merge branch 'develop' into demo 2017-05-25 12:40:11 +02:00
Anthony Lapenna
17dad6e36f fix(api): prevent the deletion of the admin user 2017-05-23 21:51:30 +02:00
Anthony Lapenna
75b836453a merge branch 'develop' into demo 2017-05-23 21:06:50 +02:00
Anthony Lapenna
b8866d487f Merge branch 'develop' into demo 2017-04-06 11:02:27 +02:00
Anthony Lapenna
dfb3b3aaa1 Merge branch 'develop' into demo 2017-03-28 15:38:24 +02:00
Anthony Lapenna
09fdc9781c feat(demo): add demo restrictions 2017-03-14 08:47:57 +01:00
67 changed files with 434 additions and 539 deletions

View File

@@ -6,10 +6,10 @@ Some basic conventions for contributing to this project.
Please make sure that there aren't existing pull requests attempting to address the issue mentioned. Likewise, please check for issues related to update, as someone else may be working on the issue in a branch or fork.
* Please open a discussion in a new issue / existing issue to talk about the changes you'd like to bring
* Develop in a topic branch, not master/develop
- Please open a discussion in a new issue / existing issue to talk about the changes you'd like to bring
- Develop in a topic branch, not master/develop
When creating a new branch, prefix it with the *type* of the change (see section **Commit Message Format** below), the associated opened issue number, a dash and some text describing the issue (using dash as a separator).
When creating a new branch, prefix it with the _type_ of the change (see section **Commit Message Format** below), the associated opened issue number, a dash and some text describing the issue (using dash as a separator).
For example, if you work on a bugfix for the issue #361, you could name the branch `fix361-template-selection`.
@@ -37,14 +37,14 @@ Lines should not exceed 100 characters. This allows the message to be easier to
Must be one of the following:
* **feat**: A new feature
* **fix**: A bug fix
* **docs**: Documentation only changes
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
- **feat**: A new feature
- **fix**: A bug fix
- **docs**: Documentation only changes
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
semi-colons, etc)
* **refactor**: A code change that neither fixes a bug or adds a feature
* **test**: Adding missing tests
* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
- **refactor**: A code change that neither fixes a bug or adds a feature
- **test**: Adding missing tests
- **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
generation
### Scope
@@ -57,9 +57,9 @@ You can use the **area** label tag associated on the issue here (for `area/conta
The subject contains succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize first letter
* no dot (.) at the end
- use the imperative, present tense: "change" not "changed" nor "changes"
- don't capitalize first letter
- no dot (.) at the end
## Contribution process

View File

@@ -1,12 +0,0 @@
package migrator
func (m *Migrator) updateSettingsToDBVersion25() error {
legacySettings, err := m.settingsService.Settings()
if err != nil {
return err
}
legacySettings.AllowContainerCapabilitiesForRegularUsers = true
return m.settingsService.UpdateSettings(legacySettings)
}

View File

@@ -2,7 +2,7 @@ package migrator
import (
"github.com/boltdb/bolt"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/endpoint"
"github.com/portainer/portainer/api/bolt/endpointgroup"
"github.com/portainer/portainer/api/bolt/endpointrelation"
@@ -330,13 +330,5 @@ func (m *Migrator) Migrate() error {
}
}
// Portainer 1.24.2
if m.currentDBVersion < 25 {
err := m.updateSettingsToDBVersion25()
if err != nil {
return err
}
}
return m.versionService.StoreDBVersion(portainer.DBVersion)
}

View File

@@ -70,6 +70,45 @@ func initStore(dataStorePath string, fileService portainer.FileService) *bolt.St
return store
}
func initDemoData(store *bolt.Store, cryptoService portainer.CryptoService) error {
password, err := cryptoService.Hash("tryportainer")
if err != nil {
return err
}
admin := &portainer.User{
Username: "admin",
Password: password,
Role: portainer.AdministratorRole,
}
err = store.UserService.CreateUser(admin)
if err != nil {
return err
}
localEndpoint := &portainer.Endpoint{
ID: portainer.EndpointID(1),
Name: "local",
URL: "unix:///var/run/docker.sock",
PublicURL: "demo.portainer.io",
TLSConfig: portainer.TLSConfiguration{
TLS: false,
},
AuthorizedUsers: []portainer.UserID{},
AuthorizedTeams: []portainer.TeamID{},
Extensions: []portainer.EndpointExtension{},
Tags: []string{},
}
err = store.EndpointService.CreateEndpoint(localEndpoint)
if err != nil {
return err
}
return nil
}
func initComposeStackManager(dataStorePath string, reverseTunnelService portainer.ReverseTunnelService) portainer.ComposeStackManager {
return libcompose.NewComposeStackManager(dataStorePath, reverseTunnelService)
}
@@ -269,17 +308,16 @@ func initSettings(settingsService portainer.SettingsService, flags *portainer.CL
portainer.LDAPGroupSearchSettings{},
},
},
OAuthSettings: portainer.OAuthSettings{},
AllowBindMountsForRegularUsers: true,
AllowPrivilegedModeForRegularUsers: true,
AllowVolumeBrowserForRegularUsers: false,
AllowDeviceMappingForRegularUsers: true,
AllowStackManagementForRegularUsers: true,
AllowContainerCapabilitiesForRegularUsers: true,
EnableHostManagementFeatures: false,
AllowHostNamespaceForRegularUsers: true,
SnapshotInterval: *flags.SnapshotInterval,
EdgeAgentCheckinInterval: portainer.DefaultEdgeAgentCheckinIntervalInSeconds,
OAuthSettings: portainer.OAuthSettings{},
AllowBindMountsForRegularUsers: true,
AllowPrivilegedModeForRegularUsers: true,
AllowVolumeBrowserForRegularUsers: false,
AllowDeviceMappingForRegularUsers: true,
AllowStackManagementForRegularUsers: true,
EnableHostManagementFeatures: false,
AllowHostNamespaceForRegularUsers: true,
SnapshotInterval: *flags.SnapshotInterval,
EdgeAgentCheckinInterval: portainer.DefaultEdgeAgentCheckinIntervalInSeconds,
}
if *flags.Templates != "" {
@@ -533,6 +571,8 @@ func main() {
cryptoService := initCryptoService()
initDemoData(store, cryptoService)
digitalSignatureService := initDigitalSignatureService()
err := initKeyPair(fileService, digitalSignatureService)

View File

@@ -89,6 +89,11 @@ const (
ErrUndefinedTLSFileType = Error("Undefined TLS file type")
)
// Demo errors.
const (
ErrNotAvailableInDemo = Error("This feature is not available in the demo version of Portainer")
)
// Extension errors.
const (
ErrExtensionAlreadyEnabled = Error("This extension is already enabled")

View File

@@ -12,7 +12,7 @@ import (
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/api/http/client"
)
@@ -261,13 +261,13 @@ func (handler *Handler) createEdgeAgentEndpoint(payload *endpointCreatePayload)
TLSConfig: portainer.TLSConfiguration{
TLS: false,
},
UserAccessPolicies: portainer.UserAccessPolicies{},
TeamAccessPolicies: portainer.TeamAccessPolicies{},
Extensions: []portainer.EndpointExtension{},
TagIDs: payload.TagIDs,
Status: portainer.EndpointStatusUp,
Snapshots: []portainer.Snapshot{},
EdgeKey: edgeKey,
AuthorizedUsers: []portainer.UserID{},
AuthorizedTeams: []portainer.TeamID{},
Extensions: []portainer.EndpointExtension{},
TagIDs: payload.TagIDs,
Status: portainer.EndpointStatusUp,
Snapshots: []portainer.Snapshot{},
EdgeKey: edgeKey,
}
err = handler.saveEndpointAndUpdateAuthorizations(endpoint)

View File

@@ -21,6 +21,10 @@ func (handler *Handler) endpointDelete(w http.ResponseWriter, r *http.Request) *
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
if endpointID == 1 {
return &httperror.HandlerError{http.StatusForbidden, "This feature is not available in the demo version of Portainer", portainer.ErrNotAvailableInDemo}
}
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}

View File

@@ -10,19 +10,18 @@ import (
)
type publicSettingsResponse struct {
LogoURL string `json:"LogoURL"`
AuthenticationMethod portainer.AuthenticationMethod `json:"AuthenticationMethod"`
AllowBindMountsForRegularUsers bool `json:"AllowBindMountsForRegularUsers"`
AllowPrivilegedModeForRegularUsers bool `json:"AllowPrivilegedModeForRegularUsers"`
AllowVolumeBrowserForRegularUsers bool `json:"AllowVolumeBrowserForRegularUsers"`
EnableHostManagementFeatures bool `json:"EnableHostManagementFeatures"`
EnableEdgeComputeFeatures bool `json:"EnableEdgeComputeFeatures"`
ExternalTemplates bool `json:"ExternalTemplates"`
OAuthLoginURI string `json:"OAuthLoginURI"`
AllowStackManagementForRegularUsers bool `json:"AllowStackManagementForRegularUsers"`
AllowHostNamespaceForRegularUsers bool `json:"AllowHostNamespaceForRegularUsers"`
AllowDeviceMappingForRegularUsers bool `json:"AllowDeviceMappingForRegularUsers"`
AllowContainerCapabilitiesForRegularUsers bool `json:"AllowContainerCapabilitiesForRegularUsers"`
LogoURL string `json:"LogoURL"`
AuthenticationMethod portainer.AuthenticationMethod `json:"AuthenticationMethod"`
AllowBindMountsForRegularUsers bool `json:"AllowBindMountsForRegularUsers"`
AllowPrivilegedModeForRegularUsers bool `json:"AllowPrivilegedModeForRegularUsers"`
AllowVolumeBrowserForRegularUsers bool `json:"AllowVolumeBrowserForRegularUsers"`
EnableHostManagementFeatures bool `json:"EnableHostManagementFeatures"`
EnableEdgeComputeFeatures bool `json:"EnableEdgeComputeFeatures"`
ExternalTemplates bool `json:"ExternalTemplates"`
OAuthLoginURI string `json:"OAuthLoginURI"`
AllowStackManagementForRegularUsers bool `json:"AllowStackManagementForRegularUsers"`
AllowHostNamespaceForRegularUsers bool `json:"AllowHostNamespaceForRegularUsers"`
AllowDeviceMappingForRegularUsers bool `json:"AllowDeviceMappingForRegularUsers"`
}
// GET request on /api/settings/public
@@ -33,17 +32,16 @@ func (handler *Handler) settingsPublic(w http.ResponseWriter, r *http.Request) *
}
publicSettings := &publicSettingsResponse{
LogoURL: settings.LogoURL,
AuthenticationMethod: settings.AuthenticationMethod,
AllowBindMountsForRegularUsers: settings.AllowBindMountsForRegularUsers,
AllowPrivilegedModeForRegularUsers: settings.AllowPrivilegedModeForRegularUsers,
AllowVolumeBrowserForRegularUsers: settings.AllowVolumeBrowserForRegularUsers,
EnableHostManagementFeatures: settings.EnableHostManagementFeatures,
EnableEdgeComputeFeatures: settings.EnableEdgeComputeFeatures,
AllowHostNamespaceForRegularUsers: settings.AllowHostNamespaceForRegularUsers,
AllowStackManagementForRegularUsers: settings.AllowStackManagementForRegularUsers,
ExternalTemplates: false,
AllowContainerCapabilitiesForRegularUsers: settings.AllowContainerCapabilitiesForRegularUsers,
LogoURL: settings.LogoURL,
AuthenticationMethod: settings.AuthenticationMethod,
AllowBindMountsForRegularUsers: settings.AllowBindMountsForRegularUsers,
AllowPrivilegedModeForRegularUsers: settings.AllowPrivilegedModeForRegularUsers,
AllowVolumeBrowserForRegularUsers: settings.AllowVolumeBrowserForRegularUsers,
EnableHostManagementFeatures: settings.EnableHostManagementFeatures,
EnableEdgeComputeFeatures: settings.EnableEdgeComputeFeatures,
AllowHostNamespaceForRegularUsers: settings.AllowHostNamespaceForRegularUsers,
AllowStackManagementForRegularUsers: settings.AllowStackManagementForRegularUsers,
ExternalTemplates: false,
OAuthLoginURI: fmt.Sprintf("%s?response_type=code&client_id=%s&redirect_uri=%s&scope=%s&prompt=login",
settings.OAuthSettings.AuthorizationURI,
settings.OAuthSettings.ClientID,

View File

@@ -12,23 +12,22 @@ import (
)
type settingsUpdatePayload struct {
LogoURL *string
BlackListedLabels []portainer.Pair
AuthenticationMethod *int
LDAPSettings *portainer.LDAPSettings
OAuthSettings *portainer.OAuthSettings
AllowBindMountsForRegularUsers *bool
AllowPrivilegedModeForRegularUsers *bool
AllowVolumeBrowserForRegularUsers *bool
EnableHostManagementFeatures *bool
SnapshotInterval *string
TemplatesURL *string
EdgeAgentCheckinInterval *int
EnableEdgeComputeFeatures *bool
AllowStackManagementForRegularUsers *bool
AllowHostNamespaceForRegularUsers *bool
AllowDeviceMappingForRegularUsers *bool
AllowContainerCapabilitiesForRegularUsers *bool
LogoURL *string
BlackListedLabels []portainer.Pair
AuthenticationMethod *int
LDAPSettings *portainer.LDAPSettings
OAuthSettings *portainer.OAuthSettings
AllowBindMountsForRegularUsers *bool
AllowPrivilegedModeForRegularUsers *bool
AllowVolumeBrowserForRegularUsers *bool
EnableHostManagementFeatures *bool
SnapshotInterval *string
TemplatesURL *string
EdgeAgentCheckinInterval *int
EnableEdgeComputeFeatures *bool
AllowStackManagementForRegularUsers *bool
AllowHostNamespaceForRegularUsers *bool
AllowDeviceMappingForRegularUsers *bool
}
func (payload *settingsUpdatePayload) Validate(r *http.Request) error {
@@ -126,10 +125,6 @@ func (handler *Handler) settingsUpdate(w http.ResponseWriter, r *http.Request) *
settings.AllowHostNamespaceForRegularUsers = *payload.AllowHostNamespaceForRegularUsers
}
if payload.AllowContainerCapabilitiesForRegularUsers != nil {
settings.AllowContainerCapabilitiesForRegularUsers = *payload.AllowContainerCapabilitiesForRegularUsers
}
if payload.SnapshotInterval != nil && *payload.SnapshotInterval != settings.SnapshotInterval {
err := handler.updateSnapshotInterval(settings, *payload.SnapshotInterval)
if err != nil {

View File

@@ -302,12 +302,9 @@ func (handler *Handler) createComposeDeployConfig(r *http.Request, stack *portai
}
filteredRegistries := security.FilterRegistries(registries, securityContext)
var user *portainer.User
if !handler.authDisabled {
user, err = handler.UserService.User(securityContext.UserID)
if err != nil {
return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to load user information from the database", err}
}
user, err := handler.UserService.User(securityContext.UserID)
if err != nil {
return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to load user information from the database", err}
}
config := &composeStackDeploymentConfig{
@@ -333,29 +330,26 @@ func (handler *Handler) deployComposeStack(config *composeStackDeploymentConfig)
return err
}
if !handler.authDisabled {
isAdminOrEndpointAdmin, err := handler.userIsAdminOrEndpointAdmin(config.user, config.endpoint.ID)
isAdminOrEndpointAdmin, err := handler.userIsAdminOrEndpointAdmin(config.user, config.endpoint.ID)
if err != nil {
return err
}
if (!settings.AllowBindMountsForRegularUsers ||
!settings.AllowPrivilegedModeForRegularUsers ||
!settings.AllowHostNamespaceForRegularUsers ||
!settings.AllowDeviceMappingForRegularUsers) && !isAdminOrEndpointAdmin {
composeFilePath := path.Join(config.stack.ProjectPath, config.stack.EntryPoint)
stackContent, err := handler.FileService.GetFileContent(composeFilePath)
if err != nil {
return err
}
if (!settings.AllowBindMountsForRegularUsers ||
!settings.AllowPrivilegedModeForRegularUsers ||
!settings.AllowHostNamespaceForRegularUsers ||
!settings.AllowDeviceMappingForRegularUsers ||
!settings.AllowContainerCapabilitiesForRegularUsers) && !isAdminOrEndpointAdmin {
composeFilePath := path.Join(config.stack.ProjectPath, config.stack.EntryPoint)
stackContent, err := handler.FileService.GetFileContent(composeFilePath)
if err != nil {
return err
}
err = handler.isValidStackFile(stackContent, settings)
if err != nil {
return err
}
err = handler.isValidStackFile(stackContent, settings)
if err != nil {
return err
}
}

View File

@@ -311,12 +311,9 @@ func (handler *Handler) createSwarmDeployConfig(r *http.Request, stack *portaine
}
filteredRegistries := security.FilterRegistries(registries, securityContext)
var user *portainer.User
if !handler.authDisabled {
user, err = handler.UserService.User(securityContext.UserID)
if err != nil {
return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to load user information from the database", err}
}
user, err := handler.UserService.User(securityContext.UserID)
if err != nil {
return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to load user information from the database", err}
}
config := &swarmStackDeploymentConfig{
@@ -338,25 +335,23 @@ func (handler *Handler) deploySwarmStack(config *swarmStackDeploymentConfig) err
return err
}
if !handler.authDisabled {
isAdminOrEndpointAdmin, err := handler.userIsAdminOrEndpointAdmin(config.user, config.endpoint.ID)
isAdminOrEndpointAdmin, err := handler.userIsAdminOrEndpointAdmin(config.user, config.endpoint.ID)
if err != nil {
return err
}
if !settings.AllowBindMountsForRegularUsers && !isAdminOrEndpointAdmin {
composeFilePath := path.Join(config.stack.ProjectPath, config.stack.EntryPoint)
stackContent, err := handler.FileService.GetFileContent(composeFilePath)
if err != nil {
return err
}
if !settings.AllowBindMountsForRegularUsers && !isAdminOrEndpointAdmin {
composeFilePath := path.Join(config.stack.ProjectPath, config.stack.EntryPoint)
stackContent, err := handler.FileService.GetFileContent(composeFilePath)
if err != nil {
return err
}
err = handler.isValidStackFile(stackContent, settings)
if err != nil {
return err
}
err = handler.isValidStackFile(stackContent, settings)
if err != nil {
return err
}
}

View File

@@ -16,8 +16,6 @@ type Handler struct {
stackCreationMutex *sync.Mutex
stackDeletionMutex *sync.Mutex
requestBouncer *security.RequestBouncer
authDisabled bool
*mux.Router
FileService portainer.FileService
GitService portainer.GitService
@@ -34,10 +32,9 @@ type Handler struct {
}
// NewHandler creates a handler to manage stack operations.
func NewHandler(bouncer *security.RequestBouncer, authDisabled bool) *Handler {
func NewHandler(bouncer *security.RequestBouncer) *Handler {
h := &Handler{
Router: mux.NewRouter(),
authDisabled: authDisabled,
stackCreationMutex: &sync.Mutex{},
stackDeletionMutex: &sync.Mutex{},
requestBouncer: bouncer,
@@ -60,7 +57,7 @@ func NewHandler(bouncer *security.RequestBouncer, authDisabled bool) *Handler {
}
func (handler *Handler) userCanAccessStack(securityContext *security.RestrictedRequestContext, endpointID portainer.EndpointID, resourceControl *portainer.ResourceControl) (bool, error) {
if securityContext.IsAdmin || handler.authDisabled {
if securityContext.IsAdmin {
return true, nil
}
@@ -93,7 +90,7 @@ func (handler *Handler) userCanAccessStack(securityContext *security.RestrictedR
}
func (handler *Handler) userCanCreateStack(securityContext *security.RestrictedRequestContext, endpointID portainer.EndpointID) (bool, error) {
if securityContext.IsAdmin || handler.authDisabled {
if securityContext.IsAdmin {
return true, nil
}

View File

@@ -164,10 +164,6 @@ func (handler *Handler) isValidStackFile(stackFileContent []byte, settings *port
if !settings.AllowDeviceMappingForRegularUsers && service.Devices != nil && len(service.Devices) > 0 {
return errors.New("device mapping disabled for non administrator users")
}
if !settings.AllowContainerCapabilitiesForRegularUsers && (len(service.CapAdd) > 0 || len(service.CapDrop) > 0) {
return errors.New("container capabilities disabled for non administrator users")
}
}
return nil

View File

@@ -22,6 +22,10 @@ func (handler *Handler) userDelete(w http.ResponseWriter, r *http.Request) *http
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve user authentication token", err}
}
if userID == 1 {
return &httperror.HandlerError{http.StatusForbidden, "This feature is not available in the demo version of Portainer", portainer.ErrNotAvailableInDemo}
}
if tokenData.ID == portainer.UserID(userID) {
return &httperror.HandlerError{http.StatusForbidden, "Cannot remove your own user account. Contact another administrator", portainer.ErrAdminCannotRemoveSelf}
}

View File

@@ -29,6 +29,10 @@ func (handler *Handler) userUpdate(w http.ResponseWriter, r *http.Request) *http
return &httperror.HandlerError{http.StatusBadRequest, "Invalid user identifier route variable", err}
}
if userID == 1 {
return &httperror.HandlerError{http.StatusForbidden, "This feature is not available in the demo version of Portainer", portainer.ErrNotAvailableInDemo}
}
tokenData, err := security.RetrieveTokenData(r)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve user authentication token", err}

View File

@@ -33,6 +33,10 @@ func (handler *Handler) userUpdatePassword(w http.ResponseWriter, r *http.Reques
return &httperror.HandlerError{http.StatusBadRequest, "Invalid user identifier route variable", err}
}
if userID == 1 {
return &httperror.HandlerError{http.StatusForbidden, "This feature is not available in the demo version of Portainer", portainer.ErrNotAvailableInDemo}
}
tokenData, err := security.RetrieveTokenData(r)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve user authentication token", err}

View File

@@ -68,7 +68,6 @@ func (factory *ProxyFactory) newDockerHTTPProxy(endpoint *portainer.Endpoint) (h
ExtensionService: factory.extensionService,
SignatureService: factory.signatureService,
DockerClientFactory: factory.dockerClientFactory,
AuthDisabled: factory.authDisabled,
}
dockerTransport, err := docker.NewTransport(transportParameters, httpTransport)

View File

@@ -159,9 +159,6 @@ func (transport *Transport) decorateContainerCreationOperation(request *http.Req
Privileged bool `json:"Privileged"`
PidMode string `json:"PidMode"`
Devices []interface{} `json:"Devices"`
CapAdd []string `json:"CapAdd"`
CapDrop []string `json:"CapDrop"`
Binds []string `json:"Binds"`
} `json:"HostConfig"`
}
@@ -174,12 +171,25 @@ func (transport *Transport) decorateContainerCreationOperation(request *http.Req
return nil, err
}
isAdminOrEndpointAdmin, err := transport.isAdminOrEndpointAdmin(request)
user, err := transport.userService.User(tokenData.ID)
if err != nil {
return nil, err
}
if !isAdminOrEndpointAdmin {
rbacExtension, err := transport.extensionService.Extension(portainer.RBACExtension)
if err != nil && err != portainer.ErrObjectNotFound {
return nil, err
}
endpointResourceAccess := false
_, ok := user.EndpointAuthorizations[portainer.EndpointID(transport.endpoint.ID)][portainer.EndpointResourcesAccess]
if ok {
endpointResourceAccess = true
}
isAdmin := (rbacExtension != nil && endpointResourceAccess) || tokenData.Role == portainer.AdministratorRole
if !isAdmin {
settings, err := transport.settingsService.Settings()
if err != nil {
return nil, err
@@ -187,9 +197,7 @@ func (transport *Transport) decorateContainerCreationOperation(request *http.Req
if !settings.AllowPrivilegedModeForRegularUsers ||
!settings.AllowHostNamespaceForRegularUsers ||
!settings.AllowDeviceMappingForRegularUsers ||
!settings.AllowContainerCapabilitiesForRegularUsers ||
!settings.AllowBindMountsForRegularUsers {
!settings.AllowDeviceMappingForRegularUsers {
body, err := ioutil.ReadAll(request.Body)
if err != nil {
@@ -202,26 +210,18 @@ func (transport *Transport) decorateContainerCreationOperation(request *http.Req
return nil, err
}
if !settings.AllowPrivilegedModeForRegularUsers && partialContainer.HostConfig.Privileged {
if partialContainer.HostConfig.Privileged {
return forbiddenResponse, errors.New("forbidden to use privileged mode")
}
if !settings.AllowHostNamespaceForRegularUsers && partialContainer.HostConfig.PidMode == "host" {
if partialContainer.HostConfig.PidMode == "host" {
return forbiddenResponse, errors.New("forbidden to use pid host namespace")
}
if !settings.AllowDeviceMappingForRegularUsers && len(partialContainer.HostConfig.Devices) > 0 {
if len(partialContainer.HostConfig.Devices) > 0 {
return nil, errors.New("forbidden to use device mapping")
}
if !settings.AllowContainerCapabilitiesForRegularUsers && (len(partialContainer.HostConfig.CapAdd) > 0 || len(partialContainer.HostConfig.CapDrop) > 0) {
return nil, errors.New("forbidden to use container capabilities")
}
if !settings.AllowBindMountsForRegularUsers && (len(partialContainer.HostConfig.Binds) > 0) {
return forbiddenResponse, errors.New("forbidden to use bind mounts")
}
request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
}
}

View File

@@ -1,11 +1,7 @@
package docker
import (
"bytes"
"context"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"github.com/docker/docker/api/types"
@@ -88,54 +84,3 @@ func selectorServiceLabels(responseObject map[string]interface{}) map[string]int
}
return nil
}
func (transport *Transport) decorateServiceCreationOperation(request *http.Request) (*http.Response, error) {
type PartialService struct {
TaskTemplate struct {
ContainerSpec struct {
Mounts []struct {
Type string
}
}
}
}
forbiddenResponse := &http.Response{
StatusCode: http.StatusForbidden,
}
isAdminOrEndpointAdmin, err := transport.isAdminOrEndpointAdmin(request)
if err != nil {
return nil, err
}
if !isAdminOrEndpointAdmin {
settings, err := transport.settingsService.Settings()
if err != nil {
return nil, err
}
body, err := ioutil.ReadAll(request.Body)
if err != nil {
return nil, err
}
partialService := &PartialService{}
err = json.Unmarshal(body, partialService)
if err != nil {
return nil, err
}
if !settings.AllowBindMountsForRegularUsers && (len(partialService.TaskTemplate.ContainerSpec.Mounts) > 0) {
for _, mount := range partialService.TaskTemplate.ContainerSpec.Mounts {
if mount.Type == "bind" {
return forbiddenResponse, errors.New("forbidden to use bind mounts")
}
}
}
request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
}
return transport.replaceRegistryAuthenticationHeader(request)
}

View File

@@ -38,7 +38,6 @@ type (
extensionService portainer.ExtensionService
dockerClient *client.Client
dockerClientFactory *docker.ClientFactory
authDisabled bool
}
// TransportParameters is used to create a new Transport
@@ -55,7 +54,6 @@ type (
ReverseTunnelService portainer.ReverseTunnelService
ExtensionService portainer.ExtensionService
DockerClientFactory *docker.ClientFactory
AuthDisabled bool
}
restrictedDockerOperationContext struct {
@@ -96,7 +94,6 @@ func NewTransport(parameters *TransportParameters, httpTransport *http.Transport
dockerClientFactory: parameters.DockerClientFactory,
HTTPTransport: httpTransport,
dockerClient: dockerClient,
authDisabled: parameters.AuthDisabled,
}
return transport, nil
@@ -248,7 +245,7 @@ func (transport *Transport) proxyContainerRequest(request *http.Request) (*http.
func (transport *Transport) proxyServiceRequest(request *http.Request) (*http.Response, error) {
switch requestPath := request.URL.Path; requestPath {
case "/services/create":
return transport.decorateServiceCreationOperation(request)
return transport.replaceRegistryAuthenticationHeader(request)
case "/services":
return transport.rewriteOperation(request, transport.serviceListOperation)
@@ -659,6 +656,7 @@ func (transport *Transport) createRegistryAccessContext(request *http.Request) (
return nil, err
}
accessContext := &registryAccessContext{
isAdmin: true,
userID: tokenData.ID,
@@ -736,36 +734,3 @@ func (transport *Transport) createOperationContext(request *http.Request) (*rest
return operationContext, nil
}
func (transport *Transport) isAdminOrEndpointAdmin(request *http.Request) (bool, error) {
if transport.authDisabled {
return true, nil
}
tokenData, err := security.RetrieveTokenData(request)
if err != nil {
return false, err
}
if tokenData.Role == portainer.AdministratorRole {
return true, nil
}
user, err := transport.userService.User(tokenData.ID)
if err != nil {
return false, err
}
rbacExtension, err := transport.extensionService.Extension(portainer.RBACExtension)
if err != nil && err != portainer.ErrObjectNotFound {
return false, err
}
if rbacExtension == nil {
return false, nil
}
_, endpointResourceAccess := user.EndpointAuthorizations[portainer.EndpointID(transport.endpoint.ID)][portainer.EndpointResourcesAccess]
return endpointResourceAccess, nil
}

View File

@@ -24,7 +24,6 @@ func (factory ProxyFactory) newOSBasedLocalProxy(path string, endpoint *portaine
ExtensionService: factory.extensionService,
SignatureService: factory.signatureService,
DockerClientFactory: factory.dockerClientFactory,
AuthDisabled: factory.authDisabled,
}
proxy := &dockerLocalProxy{}

View File

@@ -25,7 +25,6 @@ func (factory ProxyFactory) newOSBasedLocalProxy(path string, endpoint *portaine
ExtensionService: factory.extensionService,
SignatureService: factory.signatureService,
DockerClientFactory: factory.dockerClientFactory,
AuthDisabled: factory.authDisabled,
}
proxy := &dockerLocalProxy{}

View File

@@ -6,7 +6,7 @@ import (
"net/http/httputil"
"net/url"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/docker"
)
@@ -32,7 +32,6 @@ type (
reverseTunnelService portainer.ReverseTunnelService
extensionService portainer.ExtensionService
dockerClientFactory *docker.ClientFactory
authDisabled bool
}
// ProxyFactoryParameters is used to create a new ProxyFactory
@@ -48,7 +47,6 @@ type (
ReverseTunnelService portainer.ReverseTunnelService
ExtensionService portainer.ExtensionService
DockerClientFactory *docker.ClientFactory
AuthDisabled bool
}
)
@@ -66,7 +64,6 @@ func NewProxyFactory(parameters *ProxyFactoryParameters) *ProxyFactory {
reverseTunnelService: parameters.ReverseTunnelService,
extensionService: parameters.ExtensionService,
dockerClientFactory: parameters.DockerClientFactory,
authDisabled: parameters.AuthDisabled,
}
}

View File

@@ -4,8 +4,8 @@ import (
"net/http"
"strconv"
cmap "github.com/orcaman/concurrent-map"
portainer "github.com/portainer/portainer/api"
"github.com/orcaman/concurrent-map"
"github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/docker"
"github.com/portainer/portainer/api/http/proxy/factory"
)
@@ -34,7 +34,6 @@ type (
ReverseTunnelService portainer.ReverseTunnelService
ExtensionService portainer.ExtensionService
DockerClientFactory *docker.ClientFactory
AuthDisabled bool
}
)
@@ -52,7 +51,6 @@ func NewManager(parameters *ManagerParams) *Manager {
ReverseTunnelService: parameters.ReverseTunnelService,
ExtensionService: parameters.ExtensionService,
DockerClientFactory: parameters.DockerClientFactory,
AuthDisabled: parameters.AuthDisabled,
}
return &Manager{

View File

@@ -104,7 +104,6 @@ func (server *Server) Start() error {
ReverseTunnelService: server.ReverseTunnelService,
ExtensionService: server.ExtensionService,
DockerClientFactory: server.DockerClientFactory,
AuthDisabled: server.AuthDisabled,
}
proxyManager := proxy.NewManager(proxyManagerParameters)
@@ -248,7 +247,7 @@ func (server *Server) Start() error {
settingsHandler.ExtensionService = server.ExtensionService
settingsHandler.AuthorizationService = authorizationService
var stackHandler = stacks.NewHandler(requestBouncer, server.AuthDisabled)
var stackHandler = stacks.NewHandler(requestBouncer)
stackHandler.FileService = server.FileService
stackHandler.StackService = server.StackService
stackHandler.EndpointService = server.EndpointService

View File

@@ -420,23 +420,22 @@ type (
// Settings represents the application settings
Settings struct {
LogoURL string `json:"LogoURL"`
BlackListedLabels []Pair `json:"BlackListedLabels"`
AuthenticationMethod AuthenticationMethod `json:"AuthenticationMethod"`
LDAPSettings LDAPSettings `json:"LDAPSettings"`
OAuthSettings OAuthSettings `json:"OAuthSettings"`
AllowBindMountsForRegularUsers bool `json:"AllowBindMountsForRegularUsers"`
AllowPrivilegedModeForRegularUsers bool `json:"AllowPrivilegedModeForRegularUsers"`
AllowVolumeBrowserForRegularUsers bool `json:"AllowVolumeBrowserForRegularUsers"`
SnapshotInterval string `json:"SnapshotInterval"`
TemplatesURL string `json:"TemplatesURL"`
EnableHostManagementFeatures bool `json:"EnableHostManagementFeatures"`
EdgeAgentCheckinInterval int `json:"EdgeAgentCheckinInterval"`
EnableEdgeComputeFeatures bool `json:"EnableEdgeComputeFeatures"`
AllowStackManagementForRegularUsers bool `json:"AllowStackManagementForRegularUsers"`
AllowHostNamespaceForRegularUsers bool `json:"AllowHostNamespaceForRegularUsers"`
AllowDeviceMappingForRegularUsers bool `json:"AllowDeviceMappingForRegularUsers"`
AllowContainerCapabilitiesForRegularUsers bool `json:"AllowContainerCapabilitiesForRegularUsers"`
LogoURL string `json:"LogoURL"`
BlackListedLabels []Pair `json:"BlackListedLabels"`
AuthenticationMethod AuthenticationMethod `json:"AuthenticationMethod"`
LDAPSettings LDAPSettings `json:"LDAPSettings"`
OAuthSettings OAuthSettings `json:"OAuthSettings"`
AllowBindMountsForRegularUsers bool `json:"AllowBindMountsForRegularUsers"`
AllowPrivilegedModeForRegularUsers bool `json:"AllowPrivilegedModeForRegularUsers"`
AllowVolumeBrowserForRegularUsers bool `json:"AllowVolumeBrowserForRegularUsers"`
SnapshotInterval string `json:"SnapshotInterval"`
TemplatesURL string `json:"TemplatesURL"`
EnableHostManagementFeatures bool `json:"EnableHostManagementFeatures"`
EdgeAgentCheckinInterval int `json:"EdgeAgentCheckinInterval"`
EnableEdgeComputeFeatures bool `json:"EnableEdgeComputeFeatures"`
AllowStackManagementForRegularUsers bool `json:"AllowStackManagementForRegularUsers"`
AllowHostNamespaceForRegularUsers bool `json:"AllowHostNamespaceForRegularUsers"`
AllowDeviceMappingForRegularUsers bool `json:"AllowDeviceMappingForRegularUsers"`
// Deprecated fields
DisplayDonationHeader bool
@@ -1011,7 +1010,7 @@ type (
const (
// APIVersion is the version number of the Portainer API
APIVersion = "1.24.2"
APIVersion = "1.24.1"
// DBVersion is the version number of the Portainer database
DBVersion = 24
// AssetsServerURL represents the URL of the Portainer asset server

View File

@@ -1,81 +1,82 @@
import _ from 'lodash-es';
angular.module('portainer.docker')
.controller('ContainerNetworksDatatableController', ['$scope', '$controller', 'DatatableService',
function ($scope, $controller, DatatableService) {
angular.module('portainer.docker').controller('ContainerNetworksDatatableController', [
'$scope',
'$controller',
'DatatableService',
function ($scope, $controller, DatatableService) {
angular.extend(this, $controller('GenericDatatableController', { $scope: $scope }));
this.state = Object.assign(this.state, {
expandedItems: [],
expandAll: true,
});
angular.extend(this, $controller('GenericDatatableController', { $scope: $scope }));
this.state = Object.assign(this.state, {
expandedItems: [],
expandAll: true
});
this.expandItem = function (item, expanded) {
if (!this.itemCanExpand(item)) {
return;
}
item.Expanded = expanded;
if (!expanded) {
item.Highlighted = false;
}
if (!item.Expanded) {
this.state.expandAll = false;
}
};
this.itemCanExpand = function (item) {
return item.GlobalIPv6Address !== '';
this.expandItem = function (item, expanded) {
if (!this.itemCanExpand(item)) {
return;
}
this.hasExpandableItems = function () {
return _.filter(this.dataset, (item) => this.itemCanExpand(item)).length;
};
item.Expanded = expanded;
if (!expanded) {
item.Highlighted = false;
}
if (!item.Expanded) {
this.state.expandAll = false;
}
};
this.expandAll = function () {
this.state.expandAll = !this.state.expandAll;
_.forEach(this.dataset, (item) => {
if (this.itemCanExpand(item)) {
this.expandItem(item, this.state.expandAll);
}
});
};
this.itemCanExpand = function (item) {
return item.GlobalIPv6Address !== '';
};
this.$onInit = function () {
this.setDefaults();
this.prepareTableFromDataset();
this.hasExpandableItems = function () {
return _.filter(this.dataset, (item) => this.itemCanExpand(item)).length;
};
this.state.orderBy = this.orderBy;
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
this.state.reverseOrder = storedOrder.reverse;
this.state.orderBy = storedOrder.orderBy;
this.expandAll = function () {
this.state.expandAll = !this.state.expandAll;
_.forEach(this.dataset, (item) => {
if (this.itemCanExpand(item)) {
this.expandItem(item, this.state.expandAll);
}
});
};
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
this.$onInit = function () {
this.setDefaults();
this.prepareTableFromDataset();
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
this.state.orderBy = this.orderBy;
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
this.state.reverseOrder = storedOrder.reverse;
this.state.orderBy = storedOrder.orderBy;
}
var storedSettings = DatatableService.getDataTableSettings(this.tableKey);
if (storedSettings !== null) {
this.settings = storedSettings;
this.settings.open = false;
}
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
_.forEach(this.dataset, (item) => {
item.Expanded = true;
item.Highlighted = true;
});
};
}
]);
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
var storedSettings = DatatableService.getDataTableSettings(this.tableKey);
if (storedSettings !== null) {
this.settings = storedSettings;
this.settings.open = false;
}
_.forEach(this.dataset, (item) => {
item.Expanded = true;
item.Highlighted = true;
});
};
},
]);

View File

@@ -38,7 +38,14 @@
<div class="form-group col-md-12">
<label for="nfs_mountpoint" class="col-sm-2 col-md-1 control-label text-left">Mount point</label>
<div class="col-sm-10 col-md-11">
<input type="text" class="form-control" ng-model="$ctrl.data.mountPoint" name="nfs_mountpoint" placeholder="e.g. /export/share, :/export/share, /share or :/share" required />
<input
type="text"
class="form-control"
ng-model="$ctrl.data.mountPoint"
name="nfs_mountpoint"
placeholder="e.g. /export/share, :/export/share, /share or :/share"
required
/>
</div>
</div>
<div class="form-group col-md-12" ng-show="nfsInformationForm.nfs_mountpoint.$invalid">

View File

@@ -1,4 +1,4 @@
import { ContainerDetailsViewModel, ContainerViewModel, ContainerStatsViewModel } from '../models/container';
import { ContainerDetailsViewModel, ContainerStatsViewModel, ContainerViewModel } from '../models/container';
angular.module('portainer.docker').factory('ContainerService', [
'$q',

View File

@@ -610,11 +610,6 @@ angular.module('portainer.docker').controller('CreateContainerController', [
$scope.formValues.NodeName = nodeName;
HttpRequestHelper.setPortainerAgentTargetHeader(nodeName);
$scope.isAdmin = Authentication.isAdmin();
$scope.isAdminOrEndpointAdmin = await checkIfAdminOrEndpointAdmin();
$scope.showDeviceMapping = await shouldShowDevices($scope.isAdminOrEndpointAdmin);
$scope.areContainerCapabilitiesEnabled = await checkIfContainerCapabilitiesEnabled($scope.isAdminOrEndpointAdmin);
Volume.query(
{},
function (d) {
@@ -650,7 +645,7 @@ angular.module('portainer.docker').controller('CreateContainerController', [
loadFromContainerSpec();
} else {
$scope.fromContainer = {};
$scope.formValues.capabilities = $scope.areContainerCapabilitiesEnabled ? new ContainerCapabilities() : [];
$scope.formValues.capabilities = new ContainerCapabilities();
}
},
function (e) {
@@ -677,7 +672,7 @@ angular.module('portainer.docker').controller('CreateContainerController', [
SettingsService.publicSettings()
.then(function success(data) {
$scope.allowBindMounts = $scope.isAdminOrEndpointAdmin || data.AllowBindMountsForRegularUsers;
$scope.allowBindMounts = data.AllowBindMountsForRegularUsers;
$scope.allowPrivilegedMode = data.AllowPrivilegedModeForRegularUsers;
})
.catch(function error(err) {
@@ -687,6 +682,9 @@ angular.module('portainer.docker').controller('CreateContainerController', [
PluginService.loggingPlugins(apiVersion < 1.25).then(function success(loggingDrivers) {
$scope.availableLoggingDrivers = loggingDrivers;
});
$scope.isAdmin = Authentication.isAdmin();
$scope.showDeviceMapping = await shouldShowDevices();
}
function validateForm(accessControlData, isAdmin) {
@@ -899,25 +897,17 @@ angular.module('portainer.docker').controller('CreateContainerController', [
}
}
async function shouldShowDevices(isAdminOrEndpointAdmin) {
async function shouldShowDevices() {
const isAdmin = !$scope.applicationState.application.authentication || Authentication.isAdmin();
const { allowDeviceMappingForRegularUsers } = $scope.applicationState.application;
return allowDeviceMappingForRegularUsers || isAdminOrEndpointAdmin;
}
async function checkIfContainerCapabilitiesEnabled(isAdminOrEndpointAdmin) {
const { allowContainerCapabilitiesForRegularUsers } = $scope.applicationState.application;
return allowContainerCapabilitiesForRegularUsers || isAdminOrEndpointAdmin;
}
async function checkIfAdminOrEndpointAdmin() {
if (Authentication.isAdmin()) {
if (isAdmin || allowDeviceMappingForRegularUsers) {
return true;
}
const rbacEnabled = await ExtensionService.extensionEnabled(ExtensionService.EXTENSIONS.RBAC);
return rbacEnabled ? Authentication.hasAuthorizations(['EndpointResourcesAccess']) : false;
if (rbacEnabled) {
return Authentication.hasAuthorizations(['EndpointResourcesAccess']);
}
}
initView();

View File

@@ -185,7 +185,7 @@
<li class="interactive"><a data-target="#labels" data-toggle="tab">Labels</a></li>
<li class="interactive"><a data-target="#restart-policy" data-toggle="tab">Restart policy</a></li>
<li class="interactive"><a data-target="#runtime-resources" ng-click="refreshSlider()" data-toggle="tab">Runtime & Resources</a></li>
<li ng-if="areContainerCapabilitiesEnabled" class="interactive"><a data-target="#container-capabilities" data-toggle="tab">Capabilities</a></li>
<li class="interactive"><a data-target="#container-capabilities" data-toggle="tab">Capabilities</a></li>
</ul>
<!-- tab-content -->
<div class="tab-content">
@@ -338,8 +338,8 @@
</div>
<!-- !container-path -->
<!-- volume-type -->
<div class="input-group col-sm-5" style="margin-left: 5px;">
<div class="btn-group btn-group-sm" ng-if="allowBindMounts">
<div class="input-group col-sm-5" style="margin-left: 5px;" ng-if="isAdmin || allowBindMounts">
<div class="btn-group btn-group-sm">
<label class="btn btn-primary" ng-model="volume.type" uib-btn-radio="'volume'" ng-click="volume.name = ''">Volume</label>
<label class="btn btn-primary" ng-model="volume.type" uib-btn-radio="'bind'" ng-click="volume.name = ''">Bind</label>
</div>

View File

@@ -33,7 +33,6 @@ angular.module('portainer.docker').controller('CreateServiceController', [
'SettingsService',
'WebhookService',
'EndpointProvider',
'ExtensionService',
function (
$q,
$scope,
@@ -59,8 +58,7 @@ angular.module('portainer.docker').controller('CreateServiceController', [
NodeService,
SettingsService,
WebhookService,
EndpointProvider,
ExtensionService
EndpointProvider
) {
$scope.formValues = {
Name: '',
@@ -108,8 +106,6 @@ angular.module('portainer.docker').controller('CreateServiceController', [
actionInProgress: false,
};
$scope.allowBindMounts = false;
$scope.refreshSlider = function () {
$timeout(function () {
$scope.$broadcast('rzSliderForceRender');
@@ -566,8 +562,8 @@ angular.module('portainer.docker').controller('CreateServiceController', [
secrets: apiVersion >= 1.25 ? SecretService.secrets() : [],
configs: apiVersion >= 1.3 ? ConfigService.configs() : [],
nodes: NodeService.nodes(),
settings: SettingsService.publicSettings(),
availableLoggingDrivers: PluginService.loggingPlugins(apiVersion < 1.25),
allowBindMounts: checkIfAllowedBindMounts(),
})
.then(function success(data) {
$scope.availableVolumes = data.volumes;
@@ -576,8 +572,8 @@ angular.module('portainer.docker').controller('CreateServiceController', [
$scope.availableConfigs = data.configs;
$scope.availableLoggingDrivers = data.availableLoggingDrivers;
initSlidersMaxValuesBasedOnNodeData(data.nodes);
$scope.allowBindMounts = data.settings.AllowBindMountsForRegularUsers;
$scope.isAdmin = Authentication.isAdmin();
$scope.allowBindMounts = data.allowBindMounts;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to initialize view');
@@ -585,22 +581,5 @@ angular.module('portainer.docker').controller('CreateServiceController', [
}
initView();
async function checkIfAllowedBindMounts() {
const isAdmin = Authentication.isAdmin();
const settings = await SettingsService.publicSettings();
const { AllowBindMountsForRegularUsers } = settings;
if (isAdmin || AllowBindMountsForRegularUsers) {
return true;
}
const rbacEnabled = await ExtensionService.extensionEnabled(ExtensionService.EXTENSIONS.RBAC);
if (rbacEnabled) {
return Authentication.hasAuthorizations(['EndpointResourcesAccess']);
}
return false;
}
},
]);

View File

@@ -305,7 +305,7 @@
<!-- !container-path -->
<!-- volume-type -->
<div class="input-group col-sm-5" style="margin-left: 5px;">
<div class="btn-group btn-group-sm" ng-if="allowBindMounts">
<div class="btn-group btn-group-sm" ng-if="isAdmin || allowBindMounts">
<label class="btn btn-primary" ng-model="volume.Type" uib-btn-radio="'volume'" ng-click="volume.name = ''">Volume</label>
<label class="btn btn-primary" ng-model="volume.Type" uib-btn-radio="'bind'" ng-click="volume.Id = ''">Bind</label>
</div>

View File

@@ -99,7 +99,6 @@
<td colspan="2">
<p class="small text-muted" authorization="DockerServiceUpdate">
Note: you can only rollback one level of changes. Clicking the rollback button without making a new change will undo your previous rollback </p
><p>
<a
authorization="DockerServiceLogs"

View File

@@ -72,9 +72,7 @@
<input type="checkbox" name="useNFS" ng-model="formValues.NFSData.useNFS" ng-click="formValues.CIFSData.useCIFS = false" />
<i></i>
</label>
<div ng-if="formValues.NFSData.useNFS" class="small text-muted" style="margin-top: 10px;">
Ensure <code>nfs-utils</code> are installed on your hosts.
</div>
<div ng-if="formValues.NFSData.useNFS" class="small text-muted" style="margin-top: 10px;"> Ensure <code>nfs-utils</code> are installed on your hosts. </div>
</div>
<volumes-nfs-form data="formValues.NFSData" ng-show="formValues.Driver === 'local'"></volumes-nfs-form>
<!-- !nfs-management -->
@@ -87,9 +85,7 @@
<input type="checkbox" name="useCIFS" ng-model="formValues.CIFSData.useCIFS" ng-click="formValues.NFSData.useNFS = false" />
<i></i>
</label>
<div ng-if="formValues.CIFSData.useCIFS" class="small text-muted" style="margin-top: 10px;">
Ensure <code>cifs-utils</code> are installed on your hosts.
</div>
<div ng-if="formValues.CIFSData.useCIFS" class="small text-muted" style="margin-top: 10px;"> Ensure <code>cifs-utils</code> are installed on your hosts. </div>
</div>
<volumes-cifs-form data="formValues.CIFSData" ng-show="formValues.Driver === 'local'"></volumes-cifs-form>
<!-- !cifs-management -->

View File

@@ -73,16 +73,14 @@ angular.module('portainer.docker').controller('VolumesController', [
$scope.showBrowseAction = $scope.applicationState.endpoint.mode.agentProxy;
if ($scope.applicationState.application.authentication) {
ExtensionService.extensionEnabled(ExtensionService.EXTENSIONS.RBAC).then(function success(extensionEnabled) {
if (!extensionEnabled) {
var isAdmin = Authentication.isAdmin();
if (!$scope.applicationState.application.enableVolumeBrowserForNonAdminUsers && !isAdmin) {
$scope.showBrowseAction = false;
}
ExtensionService.extensionEnabled(ExtensionService.EXTENSIONS.RBAC).then(function success(extensionEnabled) {
if (!extensionEnabled) {
var isAdmin = Authentication.isAdmin();
if (!$scope.applicationState.application.enableVolumeBrowserForNonAdminUsers && !isAdmin) {
$scope.showBrowseAction = false;
}
});
}
}
});
}
initView();

View File

@@ -1,16 +1,10 @@
<ui-select
multiple
ng-model="$ctrl.model"
close-on-select="false"
>
<ui-select multiple ng-model="$ctrl.model" close-on-select="false">
<ui-select-match placeholder="Select one or multiple group(s)">
<span>
{{ $item.Name }}
</span>
</ui-select-match>
<ui-select-choices
repeat="item.Id as item in $ctrl.items | filter: { Name: $select.search }"
>
<ui-select-choices repeat="item.Id as item in $ctrl.items | filter: { Name: $select.search }">
<span>
{{ item.Name }}
</span>

View File

@@ -2,6 +2,6 @@ angular.module('portainer.edge').component('edgeGroupsSelector', {
templateUrl: './edge-groups-selector.html',
bindings: {
model: '=',
items: '<'
}
items: '<',
},
});

View File

@@ -1,8 +1,4 @@
import {
RoleViewModel,
// EndpointRoleCreateRequest,
// EndpointRoleUpdateRequest
} from '../models/role';
import { RoleViewModel } from '../models/role';
angular.module('portainer.extensions.rbac').factory('RoleService', [
'$q',

View File

@@ -1,4 +1,5 @@
import _ from 'lodash-es';
export function RegistryRepositoryViewModel(item) {
if (item.name && item.tags) {
this.Name = item.name;

View File

@@ -1,6 +1,5 @@
import _ from 'lodash-es';
import { RepositoryShortTag } from '../models/repositoryTag';
import { RepositoryAddTagPayload } from '../models/repositoryTag';
import { RepositoryAddTagPayload, RepositoryShortTag } from '../models/repositoryTag';
import { RegistryRepositoryViewModel } from '../models/registryRepository';
import genericAsyncGenerator from './genericAsyncGenerator';

View File

@@ -1,5 +1,5 @@
import _ from 'lodash-es';
import { RepositoryTagViewModel, RepositoryShortTag } from '../../../models/repositoryTag';
import { RepositoryShortTag, RepositoryTagViewModel } from '../../../models/repositoryTag';
angular.module('portainer.app').controller('RegistryRepositoryController', [
'$q',

View File

@@ -1,4 +1,4 @@
import { StoridgeNodeModel, StoridgeNodeDetailedModel } from '../models/node';
import { StoridgeNodeDetailedModel, StoridgeNodeModel } from '../models/node';
angular.module('portainer.integrations.storidge').factory('StoridgeNodeService', [
'$q',

View File

@@ -1,6 +1,6 @@
import _ from 'lodash-es';
import { ResourceControlOwnership as RCO } from 'Portainer/models/resourceControl/resourceControlOwnership';
import { ResourceControlTypeString as RCTS, ResourceControlTypeInt as RCTI } from 'Portainer/models/resourceControl/resourceControlTypes';
import { ResourceControlTypeInt as RCTI, ResourceControlTypeString as RCTS } from 'Portainer/models/resourceControl/resourceControlTypes';
import { AccessControlPanelData } from './porAccessControlPanelModel';
angular.module('portainer.app').controller('porAccessControlPanelController', [

View File

@@ -5,11 +5,7 @@
<span ng-if="$item.TagIds.length"> - <i class="fa fa-tags"></i> {{ $ctrl.tagIdsToTagNames($item.TagIds) | arraytostr }}</span>
</span>
</ui-select-match>
<ui-select-choices
group-by="$ctrl.groupEndpoints"
group-filter="$ctrl.sortGroups"
repeat="endpoint.Id as endpoint in $ctrl.endpoints | filter: { Name: $select.search }"
>
<ui-select-choices group-by="$ctrl.groupEndpoints" group-filter="$ctrl.sortGroups" repeat="endpoint.Id as endpoint in $ctrl.endpoints | filter: { Name: $select.search }">
<span>
{{ endpoint.Name }}
<span ng-if="endpoint.TagIds.length"> - <i class="fa fa-tags"></i> {{ $ctrl.tagIdsToTagNames(endpoint.TagIds) | arraytostr }}</span>

View File

@@ -1,5 +1,5 @@
import _ from 'lodash-es';
import { createStatus } from '../../docker/models/container';
import {createStatus} from '../../docker/models/container';
export function ScheduleDefaultModel() {
this.Name = '';

View File

@@ -16,7 +16,6 @@ export function SettingsViewModel(data) {
this.AllowStackManagementForRegularUsers = data.AllowStackManagementForRegularUsers;
this.AllowHostNamespaceForRegularUsers = data.AllowHostNamespaceForRegularUsers;
this.AllowDeviceMappingForRegularUsers = data.AllowDeviceMappingForRegularUsers;
this.AllowContainerCapabilitiesForRegularUsers = data.AllowContainerCapabilitiesForRegularUsers;
}
export function PublicSettingsViewModel(settings) {
@@ -32,7 +31,6 @@ export function PublicSettingsViewModel(settings) {
this.AllowStackManagementForRegularUsers = settings.AllowStackManagementForRegularUsers;
this.AllowDeviceMappingForRegularUsers = settings.AllowDeviceMappingForRegularUsers;
this.AllowHostNamespaceForRegularUsers = settings.AllowHostNamespaceForRegularUsers;
this.AllowContainerCapabilitiesForRegularUsers = settings.AllowContainerCapabilitiesForRegularUsers;
}
export function LDAPSettingsViewModel(data) {

View File

@@ -1,6 +1,5 @@
import _ from 'lodash-es';
import { UserAccessViewModel } from '../../models/access';
import { TeamAccessViewModel } from '../../models/access';
import { TeamAccessViewModel, UserAccessViewModel } from '../../models/access';
angular.module('portainer.app').factory('AccessService', [
'$q',

View File

@@ -1,7 +1,7 @@
import _ from 'lodash-es';
import { PorImageRegistryModel } from 'Docker/models/porImageRegistry';
import { RegistryTypes } from 'Extensions/registry-management/models/registryTypes';
import { RegistryViewModel, RegistryCreateRequest } from '../../models/registry';
import { RegistryCreateRequest, RegistryViewModel } from '../../models/registry';
angular.module('portainer.app').factory('RegistryService', [
'$q',

View File

@@ -1,4 +1,4 @@
import { ScheduleModel, ScheduleCreateRequest, ScheduleUpdateRequest, ScriptExecutionTaskModel } from '../../models/schedule';
import { ScheduleCreateRequest, ScheduleModel, ScheduleUpdateRequest, ScriptExecutionTaskModel } from '../../models/schedule';
angular.module('portainer.app').factory('ScheduleService', [
'$q',

View File

@@ -1,4 +1,4 @@
import { SettingsViewModel, PublicSettingsViewModel } from '../../models/settings';
import { PublicSettingsViewModel, SettingsViewModel } from '../../models/settings';
angular.module('portainer.app').factory('SettingsService', [
'$q',

View File

@@ -1,5 +1,5 @@
import _ from 'lodash-es';
import { StackViewModel, ExternalStackViewModel } from '../../models/stack';
import { ExternalStackViewModel, StackViewModel } from '../../models/stack';
angular.module('portainer.app').factory('StackService', [
'$q',

View File

@@ -1,4 +1,4 @@
import { TemplateViewModel, TemplateCreateRequest, TemplateUpdateRequest } from '../../models/template';
import { TemplateCreateRequest, TemplateUpdateRequest, TemplateViewModel } from '../../models/template';
angular.module('portainer.app').factory('TemplateService', [
'$q',

View File

@@ -91,12 +91,11 @@ angular.module('portainer.app').factory('FileUploadService', [
data: {
file: file,
Name: stackName,
EdgeGroups: Upload.json(edgeGroups)
EdgeGroups: Upload.json(edgeGroups),
},
ignoreLoadingBar: true
ignoreLoadingBar: true,
});
};
service.configureRegistry = function (registryId, registryManagementConfigurationModel) {
return Upload.upload({

View File

@@ -101,11 +101,6 @@ angular.module('portainer.app').factory('StateManager', [
LocalStorage.storeApplicationState(state.application);
};
manager.updateAllowContainerCapabilitiesForRegularUsers = function updateAllowContainerCapabilitiesForRegularUsers(allowContainerCapabilitiesForRegularUsers) {
state.application.allowContainerCapabilitiesForRegularUsers = allowContainerCapabilitiesForRegularUsers;
LocalStorage.storeApplicationState(state.application);
};
function assignStateFromStatusAndSettings(status, settings) {
state.application.authentication = status.Authentication;
state.application.analytics = status.Analytics;
@@ -122,7 +117,6 @@ angular.module('portainer.app').factory('StateManager', [
state.application.allowPrivilegedModeForRegularUsers = settings.AllowPrivilegedModeForRegularUsers;
state.application.allowDeviceMappingForRegularUsers = settings.AllowDeviceMappingForRegularUsers;
state.application.allowHostNamespaceForRegularUsers = settings.AllowHostNamespaceForRegularUsers;
state.application.allowContainerCapabilitiesForRegularUsers = settings.AllowContainerCapabilitiesForRegularUsers;
state.application.validity = moment().unix();
}

View File

@@ -3,6 +3,17 @@
<rd-header-content>User settings</rd-header-content>
</rd-header>
<div class="row" ng-if="UserId === 1">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-exclamation-triangle" title="Feature not available"> </rd-widget-header>
<rd-widget-body>
<span class="small text-muted">You cannot change the password of this account in the demo version of Portainer.</span>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>

View File

@@ -36,10 +36,10 @@
<div style="margin-top: 10px;">
<uib-tabset active="state.deploymentTab">
<uib-tab index="0" heading="Standalone">
<code style="display: block; white-space: pre-wrap; padding: 16px 90px;">{{dockerCommands.standalone}}</code>
<code style="display: block; white-space: pre-wrap; padding: 16px 90px;">{{ dockerCommands.standalone }}</code>
</uib-tab>
<uib-tab index="1" heading="Swarm">
<code style="display: block; white-space: pre-wrap; padding: 16px 90px;">{{dockerCommands.swarm}}</code>
<code style="display: block; white-space: pre-wrap; padding: 16px 90px;">{{ dockerCommands.swarm }}</code>
</uib-tab>
</uib-tabset>
<div style="margin-top: 10px;">

View File

@@ -3,6 +3,17 @@
<rd-header-content>Settings</rd-header-content>
</rd-header>
<div class="row" ng-if="UserId === 1">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-exclamation-triangle" title="Feature not available"> </rd-widget-header>
<rd-widget-body>
<span class="small text-muted">You cannot change the password of this account in the demo version of Portainer.</span>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<rd-widget>
@@ -23,7 +34,10 @@
<label for="toggle_logo" class="control-label text-left">
Use custom logo
</label>
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" name="toggle_logo" ng-model="formValues.customLogo" /><i></i> </label>
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" disabled name="toggle_logo" ng-model="formValues.customLogo" /><i></i> </label>
</div>
<div class="col-sm-12">
<span class="small text-muted">You cannot use this feature in the demo version of Portainer.</span>
</div>
</div>
<div ng-if="formValues.customLogo">
@@ -153,17 +167,6 @@
</div>
</div>
<div class="form-group">
<div class="col-sm-12">
<label for="toggle_disableContainerCapabilitiesForRegularUsers" class="control-label text-left">
Disable container capabilities for non-administrators
</label>
<label class="switch" style="margin-left: 20px;">
<input type="checkbox" name="toggle_disableContainerCapabilitiesForRegularUsers" ng-model="formValues.disableContainerCapabilitiesForRegularUsers" /><i></i>
</label>
</div>
</div>
<div class="form-group" ng-if="isContainerEditDisabled()">
<span class="col-sm-12 text-muted small">
Note: The recreate/duplicate/edit feature is currently disabled (for non-admin users) by one or more security settings.

View File

@@ -36,20 +36,11 @@ angular.module('portainer.app').controller('SettingsController', [
allowStackManagementForRegularUsers: false,
restrictHostNamespaceForRegularUsers: false,
allowDeviceMappingForRegularUsers: false,
disableContainerCapabilitiesForRegularUsers: false,
};
$scope.isContainerEditDisabled = function isContainerEditDisabled() {
const {
restrictBindMounts,
restrictHostNamespaceForRegularUsers,
restrictPrivilegedMode,
disableDeviceMappingForRegularUsers,
disableContainerCapabilitiesForRegularUsers,
} = this.formValues;
return (
restrictBindMounts || restrictHostNamespaceForRegularUsers || restrictPrivilegedMode || disableDeviceMappingForRegularUsers || disableContainerCapabilitiesForRegularUsers
);
const { restrictBindMounts, restrictHostNamespaceForRegularUsers, restrictPrivilegedMode, disableDeviceMappingForRegularUsers } = this.formValues;
return restrictBindMounts || restrictHostNamespaceForRegularUsers || restrictPrivilegedMode || disableDeviceMappingForRegularUsers;
};
$scope.removeFilteredContainerLabel = function (index) {
@@ -89,7 +80,6 @@ angular.module('portainer.app').controller('SettingsController', [
settings.AllowStackManagementForRegularUsers = !$scope.formValues.disableStackManagementForRegularUsers;
settings.AllowHostNamespaceForRegularUsers = !$scope.formValues.restrictHostNamespaceForRegularUsers;
settings.AllowDeviceMappingForRegularUsers = !$scope.formValues.disableDeviceMappingForRegularUsers;
settings.AllowContainerCapabilitiesForRegularUsers = !$scope.formValues.disableContainerCapabilitiesForRegularUsers;
$scope.state.actionInProgress = true;
updateSettings(settings);
@@ -109,7 +99,6 @@ angular.module('portainer.app').controller('SettingsController', [
StateManager.updateAllowDeviceMappingForRegularUsers(settings.AllowDeviceMappingForRegularUsers);
StateManager.updateAllowPrivilegedModeForRegularUsers(settings.AllowPrivilegedModeForRegularUsers);
StateManager.updateAllowBindMountsForRegularUsers(settings.AllowBindMountsForRegularUsers);
StateManager.updateAllowContainerCapabilitiesForRegularUsers(settings.AllowContainerCapabilitiesForRegularUsers);
$state.reload();
})
.catch(function error(err) {
@@ -139,7 +128,6 @@ angular.module('portainer.app').controller('SettingsController', [
$scope.formValues.disableStackManagementForRegularUsers = !settings.AllowStackManagementForRegularUsers;
$scope.formValues.restrictHostNamespaceForRegularUsers = !settings.AllowHostNamespaceForRegularUsers;
$scope.formValues.disableDeviceMappingForRegularUsers = !settings.AllowDeviceMappingForRegularUsers;
$scope.formValues.disableContainerCapabilitiesForRegularUsers = !settings.AllowContainerCapabilitiesForRegularUsers;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve application settings');

View File

@@ -15,7 +15,6 @@ import 'angular-moment-picker/dist/angular-moment-picker.min.css';
import 'angular-multiselect/isteven-multi-select.css';
import angular from 'angular';
window.angular = angular;
import 'moment';
import '@uirouter/angularjs';
import 'ui-select';
@@ -39,3 +38,5 @@ import 'js-yaml/dist/js-yaml.js';
import 'angular-ui-bootstrap';
import 'angular-moment-picker';
import 'angular-multiselect/isteven-multi-select.js';
window.angular = angular;

View File

@@ -0,0 +1,24 @@
param (
[string]$platform,
[string]$arch
)
$ErrorActionPreference = "Stop";
$binary = "portainer.exe"
$go_path = "$($(Get-ITEM -Path env:AGENT_TEMPDIRECTORY).Value)\go"
Set-Item env:GOPATH "$go_path"
New-Item -Name dist -Path "." -ItemType Directory -Force | Out-Null
New-Item -Name portainer -Path "$go_path\src\github.com\portainer" -ItemType Directory -Force | Out-Null
Copy-Item -Path "api" -Destination "$go_path\src\github.com\portainer\portainer\api" -Recurse -Force
Set-Location -Path "api\cmd\portainer"
go get -t -d -v ./...
## go build -v
& cmd /c 'go build -v 2>&1'
Copy-Item -Path "portainer.exe" -Destination "$($env:BUILD_SOURCESDIRECTORY)\dist\portainer.exe" -Force

View File

@@ -1,8 +1,3 @@
#!/usr/bin/env bash
PLATFORM=$1
ARCH=$2
export GOPATH="/tmp/go"
binary="portainer"
@@ -15,10 +10,6 @@ cp -R api ${GOPATH}/src/github.com/portainer/portainer/api
cd 'api/cmd/portainer'
go get -t -d -v ./...
GOOS=${PLATFORM} GOARCH=${ARCH} CGO_ENABLED=0 go build -a --installsuffix cgo --ldflags '-s'
GOOS=$1 GOARCH=$2 CGO_ENABLED=0 go build -a --installsuffix cgo --ldflags '-s'
if [ "${PLATFORM}" == 'windows' ]; then
mv "$BUILD_SOURCESDIRECTORY/api/cmd/portainer/${binary}.exe" "$BUILD_SOURCESDIRECTORY/dist/portainer.exe"
else
mv "$BUILD_SOURCESDIRECTORY/api/cmd/portainer/$binary" "$BUILD_SOURCESDIRECTORY/dist/portainer"
fi
mv "$BUILD_SOURCESDIRECTORY/api/cmd/portainer/$binary" "$BUILD_SOURCESDIRECTORY/dist/portainer"

View File

@@ -0,0 +1,13 @@
param (
[string]$docker_version
)
$ErrorActionPreference = "Stop";
New-Item -Path "docker-binary" -ItemType Directory | Out-Null
$download_folder = "docker-binary"
Invoke-WebRequest -O "$($download_folder)/docker-binaries.zip" "https://download.docker.com/win/static/stable/x86_64/docker-$($docker_version).zip"
Expand-Archive -Path "$($download_folder)/docker-binaries.zip" -DestinationPath "$($download_folder)"
Move-Item -Path "$($download_folder)/docker/docker.exe" -Destination "dist"

View File

@@ -1,14 +0,0 @@
ARG OSVERSION
FROM --platform=linux/amd64 gcr.io/k8s-staging-e2e-test-images/windows-servercore-cache:1.0-linux-amd64-${OSVERSION} as core
FROM mcr.microsoft.com/windows/nanoserver:${OSVERSION}
COPY --from=core /Windows/System32/netapi32.dll /Windows/System32/netapi32.dll
USER ContainerAdministrator
COPY dist /
EXPOSE 9000
EXPOSE 8000
ENTRYPOINT ["/portainer.exe"]

View File

@@ -0,0 +1,9 @@
FROM microsoft/nanoserver:sac2016
COPY dist /
WORKDIR /
EXPOSE 9000
ENTRYPOINT ["/portainer.exe"]

View File

@@ -142,7 +142,11 @@ function shell_build_binary(p, a) {
}
function shell_build_binary_azuredevops(p, a) {
return 'build/build_binary_azuredevops.sh ' + p + ' ' + a + ';';
if (p === 'linux') {
return 'build/build_binary_azuredevops.sh ' + p + ' ' + a + ';';
} else {
return 'powershell -Command ".\\build\\build_binary_azuredevops.ps1 -platform ' + p + ' -arch ' + a + '"';
}
}
function shell_run_container() {
@@ -160,12 +164,15 @@ function shell_download_docker_binary(p, a) {
var ip = ps[p] === undefined ? p : ps[p];
var ia = as[a] === undefined ? a : as[a];
var binaryVersion = p === 'windows' ? '<%= shippedDockerVersionWindows %>' : '<%= shippedDockerVersion %>';
return [
'if [ -f dist/docker ] || [ -f dist/docker.exe ]; then',
'echo "docker binary exists";',
'else',
'build/download_docker_binary.sh ' + ip + ' ' + ia + ' ' + binaryVersion + ';',
'fi',
].join(' ');
if (p === 'linux' || p === 'mac') {
return ['if [ -f dist/docker ]; then', 'echo "Docker binary exists";', 'else', 'build/download_docker_binary.sh ' + ip + ' ' + ia + ' ' + binaryVersion + ';', 'fi'].join(' ');
} else {
return [
'powershell -Command "& {if (Get-Item -Path dist/docker.exe -ErrorAction:SilentlyContinue) {',
'Write-Host "Docker binary exists"',
'} else {',
'& ".\\build\\download_docker_binary.ps1" -docker_version ' + binaryVersion + '',
'}}"',
].join(' ');
}
}

View File

@@ -2,7 +2,7 @@
"author": "Portainer.io",
"name": "portainer",
"homepage": "http://portainer.io",
"version": "1.24.2",
"version": "1.24.1",
"repository": {
"type": "git",
"url": "git@github.com:portainer/portainer.git"