feat(auth): add custom user timeout (#3871)

* feat(auth): introduce new timeout constant

* feat(auth): pass timeout from handler

* feat(auth): add timeout selector to auth settings view

* feat(settings): add user session timeout property

* feat(auth): load user session timeout from settings

* fix(settings): use correct time format

* feat(auth): remove no-auth flag

* refactor(auth): move timeout mgmt to jwt service

* refactor(client): remove no-auth checks from client

* refactor(cli): remove defaultNoAuth

* feat(settings): create settings with default user timeout value

* refactor(db): save user session timeout always

* refactor(jwt): return error

* feat(auth): set session timeout in jwt service on update

* feat(auth): add description and time settings

* feat(auth): parse duration

* feat(settings): validate user timeout format

* refactor(settings): remove unneccesary import
pull/3914/head
Chaim Lev-Ari 2020-06-09 12:55:36 +03:00 committed by GitHub
parent b58c2facfe
commit b02749f877
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 214 additions and 236 deletions

View File

@ -27,6 +27,7 @@ func (store *Store) Init() error {
EnableHostManagementFeatures: false, EnableHostManagementFeatures: false,
EdgeAgentCheckinInterval: portainer.DefaultEdgeAgentCheckinIntervalInSeconds, EdgeAgentCheckinInterval: portainer.DefaultEdgeAgentCheckinIntervalInSeconds,
TemplatesURL: portainer.DefaultTemplatesURL, TemplatesURL: portainer.DefaultTemplatesURL,
UserSessionTimeout: portainer.DefaultUserSessionTimeout,
} }
err = store.SettingsService.UpdateSettings(defaultSettings) err = store.SettingsService.UpdateSettings(defaultSettings)

View File

@ -10,9 +10,9 @@ func (m *Migrator) updateSettingsToDB24() error {
if legacySettings.TemplatesURL == "" { if legacySettings.TemplatesURL == "" {
legacySettings.TemplatesURL = portainer.DefaultTemplatesURL legacySettings.TemplatesURL = portainer.DefaultTemplatesURL
return m.settingsService.UpdateSettings(legacySettings)
} }
return nil legacySettings.UserSessionTimeout = portainer.DefaultUserSessionTimeout
return m.settingsService.UpdateSettings(legacySettings)
} }

View File

@ -1,7 +1,6 @@
package cli package cli
import ( import (
"log"
"time" "time"
"github.com/portainer/portainer/api" "github.com/portainer/portainer/api"
@ -20,7 +19,6 @@ const (
errInvalidEndpointProtocol = portainer.Error("Invalid endpoint protocol: Portainer only supports unix://, npipe:// or tcp://") errInvalidEndpointProtocol = portainer.Error("Invalid endpoint protocol: Portainer only supports unix://, npipe:// or tcp://")
errSocketOrNamedPipeNotFound = portainer.Error("Unable to locate Unix socket or named pipe") errSocketOrNamedPipeNotFound = portainer.Error("Unable to locate Unix socket or named pipe")
errInvalidSnapshotInterval = portainer.Error("Invalid snapshot interval") errInvalidSnapshotInterval = portainer.Error("Invalid snapshot interval")
errNoAuthExcludeAdminPassword = portainer.Error("Cannot use --no-auth with --admin-password or --admin-password-file")
errAdminPassExcludeAdminPassFile = portainer.Error("Cannot use --admin-password with --admin-password-file") errAdminPassExcludeAdminPassFile = portainer.Error("Cannot use --admin-password with --admin-password-file")
) )
@ -35,7 +33,6 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
Assets: kingpin.Flag("assets", "Path to the assets").Default(defaultAssetsDirectory).Short('a').String(), Assets: kingpin.Flag("assets", "Path to the assets").Default(defaultAssetsDirectory).Short('a').String(),
Data: kingpin.Flag("data", "Path to the folder where the data is stored").Default(defaultDataDirectory).Short('d').String(), Data: kingpin.Flag("data", "Path to the folder where the data is stored").Default(defaultDataDirectory).Short('d').String(),
EndpointURL: kingpin.Flag("host", "Endpoint URL").Short('H').String(), EndpointURL: kingpin.Flag("host", "Endpoint URL").Short('H').String(),
NoAuth: kingpin.Flag("no-auth", "Disable authentication (deprecated)").Default(defaultNoAuth).Bool(),
NoAnalytics: kingpin.Flag("no-analytics", "Disable Analytics in app").Default(defaultNoAnalytics).Bool(), NoAnalytics: kingpin.Flag("no-analytics", "Disable Analytics in app").Default(defaultNoAnalytics).Bool(),
TLS: kingpin.Flag("tlsverify", "TLS support").Default(defaultTLS).Bool(), TLS: kingpin.Flag("tlsverify", "TLS support").Default(defaultTLS).Bool(),
TLSSkipVerify: kingpin.Flag("tlsskipverify", "Disable TLS server verification").Default(defaultTLSSkipVerify).Bool(), TLSSkipVerify: kingpin.Flag("tlsskipverify", "Disable TLS server verification").Default(defaultTLSSkipVerify).Bool(),
@ -81,10 +78,6 @@ func (*Service) ValidateFlags(flags *portainer.CLIFlags) error {
return err return err
} }
if *flags.NoAuth && (*flags.AdminPassword != "" || *flags.AdminPasswordFile != "") {
return errNoAuthExcludeAdminPassword
}
if *flags.AdminPassword != "" && *flags.AdminPasswordFile != "" { if *flags.AdminPassword != "" && *flags.AdminPasswordFile != "" {
return errAdminPassExcludeAdminPassFile return errAdminPassExcludeAdminPassFile
} }
@ -93,9 +86,7 @@ func (*Service) ValidateFlags(flags *portainer.CLIFlags) error {
} }
func displayDeprecationWarnings(flags *portainer.CLIFlags) { func displayDeprecationWarnings(flags *portainer.CLIFlags) {
if *flags.NoAuth {
log.Println("Warning: the --no-auth flag is deprecated and will likely be removed in a future version of Portainer.")
}
} }
func validateEndpointURL(endpointURL string) error { func validateEndpointURL(endpointURL string) error {

View File

@ -8,7 +8,6 @@ const (
defaultTunnelServerPort = "8000" defaultTunnelServerPort = "8000"
defaultDataDirectory = "/data" defaultDataDirectory = "/data"
defaultAssetsDirectory = "./" defaultAssetsDirectory = "./"
defaultNoAuth = "false"
defaultNoAnalytics = "false" defaultNoAnalytics = "false"
defaultTLS = "false" defaultTLS = "false"
defaultTLSSkipVerify = "false" defaultTLSSkipVerify = "false"

View File

@ -6,7 +6,6 @@ const (
defaultTunnelServerPort = "8000" defaultTunnelServerPort = "8000"
defaultDataDirectory = "C:\\data" defaultDataDirectory = "C:\\data"
defaultAssetsDirectory = "./" defaultAssetsDirectory = "./"
defaultNoAuth = "false"
defaultNoAnalytics = "false" defaultNoAnalytics = "false"
defaultTLS = "false" defaultTLS = "false"
defaultTLSSkipVerify = "false" defaultTLSSkipVerify = "false"

View File

@ -77,15 +77,17 @@ func initSwarmStackManager(assetsPath string, dataStorePath string, signatureSer
return exec.NewSwarmStackManager(assetsPath, dataStorePath, signatureService, fileService, reverseTunnelService) return exec.NewSwarmStackManager(assetsPath, dataStorePath, signatureService, fileService, reverseTunnelService)
} }
func initJWTService(authenticationEnabled bool) portainer.JWTService { func initJWTService(dataStore portainer.DataStore) (portainer.JWTService, error) {
if authenticationEnabled { settings, err := dataStore.Settings().Settings()
jwtService, err := jwt.NewService()
if err != nil { if err != nil {
log.Fatal(err) return nil, err
} }
return jwtService
jwtService, err := jwt.NewService(settings.UserSessionTimeout)
if err != nil {
return nil, err
} }
return nil return jwtService, nil
} }
func initDigitalSignatureService() portainer.DigitalSignatureService { func initDigitalSignatureService() portainer.DigitalSignatureService {
@ -189,7 +191,6 @@ func loadSchedulesFromDatabase(jobScheduler portainer.JobScheduler, jobService p
func initStatus(flags *portainer.CLIFlags) *portainer.Status { func initStatus(flags *portainer.CLIFlags) *portainer.Status {
return &portainer.Status{ return &portainer.Status{
Analytics: !*flags.NoAnalytics, Analytics: !*flags.NoAnalytics,
Authentication: !*flags.NoAuth,
Version: portainer.APIVersion, Version: portainer.APIVersion,
} }
} }
@ -392,7 +393,10 @@ func main() {
dataStore := initDataStore(*flags.Data, fileService) dataStore := initDataStore(*flags.Data, fileService)
defer dataStore.Close() defer dataStore.Close()
jwtService := initJWTService(!*flags.NoAuth) jwtService, err := initJWTService(dataStore)
if err != nil {
log.Fatal(err)
}
ldapService := initLDAPService() ldapService := initLDAPService()
@ -402,7 +406,7 @@ func main() {
digitalSignatureService := initDigitalSignatureService() digitalSignatureService := initDigitalSignatureService()
err := initKeyPair(fileService, digitalSignatureService) err = initKeyPair(fileService, digitalSignatureService)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -492,9 +496,7 @@ func main() {
} }
} }
if !*flags.NoAuth {
go terminateIfNoAdminCreated(dataStore) go terminateIfNoAdminCreated(dataStore)
}
err = reverseTunnelService.StartTunnelServer(*flags.TunnelAddr, *flags.TunnelPort, snapshotter) err = reverseTunnelService.StartTunnelServer(*flags.TunnelAddr, *flags.TunnelPort, snapshotter)
if err != nil { if err != nil {
@ -506,7 +508,6 @@ func main() {
Status: applicationStatus, Status: applicationStatus,
BindAddress: *flags.Addr, BindAddress: *flags.Addr,
AssetsPath: *flags.Assets, AssetsPath: *flags.Assets,
AuthDisabled: *flags.NoAuth,
DataStore: dataStore, DataStore: dataStore,
SwarmStackManager: swarmStackManager, SwarmStackManager: swarmStackManager,
ComposeStackManager: composeStackManager, ComposeStackManager: composeStackManager,

View File

@ -32,10 +32,6 @@ func (payload *authenticatePayload) Validate(r *http.Request) error {
} }
func (handler *Handler) authenticate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { func (handler *Handler) authenticate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
if handler.authDisabled {
return &httperror.HandlerError{http.StatusServiceUnavailable, "Cannot authenticate user. Portainer was started with the --no-auth flag", ErrAuthDisabled}
}
var payload authenticatePayload var payload authenticatePayload
err := request.DecodeAndValidateJSONPayload(r, &payload) err := request.DecodeAndValidateJSONPayload(r, &payload)
if err != nil { if err != nil {

View File

@ -10,16 +10,9 @@ import (
"github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/http/security"
) )
const (
// ErrAuthDisabled is an error raised when trying to access the authentication endpoints
// when the server has been started with the --no-auth flag
ErrAuthDisabled = portainer.Error("Authentication is disabled")
)
// Handler is the HTTP handler used to handle authentication operations. // Handler is the HTTP handler used to handle authentication operations.
type Handler struct { type Handler struct {
*mux.Router *mux.Router
authDisabled bool
DataStore portainer.DataStore DataStore portainer.DataStore
CryptoService portainer.CryptoService CryptoService portainer.CryptoService
JWTService portainer.JWTService JWTService portainer.JWTService
@ -29,10 +22,9 @@ type Handler struct {
} }
// NewHandler creates a handler to manage authentication operations. // NewHandler creates a handler to manage authentication operations.
func NewHandler(bouncer *security.RequestBouncer, rateLimiter *security.RateLimiter, authDisabled bool) *Handler { func NewHandler(bouncer *security.RequestBouncer, rateLimiter *security.RateLimiter) *Handler {
h := &Handler{ h := &Handler{
Router: mux.NewRouter(), Router: mux.NewRouter(),
authDisabled: authDisabled,
} }
h.Handle("/auth/oauth/validate", h.Handle("/auth/oauth/validate",

View File

@ -17,11 +17,12 @@ func hideFields(settings *portainer.Settings) {
// Handler is the HTTP handler used to handle settings operations. // Handler is the HTTP handler used to handle settings operations.
type Handler struct { type Handler struct {
*mux.Router *mux.Router
AuthorizationService *portainer.AuthorizationService
DataStore portainer.DataStore DataStore portainer.DataStore
LDAPService portainer.LDAPService
FileService portainer.FileService FileService portainer.FileService
JobScheduler portainer.JobScheduler JobScheduler portainer.JobScheduler
AuthorizationService *portainer.AuthorizationService JWTService portainer.JWTService
LDAPService portainer.LDAPService
} }
// NewHandler creates a handler to manage settings operations. // NewHandler creates a handler to manage settings operations.

View File

@ -2,6 +2,7 @@ package settings
import ( import (
"net/http" "net/http"
"time"
"github.com/asaskevich/govalidator" "github.com/asaskevich/govalidator"
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
@ -25,6 +26,7 @@ type settingsUpdatePayload struct {
TemplatesURL *string TemplatesURL *string
EdgeAgentCheckinInterval *int EdgeAgentCheckinInterval *int
EnableEdgeComputeFeatures *bool EnableEdgeComputeFeatures *bool
UserSessionTimeout *string
} }
func (payload *settingsUpdatePayload) Validate(r *http.Request) error { func (payload *settingsUpdatePayload) Validate(r *http.Request) error {
@ -37,6 +39,13 @@ func (payload *settingsUpdatePayload) Validate(r *http.Request) error {
if payload.TemplatesURL != nil && *payload.TemplatesURL != "" && !govalidator.IsURL(*payload.TemplatesURL) { if payload.TemplatesURL != nil && *payload.TemplatesURL != "" && !govalidator.IsURL(*payload.TemplatesURL) {
return portainer.Error("Invalid external templates URL. Must correspond to a valid URL format") return portainer.Error("Invalid external templates URL. Must correspond to a valid URL format")
} }
if payload.UserSessionTimeout != nil {
_, err := time.ParseDuration(*payload.UserSessionTimeout)
if err != nil {
return portainer.Error("Invalid user session timeout")
}
}
return nil return nil
} }
@ -125,6 +134,14 @@ func (handler *Handler) settingsUpdate(w http.ResponseWriter, r *http.Request) *
settings.EdgeAgentCheckinInterval = *payload.EdgeAgentCheckinInterval settings.EdgeAgentCheckinInterval = *payload.EdgeAgentCheckinInterval
} }
if payload.UserSessionTimeout != nil {
settings.UserSessionTimeout = *payload.UserSessionTimeout
userSessionDuration, _ := time.ParseDuration(*payload.UserSessionTimeout)
handler.JWTService.SetUserSessionDuration(userSessionDuration)
}
tlsError := handler.updateTLS(settings) tlsError := handler.updateTLS(settings)
if tlsError != nil { if tlsError != nil {
return tlsError return tlsError

View File

@ -16,7 +16,6 @@ type (
dataStore portainer.DataStore dataStore portainer.DataStore
jwtService portainer.JWTService jwtService portainer.JWTService
rbacExtensionClient *rbacExtensionClient rbacExtensionClient *rbacExtensionClient
authDisabled bool
} }
// RestrictedRequestContext is a data structure containing information // RestrictedRequestContext is a data structure containing information
@ -30,12 +29,11 @@ type (
) )
// NewRequestBouncer initializes a new RequestBouncer // NewRequestBouncer initializes a new RequestBouncer
func NewRequestBouncer(dataStore portainer.DataStore, jwtService portainer.JWTService, authenticationDisabled bool, rbacExtensionURL string) *RequestBouncer { func NewRequestBouncer(dataStore portainer.DataStore, jwtService portainer.JWTService, rbacExtensionURL string) *RequestBouncer {
return &RequestBouncer{ return &RequestBouncer{
dataStore: dataStore, dataStore: dataStore,
jwtService: jwtService, jwtService: jwtService,
rbacExtensionClient: newRBACExtensionClient(rbacExtensionURL), rbacExtensionClient: newRBACExtensionClient(rbacExtensionURL),
authDisabled: authenticationDisabled,
} }
} }
@ -289,7 +287,6 @@ func (bouncer *RequestBouncer) mwUpgradeToRestrictedRequest(next http.Handler) h
func (bouncer *RequestBouncer) mwCheckAuthentication(next http.Handler) http.Handler { func (bouncer *RequestBouncer) mwCheckAuthentication(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var tokenData *portainer.TokenData var tokenData *portainer.TokenData
if !bouncer.authDisabled {
var token string var token string
// Optionally, token might be set via the "token" query parameter. // Optionally, token might be set via the "token" query parameter.
@ -323,11 +320,6 @@ func (bouncer *RequestBouncer) mwCheckAuthentication(next http.Handler) http.Han
httperror.WriteError(w, http.StatusInternalServerError, "Unable to retrieve user details from the database", err) httperror.WriteError(w, http.StatusInternalServerError, "Unable to retrieve user details from the database", err)
return return
} }
} else {
tokenData = &portainer.TokenData{
Role: portainer.AdministratorRole,
}
}
ctx := storeTokenData(r, tokenData) ctx := storeTokenData(r, tokenData)
next.ServeHTTP(w, r.WithContext(ctx)) next.ServeHTTP(w, r.WithContext(ctx))

View File

@ -47,7 +47,6 @@ import (
type Server struct { type Server struct {
BindAddress string BindAddress string
AssetsPath string AssetsPath string
AuthDisabled bool
Status *portainer.Status Status *portainer.Status
ReverseTunnelService portainer.ReverseTunnelService ReverseTunnelService portainer.ReverseTunnelService
ExtensionManager portainer.ExtensionManager ExtensionManager portainer.ExtensionManager
@ -77,11 +76,11 @@ func (server *Server) Start() error {
authorizationService := portainer.NewAuthorizationService(server.DataStore) authorizationService := portainer.NewAuthorizationService(server.DataStore)
rbacExtensionURL := proxyManager.GetExtensionURL(portainer.RBACExtension) rbacExtensionURL := proxyManager.GetExtensionURL(portainer.RBACExtension)
requestBouncer := security.NewRequestBouncer(server.DataStore, server.JWTService, server.AuthDisabled, rbacExtensionURL) requestBouncer := security.NewRequestBouncer(server.DataStore, server.JWTService, rbacExtensionURL)
rateLimiter := security.NewRateLimiter(10, 1*time.Second, 1*time.Hour) rateLimiter := security.NewRateLimiter(10, 1*time.Second, 1*time.Hour)
var authHandler = auth.NewHandler(requestBouncer, rateLimiter, server.AuthDisabled) var authHandler = auth.NewHandler(requestBouncer, rateLimiter)
authHandler.DataStore = server.DataStore authHandler.DataStore = server.DataStore
authHandler.CryptoService = server.CryptoService authHandler.CryptoService = server.CryptoService
authHandler.JWTService = server.JWTService authHandler.JWTService = server.JWTService
@ -153,11 +152,12 @@ func (server *Server) Start() error {
schedulesHandler.ReverseTunnelService = server.ReverseTunnelService schedulesHandler.ReverseTunnelService = server.ReverseTunnelService
var settingsHandler = settings.NewHandler(requestBouncer) var settingsHandler = settings.NewHandler(requestBouncer)
settingsHandler.AuthorizationService = authorizationService
settingsHandler.DataStore = server.DataStore settingsHandler.DataStore = server.DataStore
settingsHandler.LDAPService = server.LDAPService
settingsHandler.FileService = server.FileService settingsHandler.FileService = server.FileService
settingsHandler.JobScheduler = server.JobScheduler settingsHandler.JobScheduler = server.JobScheduler
settingsHandler.AuthorizationService = authorizationService settingsHandler.JWTService = server.JWTService
settingsHandler.LDAPService = server.LDAPService
var stackHandler = stacks.NewHandler(requestBouncer) var stackHandler = stacks.NewHandler(requestBouncer)
stackHandler.DataStore = server.DataStore stackHandler.DataStore = server.DataStore

View File

@ -13,6 +13,7 @@ import (
// Service represents a service for managing JWT tokens. // Service represents a service for managing JWT tokens.
type Service struct { type Service struct {
secret []byte secret []byte
userSessionTimeout time.Duration
} }
type claims struct { type claims struct {
@ -23,20 +24,27 @@ type claims struct {
} }
// NewService initializes a new service. It will generate a random key that will be used to sign JWT tokens. // NewService initializes a new service. It will generate a random key that will be used to sign JWT tokens.
func NewService() (*Service, error) { func NewService(userSessionDuration string) (*Service, error) {
userSessionTimeout, err := time.ParseDuration(userSessionDuration)
if err != nil {
return nil, err
}
secret := securecookie.GenerateRandomKey(32) secret := securecookie.GenerateRandomKey(32)
if secret == nil { if secret == nil {
return nil, portainer.ErrSecretGeneration return nil, portainer.ErrSecretGeneration
} }
service := &Service{ service := &Service{
secret, secret,
userSessionTimeout,
} }
return service, nil return service, nil
} }
// GenerateToken generates a new JWT token. // GenerateToken generates a new JWT token.
func (service *Service) GenerateToken(data *portainer.TokenData) (string, error) { func (service *Service) GenerateToken(data *portainer.TokenData) (string, error) {
expireToken := time.Now().Add(time.Hour * 8).Unix() expireToken := time.Now().Add(service.userSessionTimeout).Unix()
cl := claims{ cl := claims{
UserID: int(data.ID), UserID: int(data.ID),
Username: data.Username, Username: data.Username,
@ -77,3 +85,8 @@ func (service *Service) ParseAndVerifyToken(token string) (*portainer.TokenData,
return nil, portainer.ErrInvalidJWTToken return nil, portainer.ErrInvalidJWTToken
} }
// SetUserSessionDuration sets the user session duration
func (service *Service) SetUserSessionDuration(userSessionDuration time.Duration) {
service.userSessionTimeout = userSessionDuration
}

View File

@ -46,7 +46,6 @@ type (
EndpointURL *string EndpointURL *string
Labels *[]Pair Labels *[]Pair
Logo *string Logo *string
NoAuth *bool
NoAnalytics *bool NoAnalytics *bool
Templates *string Templates *string
TLS *bool TLS *bool
@ -459,6 +458,7 @@ type (
EnableHostManagementFeatures bool `json:"EnableHostManagementFeatures"` EnableHostManagementFeatures bool `json:"EnableHostManagementFeatures"`
EdgeAgentCheckinInterval int `json:"EdgeAgentCheckinInterval"` EdgeAgentCheckinInterval int `json:"EdgeAgentCheckinInterval"`
EnableEdgeComputeFeatures bool `json:"EnableEdgeComputeFeatures"` EnableEdgeComputeFeatures bool `json:"EnableEdgeComputeFeatures"`
UserSessionTimeout string `json:"UserSessionTimeout"`
// Deprecated fields // Deprecated fields
DisplayDonationHeader bool DisplayDonationHeader bool
@ -517,7 +517,6 @@ type (
// Status represents the application status // Status represents the application status
Status struct { Status struct {
Authentication bool `json:"Authentication"`
Analytics bool `json:"Analytics"` Analytics bool `json:"Analytics"`
Version string `json:"Version"` Version string `json:"Version"`
} }
@ -842,6 +841,7 @@ type (
JWTService interface { JWTService interface {
GenerateToken(data *TokenData) (string, error) GenerateToken(data *TokenData) (string, error)
ParseAndVerifyToken(token string) (*TokenData, error) ParseAndVerifyToken(token string) (*TokenData, error)
SetUserSessionDuration(userSessionDuration time.Duration)
} }
// LDAPService represents a service used to authenticate users against a LDAP/AD // LDAPService represents a service used to authenticate users against a LDAP/AD
@ -1057,6 +1057,8 @@ const (
LocalExtensionManifestFile = "/extensions.json" LocalExtensionManifestFile = "/extensions.json"
// DefaultTemplatesURL represents the URL to the official templates supported by Portainer // DefaultTemplatesURL represents the URL to the official templates supported by Portainer
DefaultTemplatesURL = "https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json" DefaultTemplatesURL = "https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json"
// DefaultUserSessionTimeout represents the default timeout after which the user session is cleared
DefaultUserSessionTimeout = "8h"
) )
const ( const (

View File

@ -90,7 +90,7 @@
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'CreatedAt' && $ctrl.state.reverseOrder"></i> <i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'CreatedAt' && $ctrl.state.reverseOrder"></i>
</a> </a>
</th> </th>
<th ng-if="$ctrl.showOwnershipColumn"> <th>
<a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')"> <a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')">
Ownership Ownership
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'ResourceControl.Ownership' && !$ctrl.state.reverseOrder"></i> <i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'ResourceControl.Ownership' && !$ctrl.state.reverseOrder"></i>
@ -112,7 +112,7 @@
<a ui-sref="docker.configs.config({id: item.Id})">{{ item.Name }}</a> <a ui-sref="docker.configs.config({id: item.Id})">{{ item.Name }}</a>
</td> </td>
<td>{{ item.CreatedAt | getisodate }}</td> <td>{{ item.CreatedAt | getisodate }}</td>
<td ng-if="$ctrl.showOwnershipColumn"> <td>
<span> <span>
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i> <i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }} {{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }}

View File

@ -8,7 +8,6 @@ angular.module('portainer.docker').component('configsDatatable', {
tableKey: '@', tableKey: '@',
orderBy: '@', orderBy: '@',
reverseOrder: '<', reverseOrder: '<',
showOwnershipColumn: '<',
removeAction: '<', removeAction: '<',
refreshCallback: '<', refreshCallback: '<',
}, },

View File

@ -246,7 +246,7 @@
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Ports' && $ctrl.state.reverseOrder"></i> <i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Ports' && $ctrl.state.reverseOrder"></i>
</a> </a>
</th> </th>
<th ng-if="$ctrl.showOwnershipColumn" ng-show="$ctrl.columnVisibility.columns.ownership.display"> <th ng-show="$ctrl.columnVisibility.columns.ownership.display">
<a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')"> <a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')">
Ownership Ownership
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'ResourceControl.Ownership' && !$ctrl.state.reverseOrder"></i> <i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'ResourceControl.Ownership' && !$ctrl.state.reverseOrder"></i>
@ -319,7 +319,7 @@
</a> </a>
<span ng-if="item.Ports.length == 0">-</span> <span ng-if="item.Ports.length == 0">-</span>
</td> </td>
<td ng-if="$ctrl.showOwnershipColumn" ng-show="$ctrl.columnVisibility.columns.ownership.display"> <td ng-show="$ctrl.columnVisibility.columns.ownership.display">
<span> <span>
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i> <i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }} {{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }}

View File

@ -8,7 +8,6 @@ angular.module('portainer.docker').component('containersDatatable', {
tableKey: '@', tableKey: '@',
orderBy: '@', orderBy: '@',
reverseOrder: '<', reverseOrder: '<',
showOwnershipColumn: '<',
showHostColumn: '<', showHostColumn: '<',
showAddAction: '<', showAddAction: '<',
offlineMode: '<', offlineMode: '<',

View File

@ -28,7 +28,7 @@
<td>{{ item.IPAM.IPV6Configs[0].Subnet ? item.IPAM.IPV6Configs[0].Subnet : '-' }}</td> <td>{{ item.IPAM.IPV6Configs[0].Subnet ? item.IPAM.IPV6Configs[0].Subnet : '-' }}</td>
<td>{{ item.IPAM.IPV6Configs[0].Gateway ? item.IPAM.IPV6Configs[0].Gateway : '-' }}</td> <td>{{ item.IPAM.IPV6Configs[0].Gateway ? item.IPAM.IPV6Configs[0].Gateway : '-' }}</td>
<td ng-if="parentCtrl.showHostColumn">{{ item.NodeName ? item.NodeName : '-' }}</td> <td ng-if="parentCtrl.showHostColumn">{{ item.NodeName ? item.NodeName : '-' }}</td>
<td ng-if="parentCtrl.showOwnershipColumn"> <td>
<span> <span>
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i> <i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = RCO.ADMINISTRATORS }} {{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = RCO.ADMINISTRATORS }}

View File

@ -151,7 +151,7 @@
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'NodeName' && $ctrl.state.reverseOrder"></i> <i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'NodeName' && $ctrl.state.reverseOrder"></i>
</a> </a>
</th> </th>
<th ng-if="$ctrl.showOwnershipColumn"> <th>
<a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')"> <a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')">
Ownership Ownership
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'ResourceControl.Ownership' && !$ctrl.state.reverseOrder"></i> <i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'ResourceControl.Ownership' && !$ctrl.state.reverseOrder"></i>

View File

@ -8,7 +8,6 @@ angular.module('portainer.docker').component('networksDatatable', {
tableKey: '@', tableKey: '@',
orderBy: '@', orderBy: '@',
reverseOrder: '<', reverseOrder: '<',
showOwnershipColumn: '<',
showHostColumn: '<', showHostColumn: '<',
removeAction: '<', removeAction: '<',
offlineMode: '<', offlineMode: '<',

View File

@ -90,7 +90,7 @@
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'CreatedAt' && $ctrl.state.reverseOrder"></i> <i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'CreatedAt' && $ctrl.state.reverseOrder"></i>
</a> </a>
</th> </th>
<th ng-if="$ctrl.showOwnershipColumn"> <th>
<a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')"> <a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')">
Ownership Ownership
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'ResourceControl.Ownership' && !$ctrl.state.reverseOrder"></i> <i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'ResourceControl.Ownership' && !$ctrl.state.reverseOrder"></i>
@ -112,7 +112,7 @@
<a ui-sref="docker.secrets.secret({id: item.Id})">{{ item.Name }}</a> <a ui-sref="docker.secrets.secret({id: item.Id})">{{ item.Name }}</a>
</td> </td>
<td>{{ item.CreatedAt | getisodate }}</td> <td>{{ item.CreatedAt | getisodate }}</td>
<td ng-if="$ctrl.showOwnershipColumn"> <td>
<span> <span>
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i> <i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }} {{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }}

View File

@ -8,7 +8,6 @@ angular.module('portainer.docker').component('secretsDatatable', {
tableKey: '@', tableKey: '@',
orderBy: '@', orderBy: '@',
reverseOrder: '<', reverseOrder: '<',
showOwnershipColumn: '<',
removeAction: '<', removeAction: '<',
refreshCallback: '<', refreshCallback: '<',
}, },

View File

@ -115,7 +115,7 @@
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'UpdatedAt' && $ctrl.state.reverseOrder"></i> <i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'UpdatedAt' && $ctrl.state.reverseOrder"></i>
</a> </a>
</th> </th>
<th ng-if="$ctrl.showOwnershipColumn"> <th>
<a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')"> <a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')">
Ownership Ownership
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'ResourceControl.Ownership' && !$ctrl.state.reverseOrder"></i> <i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'ResourceControl.Ownership' && !$ctrl.state.reverseOrder"></i>
@ -180,7 +180,7 @@
<span ng-if="!item.Ports || item.Ports.length === 0">-</span> <span ng-if="!item.Ports || item.Ports.length === 0">-</span>
</td> </td>
<td>{{ item.UpdatedAt | getisodate }}</td> <td>{{ item.UpdatedAt | getisodate }}</td>
<td ng-if="$ctrl.showOwnershipColumn"> <td>
<span> <span>
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i> <i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }} {{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }}

View File

@ -10,7 +10,6 @@ angular.module('portainer.docker').component('servicesDatatable', {
reverseOrder: '<', reverseOrder: '<',
nodes: '<', nodes: '<',
agentProxy: '<', agentProxy: '<',
showOwnershipColumn: '<',
showUpdateAction: '<', showUpdateAction: '<',
showAddAction: '<', showAddAction: '<',
showStackColumn: '<', showStackColumn: '<',

View File

@ -142,7 +142,7 @@
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'NodeName' && $ctrl.state.reverseOrder"></i> <i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'NodeName' && $ctrl.state.reverseOrder"></i>
</a> </a>
</th> </th>
<th ng-if="$ctrl.showOwnershipColumn"> <th>
<a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')"> <a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')">
Ownership Ownership
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'ResourceControl.Ownership' && !$ctrl.state.reverseOrder"></i> <i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'ResourceControl.Ownership' && !$ctrl.state.reverseOrder"></i>
@ -177,7 +177,7 @@
<td>{{ item.Mountpoint | truncatelr }}</td> <td>{{ item.Mountpoint | truncatelr }}</td>
<td>{{ item.CreatedAt | getisodate }}</td> <td>{{ item.CreatedAt | getisodate }}</td>
<td ng-if="$ctrl.showHostColumn">{{ item.NodeName ? item.NodeName : '-' }}</td> <td ng-if="$ctrl.showHostColumn">{{ item.NodeName ? item.NodeName : '-' }}</td>
<td ng-if="$ctrl.showOwnershipColumn"> <td>
<span> <span>
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i> <i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }} {{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }}

View File

@ -8,7 +8,6 @@ angular.module('portainer.docker').component('volumesDatatable', {
tableKey: '@', tableKey: '@',
orderBy: '@', orderBy: '@',
reverseOrder: '<', reverseOrder: '<',
showOwnershipColumn: '<',
showHostColumn: '<', showHostColumn: '<',
removeAction: '<', removeAction: '<',
showBrowseAction: '<', showBrowseAction: '<',

View File

@ -74,7 +74,7 @@
state="$ctrl.data.DatatableState" state="$ctrl.data.DatatableState"
order-by="Hostname" order-by="Hostname"
show-ip-address-column="$ctrl.applicationState.endpoint.apiVersion >= 1.25" show-ip-address-column="$ctrl.applicationState.endpoint.apiVersion >= 1.25"
access-to-node-details="!$ctrl.applicationState.application.authentication || $ctrl.isAdmin" access-to-node-details="$ctrl.isAdmin"
name="node_selector" name="node_selector"
ng-model="tmp" ng-model="tmp"
ng-required="$ctrl.requiredNodeSelection()" ng-required="$ctrl.requiredNodeSelection()"

View File

@ -23,10 +23,9 @@ angular.module('portainer.docker').controller('NetworkMacvlanFormController', [
}; };
function initComponent() { function initComponent() {
if (StateManager.getState().application.authentication) {
var isAdmin = Authentication.isAdmin(); var isAdmin = Authentication.isAdmin();
ctrl.isAdmin = isAdmin; ctrl.isAdmin = isAdmin;
}
var provider = ctrl.applicationState.endpoint.mode.provider; var provider = ctrl.applicationState.endpoint.mode.provider;
var apiVersion = ctrl.applicationState.endpoint.apiVersion; var apiVersion = ctrl.applicationState.endpoint.apiVersion;
$q.all({ $q.all({

View File

@ -15,7 +15,6 @@
dataset="ctrl.configs" dataset="ctrl.configs"
table-key="configs" table-key="configs"
order-by="Name" order-by="Name"
show-ownership-column="applicationState.application.authentication"
remove-action="ctrl.removeAction" remove-action="ctrl.removeAction"
refresh-callback="ctrl.getConfigs" refresh-callback="ctrl.getConfigs"
></configs-datatable> ></configs-datatable>

View File

@ -57,7 +57,7 @@
</div> </div>
<!-- !labels--> <!-- !labels-->
<!-- access-control --> <!-- access-control -->
<por-access-control-form form-data="ctrl.formValues.AccessControlData" ng-if="applicationState.application.authentication"></por-access-control-form> <por-access-control-form form-data="ctrl.formValues.AccessControlData"></por-access-control-form>
<!-- !access-control --> <!-- !access-control -->
<!-- actions --> <!-- actions -->
<div class="col-sm-12 form-section-title"> <div class="col-sm-12 form-section-title">

View File

@ -59,8 +59,7 @@
</div> </div>
<!-- access-control-panel --> <!-- access-control-panel -->
<por-access-control-panel ng-if="config && applicationState.application.authentication" resource-id="config.Id" resource-control="config.ResourceControl" resource-type="'config'"> <por-access-control-panel ng-if="config" resource-id="config.Id" resource-control="config.ResourceControl" resource-type="'config'"> </por-access-control-panel>
</por-access-control-panel>
<!-- !access-control-panel --> <!-- !access-control-panel -->
<div class="row" ng-if="config"> <div class="row" ng-if="config">

View File

@ -15,7 +15,6 @@
dataset="containers" dataset="containers"
table-key="containers" table-key="containers"
order-by="Status" order-by="Status"
show-ownership-column="applicationState.application.authentication"
show-host-column="applicationState.endpoint.mode.agentProxy && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'" show-host-column="applicationState.endpoint.mode.agentProxy && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'"
show-add-action="true" show-add-action="true"
offline-mode="offlineMode" offline-mode="offlineMode"

View File

@ -126,11 +126,7 @@
<!-- !node-selection --> <!-- !node-selection -->
</div> </div>
<!-- access-control --> <!-- access-control -->
<por-access-control-form <por-access-control-form form-data="formValues.AccessControlData" resource-control="fromContainer.ResourceControl" ng-if="fromContainer"></por-access-control-form>
form-data="formValues.AccessControlData"
resource-control="fromContainer.ResourceControl"
ng-if="applicationState.application.authentication && fromContainer"
></por-access-control-form>
<!-- !access-control --> <!-- !access-control -->
<!-- actions --> <!-- actions -->
<div class="col-sm-12 form-section-title"> <div class="col-sm-12 form-section-title">

View File

@ -135,13 +135,7 @@
</div> </div>
<!-- access-control-panel --> <!-- access-control-panel -->
<por-access-control-panel <por-access-control-panel ng-if="container" resource-id="container.Id" resource-control="container.ResourceControl" resource-type="'container'"> </por-access-control-panel>
ng-if="container && applicationState.application.authentication"
resource-id="container.Id"
resource-control="container.ResourceControl"
resource-type="'container'"
>
</por-access-control-panel>
<!-- !access-control-panel --> <!-- !access-control-panel -->
<div ng-if="container.State.Health" class="row"> <div ng-if="container.State.Health" class="row">

View File

@ -194,7 +194,7 @@
<!-- !node-selection --> <!-- !node-selection -->
</div> </div>
<!-- access-control --> <!-- access-control -->
<por-access-control-form form-data="formValues.AccessControlData" ng-if="applicationState.application.authentication"></por-access-control-form> <por-access-control-form form-data="formValues.AccessControlData"></por-access-control-form>
<!-- !access-control --> <!-- !access-control -->
<!-- actions --> <!-- actions -->
<div class="col-sm-12 form-section-title"> <div class="col-sm-12 form-section-title">

View File

@ -66,7 +66,7 @@
<!-- access-control-panel --> <!-- access-control-panel -->
<por-access-control-panel <por-access-control-panel
ng-if="network && applicationState.application.authentication" ng-if="network"
resource-id="network.Id" resource-id="network.Id"
resource-control="network.ResourceControl" resource-control="network.ResourceControl"
resource-type="'network'" resource-type="'network'"

View File

@ -16,7 +16,6 @@
table-key="networks" table-key="networks"
order-by="Name" order-by="Name"
remove-action="removeAction" remove-action="removeAction"
show-ownership-column="applicationState.application.authentication"
show-host-column="applicationState.endpoint.mode.agentProxy && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'" show-host-column="applicationState.endpoint.mode.agentProxy && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'"
offline-mode="offlineMode" offline-mode="offlineMode"
refresh-callback="getNetworks" refresh-callback="getNetworks"

View File

@ -61,7 +61,7 @@
</div> </div>
<!-- !labels--> <!-- !labels-->
<!-- access-control --> <!-- access-control -->
<por-access-control-form form-data="formValues.AccessControlData" ng-if="applicationState.application.authentication"></por-access-control-form> <por-access-control-form form-data="formValues.AccessControlData"></por-access-control-form>
<!-- !access-control --> <!-- !access-control -->
<!-- actions --> <!-- actions -->
<div class="col-sm-12 form-section-title"> <div class="col-sm-12 form-section-title">

View File

@ -56,6 +56,5 @@
</div> </div>
<!-- access-control-panel --> <!-- access-control-panel -->
<por-access-control-panel ng-if="secret && applicationState.application.authentication" resource-id="secret.Id" resource-control="secret.ResourceControl" resource-type="'secret'"> <por-access-control-panel ng-if="secret" resource-id="secret.Id" resource-control="secret.ResourceControl" resource-type="'secret'"> </por-access-control-panel>
</por-access-control-panel>
<!-- !access-control-panel --> <!-- !access-control-panel -->

View File

@ -15,7 +15,6 @@
dataset="secrets" dataset="secrets"
table-key="secrets" table-key="secrets"
order-by="Name" order-by="Name"
show-ownership-column="applicationState.application.authentication"
remove-action="removeAction" remove-action="removeAction"
refresh-callback="getSecrets" refresh-callback="getSecrets"
></secrets-datatable> ></secrets-datatable>

View File

@ -112,7 +112,7 @@
</div> </div>
<!-- !create-webhook --> <!-- !create-webhook -->
<!-- access-control --> <!-- access-control -->
<por-access-control-form form-data="formValues.AccessControlData" ng-if="applicationState.application.authentication"></por-access-control-form> <por-access-control-form form-data="formValues.AccessControlData"></por-access-control-form>
<!-- !access-control --> <!-- !access-control -->
<!-- actions --> <!-- actions -->
<div class="col-sm-12 form-section-title"> <div class="col-sm-12 form-section-title">

View File

@ -99,7 +99,6 @@
<td colspan="2"> <td colspan="2">
<p class="small text-muted" authorization="DockerServiceUpdate"> <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 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> ><p>
<a <a
authorization="DockerServiceLogs" authorization="DockerServiceLogs"
@ -199,13 +198,7 @@
</div> </div>
<!-- access-control-panel --> <!-- access-control-panel -->
<por-access-control-panel <por-access-control-panel ng-if="service" resource-id="service.Id" resource-control="service.ResourceControl" resource-type="'service'"> </por-access-control-panel>
ng-if="service && applicationState.application.authentication"
resource-id="service.Id"
resource-control="service.ResourceControl"
resource-type="'service'"
>
</por-access-control-panel>
<!-- !access-control-panel --> <!-- !access-control-panel -->
<div class="row"> <div class="row">

View File

@ -17,7 +17,6 @@
order-by="Name" order-by="Name"
nodes="nodes" nodes="nodes"
agent-proxy="applicationState.endpoint.mode.agentProxy" agent-proxy="applicationState.endpoint.mode.agentProxy"
show-ownership-column="applicationState.application.authentication"
show-update-action="applicationState.endpoint.apiVersion >= 1.25" show-update-action="applicationState.endpoint.apiVersion >= 1.25"
show-task-logs-button="applicationState.endpoint.apiVersion >= 1.30" show-task-logs-button="applicationState.endpoint.apiVersion >= 1.30"
show-add-action="true" show-add-action="true"

View File

@ -53,7 +53,7 @@
table-key="nodes" table-key="nodes"
order-by="Hostname" order-by="Hostname"
show-ip-address-column="applicationState.endpoint.apiVersion >= 1.25" show-ip-address-column="applicationState.endpoint.apiVersion >= 1.25"
access-to-node-details="!applicationState.application.authentication || isAdmin" access-to-node-details="isAdmin"
refresh-callback="getNodes" refresh-callback="getNodes"
></nodes-datatable> ></nodes-datatable>
</div> </div>

View File

@ -82,9 +82,7 @@ angular.module('portainer.docker').controller('SwarmController', [
} }
function initView() { function initView() {
if (StateManager.getState().application.authentication) {
$scope.isAdmin = Authentication.isAdmin(); $scope.isAdmin = Authentication.isAdmin();
}
var provider = $scope.applicationState.endpoint.mode.provider; var provider = $scope.applicationState.endpoint.mode.provider;
$q.all({ $q.all({

View File

@ -72,9 +72,7 @@
<input type="checkbox" name="useNFS" ng-model="formValues.NFSData.useNFS" ng-click="formValues.CIFSData.useCIFS = false" /> <input type="checkbox" name="useNFS" ng-model="formValues.NFSData.useNFS" ng-click="formValues.CIFSData.useCIFS = false" />
<i></i> <i></i>
</label> </label>
<div ng-if="formValues.NFSData.useNFS" class="small text-muted" style="margin-top: 10px;"> <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>
Ensure <code>nfs-utils</code> are installed on your hosts.
</div>
</div> </div>
<volumes-nfs-form data="formValues.NFSData" ng-show="formValues.Driver === 'local'"></volumes-nfs-form> <volumes-nfs-form data="formValues.NFSData" ng-show="formValues.Driver === 'local'"></volumes-nfs-form>
<!-- !nfs-management --> <!-- !nfs-management -->
@ -87,9 +85,7 @@
<input type="checkbox" name="useCIFS" ng-model="formValues.CIFSData.useCIFS" ng-click="formValues.NFSData.useNFS = false" /> <input type="checkbox" name="useCIFS" ng-model="formValues.CIFSData.useCIFS" ng-click="formValues.NFSData.useNFS = false" />
<i></i> <i></i>
</label> </label>
<div ng-if="formValues.CIFSData.useCIFS" class="small text-muted" style="margin-top: 10px;"> <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>
Ensure <code>cifs-utils</code> are installed on your hosts.
</div>
</div> </div>
<volumes-cifs-form data="formValues.CIFSData" ng-show="formValues.Driver === 'local'"></volumes-cifs-form> <volumes-cifs-form data="formValues.CIFSData" ng-show="formValues.Driver === 'local'"></volumes-cifs-form>
<!-- !cifs-management --> <!-- !cifs-management -->
@ -110,7 +106,7 @@
<!-- !node-selection --> <!-- !node-selection -->
</div> </div>
<!-- access-control --> <!-- access-control -->
<por-access-control-form form-data="formValues.AccessControlData" ng-if="applicationState.application.authentication"></por-access-control-form> <por-access-control-form form-data="formValues.AccessControlData"></por-access-control-form>
<!-- !access-control --> <!-- !access-control -->
<!-- actions --> <!-- actions -->
<div class="col-sm-12 form-section-title"> <div class="col-sm-12 form-section-title">

View File

@ -78,8 +78,7 @@
</div> </div>
<!-- access-control-panel --> <!-- access-control-panel -->
<por-access-control-panel ng-if="volume && applicationState.application.authentication" resource-id="volume.Id" resource-control="volume.ResourceControl" resource-type="'volume'"> <por-access-control-panel ng-if="volume" resource-id="volume.Id" resource-control="volume.ResourceControl" resource-type="'volume'"> </por-access-control-panel>
</por-access-control-panel>
<!-- !access-control-panel --> <!-- !access-control-panel -->
<div class="row" ng-if="!(volume.Options | emptyobject)"> <div class="row" ng-if="!(volume.Options | emptyobject)">

View File

@ -16,7 +16,6 @@
table-key="volumes" table-key="volumes"
order-by="Id" order-by="Id"
remove-action="removeAction" remove-action="removeAction"
show-ownership-column="applicationState.application.authentication"
show-host-column="applicationState.endpoint.mode.agentProxy && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'" show-host-column="applicationState.endpoint.mode.agentProxy && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'"
show-browse-action="showBrowseAction" show-browse-action="showBrowseAction"
offline-mode="offlineMode" offline-mode="offlineMode"

View File

@ -42,10 +42,7 @@ angular.module('portainer.extensions.registrymanagement').controller('RegistryRe
function initView() { function initView() {
const registryId = $transition$.params().id; const registryId = $transition$.params().id;
var authenticationEnabled = $scope.applicationState.application.authentication;
if (authenticationEnabled) {
$scope.isAdmin = Authentication.isAdmin(); $scope.isAdmin = Authentication.isAdmin();
}
RegistryService.registry(registryId) RegistryService.registry(registryId)
.then(function success(data) { .then(function success(data) {

View File

@ -55,9 +55,7 @@ angular.module('portainer.app', []).config([
if (state.application.analytics) { if (state.application.analytics) {
initAnalytics(Analytics, $rootScope); initAnalytics(Analytics, $rootScope);
} }
if (state.application.authentication) {
return $async(initAuthentication, authManager, Authentication, $rootScope, $state); return $async(initAuthentication, authManager, Authentication, $rootScope, $state);
}
}) })
.then(() => deferred.resolve()) .then(() => deferred.resolve())
.catch(function error(err) { .catch(function error(err) {

View File

@ -83,7 +83,7 @@
<td>{{ item.URL | stripprotocol }}</td> <td>{{ item.URL | stripprotocol }}</td>
<td>{{ item.GroupName }}</td> <td>{{ item.GroupName }}</td>
<td> <td>
<a ui-sref="portainer.endpoints.endpoint.access({id: item.Id})" ng-if="$ctrl.accessManagement"> <i class="fa fa-users" aria-hidden="true"></i> Manage access </a> <a ui-sref="portainer.endpoints.endpoint.access({id: item.Id})"> <i class="fa fa-users" aria-hidden="true"></i> Manage access </a>
</td> </td>
</tr> </tr>
<tr ng-if="$ctrl.state.loading"> <tr ng-if="$ctrl.state.loading">

View File

@ -7,7 +7,6 @@ angular.module('portainer.app').component('endpointsDatatable', {
tableKey: '@', tableKey: '@',
orderBy: '@', orderBy: '@',
reverseOrder: '<', reverseOrder: '<',
accessManagement: '<',
removeAction: '<', removeAction: '<',
retrievePage: '<', retrievePage: '<',
}, },

View File

@ -53,7 +53,7 @@
<a ui-sref="portainer.groups.group({id: item.Id})">{{ item.Name }}</a> <a ui-sref="portainer.groups.group({id: item.Id})">{{ item.Name }}</a>
</td> </td>
<td> <td>
<a ui-sref="portainer.groups.group.access({id: item.Id})" ng-if="$ctrl.accessManagement"> <i class="fa fa-users" aria-hidden="true"></i> Manage access </a> <a ui-sref="portainer.groups.group.access({id: item.Id})"> <i class="fa fa-users" aria-hidden="true"></i> Manage access </a>
</td> </td>
</tr> </tr>
<tr ng-if="!$ctrl.dataset"> <tr ng-if="!$ctrl.dataset">

View File

@ -8,7 +8,6 @@ angular.module('portainer.app').component('groupsDatatable', {
tableKey: '@', tableKey: '@',
orderBy: '@', orderBy: '@',
reverseOrder: '<', reverseOrder: '<',
accessManagement: '<',
removeAction: '<', removeAction: '<',
}, },
}); });

View File

@ -91,7 +91,7 @@
</a> </a>
</th> </th>
<th>Control</th> <th>Control</th>
<th ng-if="$ctrl.showOwnershipColumn"> <th>
<a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')"> <a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')">
Ownership Ownership
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'ResourceControl.Ownership' && !$ctrl.state.reverseOrder"></i> <i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'ResourceControl.Ownership' && !$ctrl.state.reverseOrder"></i>
@ -127,7 +127,7 @@
</span> </span>
<span ng-if="!item.External">Total</span> <span ng-if="!item.External">Total</span>
</td> </td>
<td ng-if="$ctrl.showOwnershipColumn"> <td>
<span> <span>
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i> <i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
{{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }} {{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }}

View File

@ -8,7 +8,6 @@ angular.module('portainer.app').component('stacksDatatable', {
tableKey: '@', tableKey: '@',
orderBy: '@', orderBy: '@',
reverseOrder: '<', reverseOrder: '<',
showOwnershipColumn: '<',
removeAction: '<', removeAction: '<',
offlineMode: '<', offlineMode: '<',
refreshCallback: '<', refreshCallback: '<',

View File

@ -12,6 +12,7 @@ export function SettingsViewModel(data) {
this.EnableHostManagementFeatures = data.EnableHostManagementFeatures; this.EnableHostManagementFeatures = data.EnableHostManagementFeatures;
this.EdgeAgentCheckinInterval = data.EdgeAgentCheckinInterval; this.EdgeAgentCheckinInterval = data.EdgeAgentCheckinInterval;
this.EnableEdgeComputeFeatures = data.EnableEdgeComputeFeatures; this.EnableEdgeComputeFeatures = data.EnableEdgeComputeFeatures;
this.UserSessionTimeout = data.UserSessionTimeout;
} }
export function PublicSettingsViewModel(settings) { export function PublicSettingsViewModel(settings) {

View File

@ -77,7 +77,6 @@ angular.module('portainer.app').factory('StateManager', [
}; };
function assignStateFromStatusAndSettings(status, settings) { function assignStateFromStatusAndSettings(status, settings) {
state.application.authentication = status.Authentication;
state.application.analytics = status.Analytics; state.application.analytics = status.Analytics;
state.application.version = status.Version; state.application.version = status.Version;
state.application.logo = settings.LogoURL; state.application.logo = settings.LogoURL;

View File

@ -128,10 +128,10 @@ class AuthenticationController {
} }
} }
async checkForEndpointsAsync(noAuth) { async checkForEndpointsAsync() {
try { try {
const endpoints = await this.EndpointService.endpoints(0, 1); const endpoints = await this.EndpointService.endpoints(0, 1);
const isAdmin = noAuth || this.Authentication.isAdmin(); const isAdmin = this.Authentication.isAdmin();
if (endpoints.value.length === 0 && isAdmin) { if (endpoints.value.length === 0 && isAdmin) {
return this.$state.go('portainer.init.endpoint'); return this.$state.go('portainer.init.endpoint');
@ -162,7 +162,7 @@ class AuthenticationController {
async postLoginSteps() { async postLoginSteps() {
await this.retrieveAndSaveEnabledExtensionsAsync(); await this.retrieveAndSaveEnabledExtensionsAsync();
await this.checkForEndpointsAsync(false); await this.checkForEndpointsAsync();
await this.checkForLatestVersionAsync(); await this.checkForLatestVersionAsync();
} }
/** /**
@ -282,12 +282,7 @@ class AuthenticationController {
} }
this.state.loginInProgress = false; this.state.loginInProgress = false;
const authenticationEnabled = this.$scope.applicationState.application.authentication;
if (!authenticationEnabled) {
await this.checkForEndpointsAsync(true);
} else {
await this.authEnabledFlowAsync(); await this.authEnabledFlowAsync();
}
} catch (err) { } catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve public settings'); this.Notifications.error('Failure', err, 'Unable to retrieve public settings');
} }

View File

@ -14,7 +14,6 @@
title-icon="fa-plug" title-icon="fa-plug"
table-key="endpoints" table-key="endpoints"
order-by="Name" order-by="Name"
access-management="applicationState.application.authentication"
remove-action="removeAction" remove-action="removeAction"
retrieve-page="getPaginatedEndpoints" retrieve-page="getPaginatedEndpoints"
></endpoints-datatable> ></endpoints-datatable>

View File

@ -9,14 +9,6 @@
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<groups-datatable <groups-datatable title-text="Endpoint groups" title-icon="fa-object-group" dataset="groups" table-key="groups" order-by="Name" remove-action="removeAction"></groups-datatable>
title-text="Endpoint groups"
title-icon="fa-object-group"
dataset="groups"
table-key="groups"
order-by="Name"
access-management="applicationState.application.authentication"
remove-action="removeAction"
></groups-datatable>
</div> </div>
</div> </div>

View File

@ -37,7 +37,7 @@
table-key="home_endpoints" table-key="home_endpoints"
tags="tags" tags="tags"
dashboard-action="goToDashboard" dashboard-action="goToDashboard"
show-snapshot-action="!applicationState.application.authentication || isAdmin" show-snapshot-action="isAdmin"
snapshot-action="triggerSnapshot" snapshot-action="triggerSnapshot"
edit-action="goToEdit" edit-action="goToEdit"
is-admin="isAdmin" is-admin="isAdmin"

View File

@ -7,7 +7,7 @@
<rd-header-content>Registry management</rd-header-content> <rd-header-content>Registry management</rd-header-content>
</rd-header> </rd-header>
<div class="row" ng-if="dockerhub && (!applicationState.application.authentication || isAdmin)"> <div class="row" ng-if="dockerhub && isAdmin">
<div class="col-sm-12"> <div class="col-sm-12">
<rd-widget> <rd-widget>
<rd-widget-header icon="fa-database" title-text="DockerHub"> </rd-widget-header> <rd-widget-header icon="fa-database" title-text="DockerHub"> </rd-widget-header>
@ -79,7 +79,7 @@
dataset="registries" dataset="registries"
table-key="registries" table-key="registries"
order-by="Name" order-by="Name"
access-management="!applicationState.application.authentication || isAdmin" access-management="isAdmin"
remove-action="removeAction" remove-action="removeAction"
registry-management="registryManagementAvailable" registry-management="registryManagementAvailable"
can-browse="canBrowse" can-browse="canBrowse"

View File

@ -81,10 +81,7 @@ angular.module('portainer.app').controller('RegistriesController', [
$scope.registries = data.registries; $scope.registries = data.registries;
$scope.dockerhub = data.dockerhub; $scope.dockerhub = data.dockerhub;
$scope.registryManagementAvailable = data.registryManagement; $scope.registryManagementAvailable = data.registryManagement;
var authenticationEnabled = $scope.applicationState.application.authentication;
if (authenticationEnabled) {
$scope.isAdmin = Authentication.isAdmin(); $scope.isAdmin = Authentication.isAdmin();
}
}) })
.catch(function error(err) { .catch(function error(err) {
$scope.registries = []; $scope.registries = [];

View File

@ -9,6 +9,28 @@
<rd-widget-header icon="fa-users" title-text="Authentication"></rd-widget-header> <rd-widget-header icon="fa-users" title-text="Authentication"></rd-widget-header>
<rd-widget-body> <rd-widget-body>
<form class="form-horizontal"> <form class="form-horizontal">
<div class="col-sm-12 form-section-title">
Configuration
</div>
<div class="form-group">
<label for="user_timeout" class="col-sm-2 control-label text-left">
Session lifetime
<portainer-tooltip message="Time before users are forced to relogin."></portainer-tooltip>
</label>
<div class="col-sm-10">
<select
id="user_timeout"
class="form-control"
ng-model="settings.UserSessionTimeout"
ng-options="opt.value as opt.key for opt in state.availableUserSessionTimeoutOptions"
></select>
</div>
</div>
<div class="form-group">
<span class="col-sm-12 text-muted small">
Changing from default is only recommended if you have additional layers of authentication in front of Portainer.
</span>
</div>
<div class="col-sm-12 form-section-title"> <div class="col-sm-12 form-section-title">
Authentication method Authentication method
</div> </div>

View File

@ -14,9 +14,32 @@ angular.module('portainer.app').controller('SettingsAuthenticationController', [
uploadInProgress: false, uploadInProgress: false,
connectivityCheckInProgress: false, connectivityCheckInProgress: false,
actionInProgress: false, actionInProgress: false,
availableUserSessionTimeoutOptions: [
{
key: '1 hour',
value: '1h',
},
{
key: '4 hours',
value: '4h',
},
{
key: '8 hours',
value: '8h',
},
{
key: '24 hours',
value: '24h',
},
{ key: '1 week', value: `${24 * 7}h` },
{ key: '1 month', value: `${24 * 30}h` },
{ key: '6 months', value: `${24 * 30 * 6}h` },
{ key: '1 year', value: `${24 * 30 * 12}h` },
],
}; };
$scope.formValues = { $scope.formValues = {
UserSessionTimeout: $scope.state.availableUserSessionTimeoutOptions[0],
TLSCACert: '', TLSCACert: '',
LDAPSettings: { LDAPSettings: {
AnonymousMode: true, AnonymousMode: true,

View File

@ -19,7 +19,7 @@
endpoint-api-version="applicationState.endpoint.apiVersion" endpoint-api-version="applicationState.endpoint.apiVersion"
swarm-management="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER'" swarm-management="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER'"
standalone-management="applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE' || applicationState.endpoint.mode.provider === 'VMWARE_VIC'" standalone-management="applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE' || applicationState.endpoint.mode.provider === 'VMWARE_VIC'"
admin-access="!applicationState.application.authentication || isAdmin" admin-access="isAdmin"
offline-mode="endpointState.OfflineMode" offline-mode="endpointState.OfflineMode"
></docker-sidebar-content> ></docker-sidebar-content>
<li class="sidebar-title" authorization="IntegrationStoridgeAdmin" ng-if="applicationState.endpoint.mode && applicationState.endpoint.extensions.length > 0"> <li class="sidebar-title" authorization="IntegrationStoridgeAdmin" ng-if="applicationState.endpoint.mode && applicationState.endpoint.extensions.length > 0">
@ -85,10 +85,10 @@
<a ui-sref="storidge.drives" ui-sref-active="active">Drives</a> <a ui-sref="storidge.drives" ui-sref-active="active">Drives</a>
</div> </div>
</li> </li>
<li class="sidebar-title" ng-if="(!applicationState.application.authentication || isAdmin) && applicationState.application.enableHostManagementFeatures"> <li class="sidebar-title" ng-if="isAdmin && applicationState.application.enableHostManagementFeatures">
<span>Scheduler</span> <span>Scheduler</span>
</li> </li>
<li class="sidebar-list" ng-if="(!applicationState.application.authentication || isAdmin) && applicationState.application.enableHostManagementFeatures"> <li class="sidebar-list" ng-if="isAdmin && applicationState.application.enableHostManagementFeatures">
<a ui-sref="portainer.schedules" ui-sref-active="active">Host jobs <span class="menu-icon fa fa-clock fa-fw"></span></a> <a ui-sref="portainer.schedules" ui-sref-active="active">Host jobs <span class="menu-icon fa fa-clock fa-fw"></span></a>
</li> </li>
<li class="sidebar-title" ng-if="isAdmin && applicationState.application.enableEdgeComputeFeatures"> <li class="sidebar-title" ng-if="isAdmin && applicationState.application.enableEdgeComputeFeatures">
@ -103,10 +103,10 @@
<li class="sidebar-title"> <li class="sidebar-title">
<span>Settings</span> <span>Settings</span>
</li> </li>
<li class="sidebar-list" ng-if="!applicationState.application.authentication || isAdmin"> <li class="sidebar-list" ng-if="isAdmin">
<a ui-sref="portainer.extensions" ui-sref-active="active">Extensions <span class="menu-icon fa fa-bolt fa-fw"></span></a> <a ui-sref="portainer.extensions" ui-sref-active="active">Extensions <span class="menu-icon fa fa-bolt fa-fw"></span></a>
</li> </li>
<li class="sidebar-list" ng-if="applicationState.application.authentication && (isAdmin || isTeamLeader)"> <li class="sidebar-list" ng-if="isAdmin || isTeamLeader">
<a ui-sref="portainer.users" ui-sref-active="active">Users <span class="menu-icon fa fa-users fa-fw"></span></a> <a ui-sref="portainer.users" ui-sref-active="active">Users <span class="menu-icon fa fa-users fa-fw"></span></a>
<div <div
class="sidebar-sublist" class="sidebar-sublist"
@ -139,7 +139,7 @@
<a ui-sref="portainer.roles" ui-sref-active="active">Roles</a> <a ui-sref="portainer.roles" ui-sref-active="active">Roles</a>
</div> </div>
</li> </li>
<li class="sidebar-list" ng-if="!applicationState.application.authentication || isAdmin"> <li class="sidebar-list" ng-if="isAdmin">
<a ui-sref="portainer.endpoints" ui-sref-active="active">Endpoints <span class="menu-icon fa fa-plug fa-fw"></span></a> <a ui-sref="portainer.endpoints" ui-sref-active="active">Endpoints <span class="menu-icon fa fa-plug fa-fw"></span></a>
<div <div
class="sidebar-sublist" class="sidebar-sublist"
@ -179,14 +179,13 @@
<li class="sidebar-list"> <li class="sidebar-list">
<a ui-sref="portainer.registries" ui-sref-active="active">Registries <span class="menu-icon fa fa-database fa-fw"></span></a> <a ui-sref="portainer.registries" ui-sref-active="active">Registries <span class="menu-icon fa fa-database fa-fw"></span></a>
</li> </li>
<li class="sidebar-list" ng-if="!applicationState.application.authentication || isAdmin"> <li class="sidebar-list" ng-if="isAdmin">
<a ui-sref="portainer.settings" ui-sref-active="active">Settings <span class="menu-icon fa fa-cogs fa-fw"></span></a> <a ui-sref="portainer.settings" ui-sref-active="active">Settings <span class="menu-icon fa fa-cogs fa-fw"></span></a>
<div <div
class="sidebar-sublist" class="sidebar-sublist"
ng-if=" ng-if="
toggle && toggle &&
($state.current.name === 'portainer.settings' || $state.current.name === 'portainer.settings.authentication' || $state.current.name === 'portainer.about') && ($state.current.name === 'portainer.settings' || $state.current.name === 'portainer.settings.authentication' || $state.current.name === 'portainer.about') &&
applicationState.application.authentication &&
isAdmin isAdmin
" "
> >

View File

@ -20,8 +20,6 @@ angular.module('portainer.app').controller('SidebarController', [
$scope.uiVersion = StateManager.getState().application.version; $scope.uiVersion = StateManager.getState().application.version;
$scope.logo = StateManager.getState().application.logo; $scope.logo = StateManager.getState().application.logo;
var authenticationEnabled = $scope.applicationState.application.authentication;
if (authenticationEnabled) {
let userDetails = Authentication.getUserDetails(); let userDetails = Authentication.getUserDetails();
let isAdmin = Authentication.isAdmin(); let isAdmin = Authentication.isAdmin();
$scope.isAdmin = isAdmin; $scope.isAdmin = isAdmin;
@ -34,7 +32,6 @@ angular.module('portainer.app').controller('SidebarController', [
Notifications.error('Failure', err, 'Unable to retrieve user memberships'); Notifications.error('Failure', err, 'Unable to retrieve user memberships');
}); });
} }
}
initView(); initView();
}, },

View File

@ -214,7 +214,7 @@
</div> </div>
<!-- !environment-variables --> <!-- !environment-variables -->
<!-- !repository --> <!-- !repository -->
<por-access-control-form form-data="formValues.AccessControlData" ng-if="applicationState.application.authentication"></por-access-control-form> <por-access-control-form form-data="formValues.AccessControlData"></por-access-control-form>
<!-- actions --> <!-- actions -->
<div class="col-sm-12 form-section-title"> <div class="col-sm-12 form-section-title">
Actions Actions

View File

@ -154,7 +154,6 @@
dataset="containers" dataset="containers"
table-key="stack-containers" table-key="stack-containers"
order-by="Status" order-by="Status"
show-ownership-column="applicationState.application.authentication"
show-host-column="false" show-host-column="false"
show-add-action="false" show-add-action="false"
></containers-datatable> ></containers-datatable>
@ -181,6 +180,5 @@
</div> </div>
<!-- access-control-panel --> <!-- access-control-panel -->
<por-access-control-panel ng-if="stack && applicationState.application.authentication" resource-id="stack.Name" resource-control="stack.ResourceControl" resource-type="'stack'"> <por-access-control-panel ng-if="stack" resource-id="stack.Name" resource-control="stack.ResourceControl" resource-type="'stack'"> </por-access-control-panel>
</por-access-control-panel>
<!-- !access-control-panel --> <!-- !access-control-panel -->

View File

@ -16,7 +16,6 @@
table-key="stacks" table-key="stacks"
order-by="Name" order-by="Name"
remove-action="removeAction" remove-action="removeAction"
show-ownership-column="applicationState.application.authentication"
offline-mode="offlineMode" offline-mode="offlineMode"
refresh-callback="getStacks" refresh-callback="getStacks"
></stacks-datatable> ></stacks-datatable>

View File

@ -53,7 +53,7 @@
</div> </div>
<!-- !env --> <!-- !env -->
<!-- access-control --> <!-- access-control -->
<por-access-control-form form-data="formValues.AccessControlData" ng-if="applicationState.application.authentication"></por-access-control-form> <por-access-control-form form-data="formValues.AccessControlData"></por-access-control-form>
<!-- !access-control --> <!-- !access-control -->
<!-- actions --> <!-- actions -->
<div class="col-sm-12 form-section-title"> <div class="col-sm-12 form-section-title">
@ -136,7 +136,7 @@
</div> </div>
<!-- !env --> <!-- !env -->
<!-- access-control --> <!-- access-control -->
<por-access-control-form form-data="formValues.AccessControlData" ng-if="applicationState.application.authentication"></por-access-control-form> <por-access-control-form form-data="formValues.AccessControlData"></por-access-control-form>
<!-- !access-control --> <!-- !access-control -->
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">