From 7c3b83f6e5e8074a5dc20e4db40951fad2852735 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Tue, 16 Jun 2020 10:58:16 +0300 Subject: [PATCH] refactor(portainer): introduce internal package (#3924) * refactor(auth): move auth helpers to internal package * refactor(edge-compute): move edge helpers to internal package * refactor(tags): move tags helper to internal package * style(portainer): sort imports --- api/authorizations.go | 774 ----------------- api/bolt/datastore.go | 3 +- api/bolt/init.go | 13 +- api/bolt/migrator/migrate_dbversion20.go | 15 +- api/bolt/migrator/migrator.go | 5 +- api/cmd/portainer/main.go | 3 +- api/edgegroup.go | 54 -- api/http/handler/auth/authenticate.go | 3 +- api/http/handler/auth/authenticate_oauth.go | 3 +- api/http/handler/auth/handler.go | 3 +- .../handler/edgegroups/edgegroup_update.go | 9 +- .../handler/edgestacks/edgestack_create.go | 3 +- .../handler/edgestacks/edgestack_delete.go | 3 +- .../handler/edgestacks/edgestack_update.go | 5 +- .../endpointgroups/endpointgroup_update.go | 11 +- api/http/handler/endpointgroups/endpoints.go | 7 +- api/http/handler/endpointgroups/handler.go | 3 +- api/http/handler/endpoints/endpoint_create.go | 3 +- api/http/handler/endpoints/endpoint_update.go | 14 +- api/http/handler/endpoints/handler.go | 3 +- api/http/handler/extensions/handler.go | 3 +- api/http/handler/settings/handler.go | 3 +- api/http/handler/stacks/handler.go | 3 +- api/http/handler/stacks/stack_create.go | 3 +- api/http/handler/stacks/stack_list.go | 5 +- api/http/handler/tags/tag_delete.go | 3 +- api/http/handler/teammemberships/handler.go | 3 +- api/http/handler/teams/handler.go | 3 +- api/http/handler/users/admin_init.go | 3 +- api/http/handler/users/handler.go | 3 +- api/http/handler/users/user_create.go | 3 +- .../proxy/factory/docker/access_control.go | 19 +- api/http/proxy/factory/docker/configs.go | 3 +- api/http/proxy/factory/docker/containers.go | 7 +- api/http/proxy/factory/docker/networks.go | 5 +- api/http/proxy/factory/docker/secrets.go | 3 +- api/http/proxy/factory/docker/services.go | 3 +- api/http/proxy/factory/docker/transport.go | 12 +- api/http/proxy/factory/docker/volumes.go | 5 +- api/http/server.go | 3 +- .../authorization}/access_control.go | 60 +- api/internal/authorization/authorizations.go | 776 ++++++++++++++++++ api/internal/edge/edgegroup.go | 59 ++ api/{ => internal/edge}/edgestack.go | 13 +- api/{ => internal/edge}/endpoint.go | 10 +- api/{ => internal/tag}/tag.go | 28 +- 46 files changed, 1019 insertions(+), 959 deletions(-) delete mode 100644 api/authorizations.go delete mode 100644 api/edgegroup.go rename api/{ => internal/authorization}/access_control.go (66%) create mode 100644 api/internal/authorization/authorizations.go create mode 100644 api/internal/edge/edgegroup.go rename api/{ => internal/edge}/edgestack.go (56%) rename api/{ => internal/edge}/endpoint.go (58%) rename api/{ => internal/tag}/tag.go (54%) diff --git a/api/authorizations.go b/api/authorizations.go deleted file mode 100644 index 78b84b6bf..000000000 --- a/api/authorizations.go +++ /dev/null @@ -1,774 +0,0 @@ -package portainer - -// AuthorizationService represents a service used to -// update authorizations associated to a user or team. -type AuthorizationService struct { - dataStore DataStore -} - -// NewAuthorizationService returns a point to a new AuthorizationService instance. -func NewAuthorizationService(dataStore DataStore) *AuthorizationService { - return &AuthorizationService{ - dataStore: dataStore, - } -} - -// DefaultEndpointAuthorizationsForEndpointAdministratorRole returns the default endpoint authorizations -// associated to the endpoint administrator role. -func DefaultEndpointAuthorizationsForEndpointAdministratorRole() Authorizations { - return map[Authorization]bool{ - OperationDockerContainerArchiveInfo: true, - OperationDockerContainerList: true, - OperationDockerContainerExport: true, - OperationDockerContainerChanges: true, - OperationDockerContainerInspect: true, - OperationDockerContainerTop: true, - OperationDockerContainerLogs: true, - OperationDockerContainerStats: true, - OperationDockerContainerAttachWebsocket: true, - OperationDockerContainerArchive: true, - OperationDockerContainerCreate: true, - OperationDockerContainerPrune: true, - OperationDockerContainerKill: true, - OperationDockerContainerPause: true, - OperationDockerContainerUnpause: true, - OperationDockerContainerRestart: true, - OperationDockerContainerStart: true, - OperationDockerContainerStop: true, - OperationDockerContainerWait: true, - OperationDockerContainerResize: true, - OperationDockerContainerAttach: true, - OperationDockerContainerExec: true, - OperationDockerContainerRename: true, - OperationDockerContainerUpdate: true, - OperationDockerContainerPutContainerArchive: true, - OperationDockerContainerDelete: true, - OperationDockerImageList: true, - OperationDockerImageSearch: true, - OperationDockerImageGetAll: true, - OperationDockerImageGet: true, - OperationDockerImageHistory: true, - OperationDockerImageInspect: true, - OperationDockerImageLoad: true, - OperationDockerImageCreate: true, - OperationDockerImagePrune: true, - OperationDockerImagePush: true, - OperationDockerImageTag: true, - OperationDockerImageDelete: true, - OperationDockerImageCommit: true, - OperationDockerImageBuild: true, - OperationDockerNetworkList: true, - OperationDockerNetworkInspect: true, - OperationDockerNetworkCreate: true, - OperationDockerNetworkConnect: true, - OperationDockerNetworkDisconnect: true, - OperationDockerNetworkPrune: true, - OperationDockerNetworkDelete: true, - OperationDockerVolumeList: true, - OperationDockerVolumeInspect: true, - OperationDockerVolumeCreate: true, - OperationDockerVolumePrune: true, - OperationDockerVolumeDelete: true, - OperationDockerExecInspect: true, - OperationDockerExecStart: true, - OperationDockerExecResize: true, - OperationDockerSwarmInspect: true, - OperationDockerSwarmUnlockKey: true, - OperationDockerSwarmInit: true, - OperationDockerSwarmJoin: true, - OperationDockerSwarmLeave: true, - OperationDockerSwarmUpdate: true, - OperationDockerSwarmUnlock: true, - OperationDockerNodeList: true, - OperationDockerNodeInspect: true, - OperationDockerNodeUpdate: true, - OperationDockerNodeDelete: true, - OperationDockerServiceList: true, - OperationDockerServiceInspect: true, - OperationDockerServiceLogs: true, - OperationDockerServiceCreate: true, - OperationDockerServiceUpdate: true, - OperationDockerServiceDelete: true, - OperationDockerSecretList: true, - OperationDockerSecretInspect: true, - OperationDockerSecretCreate: true, - OperationDockerSecretUpdate: true, - OperationDockerSecretDelete: true, - OperationDockerConfigList: true, - OperationDockerConfigInspect: true, - OperationDockerConfigCreate: true, - OperationDockerConfigUpdate: true, - OperationDockerConfigDelete: true, - OperationDockerTaskList: true, - OperationDockerTaskInspect: true, - OperationDockerTaskLogs: true, - OperationDockerPluginList: true, - OperationDockerPluginPrivileges: true, - OperationDockerPluginInspect: true, - OperationDockerPluginPull: true, - OperationDockerPluginCreate: true, - OperationDockerPluginEnable: true, - OperationDockerPluginDisable: true, - OperationDockerPluginPush: true, - OperationDockerPluginUpgrade: true, - OperationDockerPluginSet: true, - OperationDockerPluginDelete: true, - OperationDockerSessionStart: true, - OperationDockerDistributionInspect: true, - OperationDockerBuildPrune: true, - OperationDockerBuildCancel: true, - OperationDockerPing: true, - OperationDockerInfo: true, - OperationDockerVersion: true, - OperationDockerEvents: true, - OperationDockerSystem: true, - OperationDockerUndefined: true, - OperationDockerAgentPing: true, - OperationDockerAgentList: true, - OperationDockerAgentHostInfo: true, - OperationDockerAgentBrowseDelete: true, - OperationDockerAgentBrowseGet: true, - OperationDockerAgentBrowseList: true, - OperationDockerAgentBrowsePut: true, - OperationDockerAgentBrowseRename: true, - OperationDockerAgentUndefined: true, - OperationPortainerResourceControlCreate: true, - OperationPortainerResourceControlUpdate: true, - OperationPortainerStackList: true, - OperationPortainerStackInspect: true, - OperationPortainerStackFile: true, - OperationPortainerStackCreate: true, - OperationPortainerStackMigrate: true, - OperationPortainerStackUpdate: true, - OperationPortainerStackDelete: true, - OperationPortainerWebsocketExec: true, - OperationPortainerWebhookList: true, - OperationPortainerWebhookCreate: true, - OperationPortainerWebhookDelete: true, - OperationIntegrationStoridgeAdmin: true, - EndpointResourcesAccess: true, - } -} - -// DefaultEndpointAuthorizationsForHelpDeskRole returns the default endpoint authorizations -// associated to the helpdesk role. -func DefaultEndpointAuthorizationsForHelpDeskRole(volumeBrowsingAuthorizations bool) Authorizations { - authorizations := map[Authorization]bool{ - OperationDockerContainerArchiveInfo: true, - OperationDockerContainerList: true, - OperationDockerContainerChanges: true, - OperationDockerContainerInspect: true, - OperationDockerContainerTop: true, - OperationDockerContainerLogs: true, - OperationDockerContainerStats: true, - OperationDockerImageList: true, - OperationDockerImageSearch: true, - OperationDockerImageGetAll: true, - OperationDockerImageGet: true, - OperationDockerImageHistory: true, - OperationDockerImageInspect: true, - OperationDockerNetworkList: true, - OperationDockerNetworkInspect: true, - OperationDockerVolumeList: true, - OperationDockerVolumeInspect: true, - OperationDockerSwarmInspect: true, - OperationDockerNodeList: true, - OperationDockerNodeInspect: true, - OperationDockerServiceList: true, - OperationDockerServiceInspect: true, - OperationDockerServiceLogs: true, - OperationDockerSecretList: true, - OperationDockerSecretInspect: true, - OperationDockerConfigList: true, - OperationDockerConfigInspect: true, - OperationDockerTaskList: true, - OperationDockerTaskInspect: true, - OperationDockerTaskLogs: true, - OperationDockerPluginList: true, - OperationDockerDistributionInspect: true, - OperationDockerPing: true, - OperationDockerInfo: true, - OperationDockerVersion: true, - OperationDockerEvents: true, - OperationDockerSystem: true, - OperationDockerAgentPing: true, - OperationDockerAgentList: true, - OperationDockerAgentHostInfo: true, - OperationPortainerStackList: true, - OperationPortainerStackInspect: true, - OperationPortainerStackFile: true, - OperationPortainerWebhookList: true, - EndpointResourcesAccess: true, - } - - if volumeBrowsingAuthorizations { - authorizations[OperationDockerAgentBrowseGet] = true - authorizations[OperationDockerAgentBrowseList] = true - } - - return authorizations -} - -// DefaultEndpointAuthorizationsForStandardUserRole returns the default endpoint authorizations -// associated to the standard user role. -func DefaultEndpointAuthorizationsForStandardUserRole(volumeBrowsingAuthorizations bool) Authorizations { - authorizations := map[Authorization]bool{ - OperationDockerContainerArchiveInfo: true, - OperationDockerContainerList: true, - OperationDockerContainerExport: true, - OperationDockerContainerChanges: true, - OperationDockerContainerInspect: true, - OperationDockerContainerTop: true, - OperationDockerContainerLogs: true, - OperationDockerContainerStats: true, - OperationDockerContainerAttachWebsocket: true, - OperationDockerContainerArchive: true, - OperationDockerContainerCreate: true, - OperationDockerContainerKill: true, - OperationDockerContainerPause: true, - OperationDockerContainerUnpause: true, - OperationDockerContainerRestart: true, - OperationDockerContainerStart: true, - OperationDockerContainerStop: true, - OperationDockerContainerWait: true, - OperationDockerContainerResize: true, - OperationDockerContainerAttach: true, - OperationDockerContainerExec: true, - OperationDockerContainerRename: true, - OperationDockerContainerUpdate: true, - OperationDockerContainerPutContainerArchive: true, - OperationDockerContainerDelete: true, - OperationDockerImageList: true, - OperationDockerImageSearch: true, - OperationDockerImageGetAll: true, - OperationDockerImageGet: true, - OperationDockerImageHistory: true, - OperationDockerImageInspect: true, - OperationDockerImageLoad: true, - OperationDockerImageCreate: true, - OperationDockerImagePush: true, - OperationDockerImageTag: true, - OperationDockerImageDelete: true, - OperationDockerImageCommit: true, - OperationDockerImageBuild: true, - OperationDockerNetworkList: true, - OperationDockerNetworkInspect: true, - OperationDockerNetworkCreate: true, - OperationDockerNetworkConnect: true, - OperationDockerNetworkDisconnect: true, - OperationDockerNetworkDelete: true, - OperationDockerVolumeList: true, - OperationDockerVolumeInspect: true, - OperationDockerVolumeCreate: true, - OperationDockerVolumeDelete: true, - OperationDockerExecInspect: true, - OperationDockerExecStart: true, - OperationDockerExecResize: true, - OperationDockerSwarmInspect: true, - OperationDockerSwarmUnlockKey: true, - OperationDockerSwarmInit: true, - OperationDockerSwarmJoin: true, - OperationDockerSwarmLeave: true, - OperationDockerSwarmUpdate: true, - OperationDockerSwarmUnlock: true, - OperationDockerNodeList: true, - OperationDockerNodeInspect: true, - OperationDockerNodeUpdate: true, - OperationDockerNodeDelete: true, - OperationDockerServiceList: true, - OperationDockerServiceInspect: true, - OperationDockerServiceLogs: true, - OperationDockerServiceCreate: true, - OperationDockerServiceUpdate: true, - OperationDockerServiceDelete: true, - OperationDockerSecretList: true, - OperationDockerSecretInspect: true, - OperationDockerSecretCreate: true, - OperationDockerSecretUpdate: true, - OperationDockerSecretDelete: true, - OperationDockerConfigList: true, - OperationDockerConfigInspect: true, - OperationDockerConfigCreate: true, - OperationDockerConfigUpdate: true, - OperationDockerConfigDelete: true, - OperationDockerTaskList: true, - OperationDockerTaskInspect: true, - OperationDockerTaskLogs: true, - OperationDockerPluginList: true, - OperationDockerPluginPrivileges: true, - OperationDockerPluginInspect: true, - OperationDockerPluginPull: true, - OperationDockerPluginCreate: true, - OperationDockerPluginEnable: true, - OperationDockerPluginDisable: true, - OperationDockerPluginPush: true, - OperationDockerPluginUpgrade: true, - OperationDockerPluginSet: true, - OperationDockerPluginDelete: true, - OperationDockerSessionStart: true, - OperationDockerDistributionInspect: true, - OperationDockerBuildPrune: true, - OperationDockerBuildCancel: true, - OperationDockerPing: true, - OperationDockerInfo: true, - OperationDockerVersion: true, - OperationDockerEvents: true, - OperationDockerSystem: true, - OperationDockerUndefined: true, - OperationDockerAgentPing: true, - OperationDockerAgentList: true, - OperationDockerAgentHostInfo: true, - OperationDockerAgentUndefined: true, - OperationPortainerResourceControlUpdate: true, - OperationPortainerStackList: true, - OperationPortainerStackInspect: true, - OperationPortainerStackFile: true, - OperationPortainerStackCreate: true, - OperationPortainerStackMigrate: true, - OperationPortainerStackUpdate: true, - OperationPortainerStackDelete: true, - OperationPortainerWebsocketExec: true, - OperationPortainerWebhookList: true, - OperationPortainerWebhookCreate: true, - } - - if volumeBrowsingAuthorizations { - authorizations[OperationDockerAgentBrowseGet] = true - authorizations[OperationDockerAgentBrowseList] = true - authorizations[OperationDockerAgentBrowseDelete] = true - authorizations[OperationDockerAgentBrowsePut] = true - authorizations[OperationDockerAgentBrowseRename] = true - } - - return authorizations -} - -// DefaultEndpointAuthorizationsForReadOnlyUserRole returns the default endpoint authorizations -// associated to the readonly user role. -func DefaultEndpointAuthorizationsForReadOnlyUserRole(volumeBrowsingAuthorizations bool) Authorizations { - authorizations := map[Authorization]bool{ - OperationDockerContainerArchiveInfo: true, - OperationDockerContainerList: true, - OperationDockerContainerChanges: true, - OperationDockerContainerInspect: true, - OperationDockerContainerTop: true, - OperationDockerContainerLogs: true, - OperationDockerContainerStats: true, - OperationDockerImageList: true, - OperationDockerImageSearch: true, - OperationDockerImageGetAll: true, - OperationDockerImageGet: true, - OperationDockerImageHistory: true, - OperationDockerImageInspect: true, - OperationDockerNetworkList: true, - OperationDockerNetworkInspect: true, - OperationDockerVolumeList: true, - OperationDockerVolumeInspect: true, - OperationDockerSwarmInspect: true, - OperationDockerNodeList: true, - OperationDockerNodeInspect: true, - OperationDockerServiceList: true, - OperationDockerServiceInspect: true, - OperationDockerServiceLogs: true, - OperationDockerSecretList: true, - OperationDockerSecretInspect: true, - OperationDockerConfigList: true, - OperationDockerConfigInspect: true, - OperationDockerTaskList: true, - OperationDockerTaskInspect: true, - OperationDockerTaskLogs: true, - OperationDockerPluginList: true, - OperationDockerDistributionInspect: true, - OperationDockerPing: true, - OperationDockerInfo: true, - OperationDockerVersion: true, - OperationDockerEvents: true, - OperationDockerSystem: true, - OperationDockerAgentPing: true, - OperationDockerAgentList: true, - OperationDockerAgentHostInfo: true, - OperationPortainerStackList: true, - OperationPortainerStackInspect: true, - OperationPortainerStackFile: true, - OperationPortainerWebhookList: true, - } - - if volumeBrowsingAuthorizations { - authorizations[OperationDockerAgentBrowseGet] = true - authorizations[OperationDockerAgentBrowseList] = true - } - - return authorizations -} - -// DefaultPortainerAuthorizations returns the default Portainer authorizations used by non-admin users. -func DefaultPortainerAuthorizations() Authorizations { - return map[Authorization]bool{ - OperationPortainerDockerHubInspect: true, - OperationPortainerEndpointGroupList: true, - OperationPortainerEndpointList: true, - OperationPortainerEndpointInspect: true, - OperationPortainerEndpointExtensionAdd: true, - OperationPortainerEndpointExtensionRemove: true, - OperationPortainerExtensionList: true, - OperationPortainerMOTD: true, - OperationPortainerRegistryList: true, - OperationPortainerRegistryInspect: true, - OperationPortainerTeamList: true, - OperationPortainerTemplateList: true, - OperationPortainerTemplateInspect: true, - OperationPortainerUserList: true, - OperationPortainerUserInspect: true, - OperationPortainerUserMemberships: true, - } -} - -// UpdateVolumeBrowsingAuthorizations will update all the volume browsing authorizations for each role (except endpoint administrator) -// based on the specified removeAuthorizations parameter. If removeAuthorizations is set to true, all -// the authorizations will be dropped for the each role. If removeAuthorizations is set to false, the authorizations -// will be reset based for each role. -func (service AuthorizationService) UpdateVolumeBrowsingAuthorizations(remove bool) error { - roles, err := service.dataStore.Role().Roles() - if err != nil { - return err - } - - for _, role := range roles { - // all roles except endpoint administrator - if role.ID != RoleID(1) { - updateRoleVolumeBrowsingAuthorizations(&role, remove) - - err := service.dataStore.Role().UpdateRole(role.ID, &role) - if err != nil { - return err - } - } - } - - return nil -} - -func updateRoleVolumeBrowsingAuthorizations(role *Role, removeAuthorizations bool) { - if !removeAuthorizations { - delete(role.Authorizations, OperationDockerAgentBrowseDelete) - delete(role.Authorizations, OperationDockerAgentBrowseGet) - delete(role.Authorizations, OperationDockerAgentBrowseList) - delete(role.Authorizations, OperationDockerAgentBrowsePut) - delete(role.Authorizations, OperationDockerAgentBrowseRename) - return - } - - role.Authorizations[OperationDockerAgentBrowseGet] = true - role.Authorizations[OperationDockerAgentBrowseList] = true - - // Standard-user - if role.ID == RoleID(3) { - role.Authorizations[OperationDockerAgentBrowseDelete] = true - role.Authorizations[OperationDockerAgentBrowsePut] = true - role.Authorizations[OperationDockerAgentBrowseRename] = true - } -} - -// RemoveTeamAccessPolicies will remove all existing access policies associated to the specified team -func (service *AuthorizationService) RemoveTeamAccessPolicies(teamID TeamID) error { - endpoints, err := service.dataStore.Endpoint().Endpoints() - if err != nil { - return err - } - - for _, endpoint := range endpoints { - for policyTeamID := range endpoint.TeamAccessPolicies { - if policyTeamID == teamID { - delete(endpoint.TeamAccessPolicies, policyTeamID) - - err := service.dataStore.Endpoint().UpdateEndpoint(endpoint.ID, &endpoint) - if err != nil { - return err - } - - break - } - } - } - - endpointGroups, err := service.dataStore.EndpointGroup().EndpointGroups() - if err != nil { - return err - } - - for _, endpointGroup := range endpointGroups { - for policyTeamID := range endpointGroup.TeamAccessPolicies { - if policyTeamID == teamID { - delete(endpointGroup.TeamAccessPolicies, policyTeamID) - - err := service.dataStore.EndpointGroup().UpdateEndpointGroup(endpointGroup.ID, &endpointGroup) - if err != nil { - return err - } - - break - } - } - } - - registries, err := service.dataStore.Registry().Registries() - if err != nil { - return err - } - - for _, registry := range registries { - for policyTeamID := range registry.TeamAccessPolicies { - if policyTeamID == teamID { - delete(registry.TeamAccessPolicies, policyTeamID) - - err := service.dataStore.Registry().UpdateRegistry(registry.ID, ®istry) - if err != nil { - return err - } - - break - } - } - } - - return service.UpdateUsersAuthorizations() -} - -// RemoveUserAccessPolicies will remove all existing access policies associated to the specified user -func (service *AuthorizationService) RemoveUserAccessPolicies(userID UserID) error { - endpoints, err := service.dataStore.Endpoint().Endpoints() - if err != nil { - return err - } - - for _, endpoint := range endpoints { - for policyUserID := range endpoint.UserAccessPolicies { - if policyUserID == userID { - delete(endpoint.UserAccessPolicies, policyUserID) - - err := service.dataStore.Endpoint().UpdateEndpoint(endpoint.ID, &endpoint) - if err != nil { - return err - } - - break - } - } - } - - endpointGroups, err := service.dataStore.EndpointGroup().EndpointGroups() - if err != nil { - return err - } - - for _, endpointGroup := range endpointGroups { - for policyUserID := range endpointGroup.UserAccessPolicies { - if policyUserID == userID { - delete(endpointGroup.UserAccessPolicies, policyUserID) - - err := service.dataStore.EndpointGroup().UpdateEndpointGroup(endpointGroup.ID, &endpointGroup) - if err != nil { - return err - } - - break - } - } - } - - registries, err := service.dataStore.Registry().Registries() - if err != nil { - return err - } - - for _, registry := range registries { - for policyUserID := range registry.UserAccessPolicies { - if policyUserID == userID { - delete(registry.UserAccessPolicies, policyUserID) - - err := service.dataStore.Registry().UpdateRegistry(registry.ID, ®istry) - if err != nil { - return err - } - - break - } - } - } - - return nil -} - -// UpdateUsersAuthorizations will trigger an update of the authorizations for all the users. -func (service *AuthorizationService) UpdateUsersAuthorizations() error { - users, err := service.dataStore.User().Users() - if err != nil { - return err - } - - for _, user := range users { - err := service.updateUserAuthorizations(user.ID) - if err != nil { - return err - } - } - - return nil -} - -func (service *AuthorizationService) updateUserAuthorizations(userID UserID) error { - user, err := service.dataStore.User().User(userID) - if err != nil { - return err - } - - endpointAuthorizations, err := service.getAuthorizations(user) - if err != nil { - return err - } - - user.EndpointAuthorizations = endpointAuthorizations - - return service.dataStore.User().UpdateUser(userID, user) -} - -func (service *AuthorizationService) getAuthorizations(user *User) (EndpointAuthorizations, error) { - endpointAuthorizations := EndpointAuthorizations{} - if user.Role == AdministratorRole { - return endpointAuthorizations, nil - } - - userMemberships, err := service.dataStore.TeamMembership().TeamMembershipsByUserID(user.ID) - if err != nil { - return endpointAuthorizations, err - } - - endpoints, err := service.dataStore.Endpoint().Endpoints() - if err != nil { - return endpointAuthorizations, err - } - - endpointGroups, err := service.dataStore.EndpointGroup().EndpointGroups() - if err != nil { - return endpointAuthorizations, err - } - - roles, err := service.dataStore.Role().Roles() - if err != nil { - return endpointAuthorizations, err - } - - endpointAuthorizations = getUserEndpointAuthorizations(user, endpoints, endpointGroups, roles, userMemberships) - - return endpointAuthorizations, nil -} - -func getUserEndpointAuthorizations(user *User, endpoints []Endpoint, endpointGroups []EndpointGroup, roles []Role, userMemberships []TeamMembership) EndpointAuthorizations { - endpointAuthorizations := make(EndpointAuthorizations) - - groupUserAccessPolicies := map[EndpointGroupID]UserAccessPolicies{} - groupTeamAccessPolicies := map[EndpointGroupID]TeamAccessPolicies{} - for _, endpointGroup := range endpointGroups { - groupUserAccessPolicies[endpointGroup.ID] = endpointGroup.UserAccessPolicies - groupTeamAccessPolicies[endpointGroup.ID] = endpointGroup.TeamAccessPolicies - } - - for _, endpoint := range endpoints { - authorizations := getAuthorizationsFromUserEndpointPolicy(user, &endpoint, roles) - if len(authorizations) > 0 { - endpointAuthorizations[endpoint.ID] = authorizations - continue - } - - authorizations = getAuthorizationsFromUserEndpointGroupPolicy(user, &endpoint, roles, groupUserAccessPolicies) - if len(authorizations) > 0 { - endpointAuthorizations[endpoint.ID] = authorizations - continue - } - - authorizations = getAuthorizationsFromTeamEndpointPolicies(userMemberships, &endpoint, roles) - if len(authorizations) > 0 { - endpointAuthorizations[endpoint.ID] = authorizations - continue - } - - authorizations = getAuthorizationsFromTeamEndpointGroupPolicies(userMemberships, &endpoint, roles, groupTeamAccessPolicies) - if len(authorizations) > 0 { - endpointAuthorizations[endpoint.ID] = authorizations - } - } - - return endpointAuthorizations -} - -func getAuthorizationsFromUserEndpointPolicy(user *User, endpoint *Endpoint, roles []Role) Authorizations { - policyRoles := make([]RoleID, 0) - - policy, ok := endpoint.UserAccessPolicies[user.ID] - if ok { - policyRoles = append(policyRoles, policy.RoleID) - } - - return getAuthorizationsFromRoles(policyRoles, roles) -} - -func getAuthorizationsFromUserEndpointGroupPolicy(user *User, endpoint *Endpoint, roles []Role, groupAccessPolicies map[EndpointGroupID]UserAccessPolicies) Authorizations { - policyRoles := make([]RoleID, 0) - - policy, ok := groupAccessPolicies[endpoint.GroupID][user.ID] - if ok { - policyRoles = append(policyRoles, policy.RoleID) - } - - return getAuthorizationsFromRoles(policyRoles, roles) -} - -func getAuthorizationsFromTeamEndpointPolicies(memberships []TeamMembership, endpoint *Endpoint, roles []Role) Authorizations { - policyRoles := make([]RoleID, 0) - - for _, membership := range memberships { - policy, ok := endpoint.TeamAccessPolicies[membership.TeamID] - if ok { - policyRoles = append(policyRoles, policy.RoleID) - } - } - - return getAuthorizationsFromRoles(policyRoles, roles) -} - -func getAuthorizationsFromTeamEndpointGroupPolicies(memberships []TeamMembership, endpoint *Endpoint, roles []Role, groupAccessPolicies map[EndpointGroupID]TeamAccessPolicies) Authorizations { - policyRoles := make([]RoleID, 0) - - for _, membership := range memberships { - policy, ok := groupAccessPolicies[endpoint.GroupID][membership.TeamID] - if ok { - policyRoles = append(policyRoles, policy.RoleID) - } - } - - return getAuthorizationsFromRoles(policyRoles, roles) -} - -func getAuthorizationsFromRoles(roleIdentifiers []RoleID, roles []Role) Authorizations { - var associatedRoles []Role - - for _, id := range roleIdentifiers { - for _, role := range roles { - if role.ID == id { - associatedRoles = append(associatedRoles, role) - break - } - } - } - - var authorizations Authorizations - highestPriority := 0 - for _, role := range associatedRoles { - if role.Priority > highestPriority { - highestPriority = role.Priority - authorizations = role.Authorizations - } - } - - return authorizations -} diff --git a/api/bolt/datastore.go b/api/bolt/datastore.go index 476082604..05ec10d10 100644 --- a/api/bolt/datastore.go +++ b/api/bolt/datastore.go @@ -28,6 +28,7 @@ import ( "github.com/portainer/portainer/api/bolt/user" "github.com/portainer/portainer/api/bolt/version" "github.com/portainer/portainer/api/bolt/webhook" + "github.com/portainer/portainer/api/internal/authorization" ) const ( @@ -143,7 +144,7 @@ func (store *Store) MigrateData() error { UserService: store.UserService, VersionService: store.VersionService, FileService: store.fileService, - AuthorizationService: portainer.NewAuthorizationService(store), + AuthorizationService: authorization.NewService(store), } migrator := migrator.NewMigrator(migratorParams) diff --git a/api/bolt/init.go b/api/bolt/init.go index 53fb28044..df6277ed8 100644 --- a/api/bolt/init.go +++ b/api/bolt/init.go @@ -1,6 +1,9 @@ package bolt -import portainer "github.com/portainer/portainer/api" +import ( + portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/internal/authorization" +) // Init creates the default data set. func (store *Store) Init() error { @@ -85,7 +88,7 @@ func (store *Store) Init() error { Name: "Endpoint administrator", Description: "Full control of all resources in an endpoint", Priority: 1, - Authorizations: portainer.DefaultEndpointAuthorizationsForEndpointAdministratorRole(), + Authorizations: authorization.DefaultEndpointAuthorizationsForEndpointAdministratorRole(), } err = store.RoleService.CreateRole(environmentAdministratorRole) @@ -97,7 +100,7 @@ func (store *Store) Init() error { Name: "Helpdesk", Description: "Read-only access of all resources in an endpoint", Priority: 2, - Authorizations: portainer.DefaultEndpointAuthorizationsForHelpDeskRole(false), + Authorizations: authorization.DefaultEndpointAuthorizationsForHelpDeskRole(false), } err = store.RoleService.CreateRole(environmentReadOnlyUserRole) @@ -109,7 +112,7 @@ func (store *Store) Init() error { Name: "Standard user", Description: "Full control of assigned resources in an endpoint", Priority: 3, - Authorizations: portainer.DefaultEndpointAuthorizationsForStandardUserRole(false), + Authorizations: authorization.DefaultEndpointAuthorizationsForStandardUserRole(false), } err = store.RoleService.CreateRole(standardUserRole) @@ -121,7 +124,7 @@ func (store *Store) Init() error { Name: "Read-only user", Description: "Read-only access of assigned resources in an endpoint", Priority: 4, - Authorizations: portainer.DefaultEndpointAuthorizationsForReadOnlyUserRole(false), + Authorizations: authorization.DefaultEndpointAuthorizationsForReadOnlyUserRole(false), } err = store.RoleService.CreateRole(readOnlyUserRole) diff --git a/api/bolt/migrator/migrate_dbversion20.go b/api/bolt/migrator/migrate_dbversion20.go index df272905a..23ddd82ab 100644 --- a/api/bolt/migrator/migrate_dbversion20.go +++ b/api/bolt/migrator/migrate_dbversion20.go @@ -1,6 +1,9 @@ package migrator -import portainer "github.com/portainer/portainer/api" +import ( + portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/internal/authorization" +) func (m *Migrator) updateResourceControlsToDBVersion22() error { legacyResourceControls, err := m.resourceControlService.ResourceControls() @@ -32,7 +35,7 @@ func (m *Migrator) updateUsersAndRolesToDBVersion22() error { } for _, user := range legacyUsers { - user.PortainerAuthorizations = portainer.DefaultPortainerAuthorizations() + user.PortainerAuthorizations = authorization.DefaultPortainerAuthorizations() err = m.userService.UpdateUser(user.ID, &user) if err != nil { return err @@ -44,7 +47,7 @@ func (m *Migrator) updateUsersAndRolesToDBVersion22() error { return err } endpointAdministratorRole.Priority = 1 - endpointAdministratorRole.Authorizations = portainer.DefaultEndpointAuthorizationsForEndpointAdministratorRole() + endpointAdministratorRole.Authorizations = authorization.DefaultEndpointAuthorizationsForEndpointAdministratorRole() err = m.roleService.UpdateRole(endpointAdministratorRole.ID, endpointAdministratorRole) @@ -53,7 +56,7 @@ func (m *Migrator) updateUsersAndRolesToDBVersion22() error { return err } helpDeskRole.Priority = 2 - helpDeskRole.Authorizations = portainer.DefaultEndpointAuthorizationsForHelpDeskRole(settings.AllowVolumeBrowserForRegularUsers) + helpDeskRole.Authorizations = authorization.DefaultEndpointAuthorizationsForHelpDeskRole(settings.AllowVolumeBrowserForRegularUsers) err = m.roleService.UpdateRole(helpDeskRole.ID, helpDeskRole) @@ -62,7 +65,7 @@ func (m *Migrator) updateUsersAndRolesToDBVersion22() error { return err } standardUserRole.Priority = 3 - standardUserRole.Authorizations = portainer.DefaultEndpointAuthorizationsForStandardUserRole(settings.AllowVolumeBrowserForRegularUsers) + standardUserRole.Authorizations = authorization.DefaultEndpointAuthorizationsForStandardUserRole(settings.AllowVolumeBrowserForRegularUsers) err = m.roleService.UpdateRole(standardUserRole.ID, standardUserRole) @@ -71,7 +74,7 @@ func (m *Migrator) updateUsersAndRolesToDBVersion22() error { return err } readOnlyUserRole.Priority = 4 - readOnlyUserRole.Authorizations = portainer.DefaultEndpointAuthorizationsForReadOnlyUserRole(settings.AllowVolumeBrowserForRegularUsers) + readOnlyUserRole.Authorizations = authorization.DefaultEndpointAuthorizationsForReadOnlyUserRole(settings.AllowVolumeBrowserForRegularUsers) err = m.roleService.UpdateRole(readOnlyUserRole.ID, readOnlyUserRole) if err != nil { diff --git a/api/bolt/migrator/migrator.go b/api/bolt/migrator/migrator.go index ed84b52e5..a933b6519 100644 --- a/api/bolt/migrator/migrator.go +++ b/api/bolt/migrator/migrator.go @@ -17,6 +17,7 @@ import ( "github.com/portainer/portainer/api/bolt/teammembership" "github.com/portainer/portainer/api/bolt/user" "github.com/portainer/portainer/api/bolt/version" + "github.com/portainer/portainer/api/internal/authorization" ) type ( @@ -39,7 +40,7 @@ type ( userService *user.Service versionService *version.Service fileService portainer.FileService - authorizationService *portainer.AuthorizationService + authorizationService *authorization.Service } // Parameters represents the required parameters to create a new Migrator instance. @@ -61,7 +62,7 @@ type ( UserService *user.Service VersionService *version.Service FileService portainer.FileService - AuthorizationService *portainer.AuthorizationService + AuthorizationService *authorization.Service } ) diff --git a/api/cmd/portainer/main.go b/api/cmd/portainer/main.go index 55f65bb6b..3ec42000c 100644 --- a/api/cmd/portainer/main.go +++ b/api/cmd/portainer/main.go @@ -7,6 +7,7 @@ import ( "time" "github.com/portainer/portainer/api/chisel" + "github.com/portainer/portainer/api/internal/authorization" "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/bolt" @@ -486,7 +487,7 @@ func main() { Username: "admin", Role: portainer.AdministratorRole, Password: adminPasswordHash, - PortainerAuthorizations: portainer.DefaultPortainerAuthorizations(), + PortainerAuthorizations: authorization.DefaultPortainerAuthorizations(), } err := dataStore.User().CreateUser(user) if err != nil { diff --git a/api/edgegroup.go b/api/edgegroup.go deleted file mode 100644 index d68ee7a9b..000000000 --- a/api/edgegroup.go +++ /dev/null @@ -1,54 +0,0 @@ -package portainer - -// EdgeGroupRelatedEndpoints returns a list of endpoints related to this Edge group -func EdgeGroupRelatedEndpoints(edgeGroup *EdgeGroup, endpoints []Endpoint, endpointGroups []EndpointGroup) []EndpointID { - if !edgeGroup.Dynamic { - return edgeGroup.Endpoints - } - - endpointIDs := []EndpointID{} - for _, endpoint := range endpoints { - if endpoint.Type != EdgeAgentEnvironment { - continue - } - - var endpointGroup EndpointGroup - for _, group := range endpointGroups { - if endpoint.GroupID == group.ID { - endpointGroup = group - break - } - } - - if edgeGroupRelatedToEndpoint(edgeGroup, &endpoint, &endpointGroup) { - endpointIDs = append(endpointIDs, endpoint.ID) - } - } - - return endpointIDs -} - -// edgeGroupRelatedToEndpoint returns true is edgeGroup is associated with endpoint -func edgeGroupRelatedToEndpoint(edgeGroup *EdgeGroup, endpoint *Endpoint, endpointGroup *EndpointGroup) bool { - if !edgeGroup.Dynamic { - for _, endpointID := range edgeGroup.Endpoints { - if endpoint.ID == endpointID { - return true - } - } - return false - } - - endpointTags := TagSet(endpoint.TagIDs) - if endpointGroup.TagIDs != nil { - endpointTags = TagUnion(endpointTags, TagSet(endpointGroup.TagIDs)) - } - edgeGroupTags := TagSet(edgeGroup.TagIDs) - - if edgeGroup.PartialMatch { - intersection := TagIntersection(endpointTags, edgeGroupTags) - return len(intersection) != 0 - } - - return TagContains(edgeGroupTags, endpointTags) -} diff --git a/api/http/handler/auth/authenticate.go b/api/http/handler/auth/authenticate.go index 6a1d47308..eb9592566 100644 --- a/api/http/handler/auth/authenticate.go +++ b/api/http/handler/auth/authenticate.go @@ -10,6 +10,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/internal/authorization" ) type authenticatePayload struct { @@ -101,7 +102,7 @@ func (handler *Handler) authenticateLDAPAndCreateUser(w http.ResponseWriter, use user := &portainer.User{ Username: username, Role: portainer.StandardUserRole, - PortainerAuthorizations: portainer.DefaultPortainerAuthorizations(), + PortainerAuthorizations: authorization.DefaultPortainerAuthorizations(), } err = handler.DataStore.User().CreateUser(user) diff --git a/api/http/handler/auth/authenticate_oauth.go b/api/http/handler/auth/authenticate_oauth.go index 757f3fd76..68f998495 100644 --- a/api/http/handler/auth/authenticate_oauth.go +++ b/api/http/handler/auth/authenticate_oauth.go @@ -2,6 +2,7 @@ package auth import ( "encoding/json" + "github.com/portainer/portainer/api/internal/authorization" "io/ioutil" "log" "net/http" @@ -113,7 +114,7 @@ func (handler *Handler) validateOAuth(w http.ResponseWriter, r *http.Request) *h user = &portainer.User{ Username: username, Role: portainer.StandardUserRole, - PortainerAuthorizations: portainer.DefaultPortainerAuthorizations(), + PortainerAuthorizations: authorization.DefaultPortainerAuthorizations(), } err = handler.DataStore.User().CreateUser(user) diff --git a/api/http/handler/auth/handler.go b/api/http/handler/auth/handler.go index bfb33c6f9..1f8415597 100644 --- a/api/http/handler/auth/handler.go +++ b/api/http/handler/auth/handler.go @@ -8,6 +8,7 @@ import ( "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/proxy" "github.com/portainer/portainer/api/http/security" + "github.com/portainer/portainer/api/internal/authorization" ) // Handler is the HTTP handler used to handle authentication operations. @@ -18,7 +19,7 @@ type Handler struct { JWTService portainer.JWTService LDAPService portainer.LDAPService ProxyManager *proxy.Manager - AuthorizationService *portainer.AuthorizationService + AuthorizationService *authorization.Service } // NewHandler creates a handler to manage authentication operations. diff --git a/api/http/handler/edgegroups/edgegroup_update.go b/api/http/handler/edgegroups/edgegroup_update.go index 6c227b11b..c2c4e36d2 100644 --- a/api/http/handler/edgegroups/edgegroup_update.go +++ b/api/http/handler/edgegroups/edgegroup_update.go @@ -7,7 +7,8 @@ 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/internal/edge" ) type edgeGroupUpdatePayload struct { @@ -73,7 +74,7 @@ func (handler *Handler) edgeGroupUpdate(w http.ResponseWriter, r *http.Request) return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoint groups from database", err} } - oldRelatedEndpoints := portainer.EdgeGroupRelatedEndpoints(edgeGroup, endpoints, endpointGroups) + oldRelatedEndpoints := edge.EdgeGroupRelatedEndpoints(edgeGroup, endpoints, endpointGroups) edgeGroup.Dynamic = payload.Dynamic if edgeGroup.Dynamic { @@ -102,7 +103,7 @@ func (handler *Handler) edgeGroupUpdate(w http.ResponseWriter, r *http.Request) return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist Edge group changes inside the database", err} } - newRelatedEndpoints := portainer.EdgeGroupRelatedEndpoints(edgeGroup, endpoints, endpointGroups) + newRelatedEndpoints := edge.EdgeGroupRelatedEndpoints(edgeGroup, endpoints, endpointGroups) endpointsToUpdate := append(newRelatedEndpoints, oldRelatedEndpoints...) for _, endpointID := range endpointsToUpdate { @@ -143,7 +144,7 @@ func (handler *Handler) updateEndpoint(endpointID portainer.EndpointID) error { edgeStackSet := map[portainer.EdgeStackID]bool{} - endpointEdgeStacks := portainer.EndpointRelatedEdgeStacks(endpoint, endpointGroup, edgeGroups, edgeStacks) + endpointEdgeStacks := edge.EndpointRelatedEdgeStacks(endpoint, endpointGroup, edgeGroups, edgeStacks) for _, edgeStackID := range endpointEdgeStacks { edgeStackSet[edgeStackID] = true } diff --git a/api/http/handler/edgestacks/edgestack_create.go b/api/http/handler/edgestacks/edgestack_create.go index 27f7ce87b..e6e96f02d 100644 --- a/api/http/handler/edgestacks/edgestack_create.go +++ b/api/http/handler/edgestacks/edgestack_create.go @@ -13,6 +13,7 @@ import ( "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/filesystem" + "github.com/portainer/portainer/api/internal/edge" ) // POST request on /api/endpoint_groups @@ -42,7 +43,7 @@ func (handler *Handler) edgeStackCreate(w http.ResponseWriter, r *http.Request) return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge groups from database", err} } - relatedEndpoints, err := portainer.EdgeStackRelatedEndpoints(edgeStack.EdgeGroups, endpoints, endpointGroups, edgeGroups) + relatedEndpoints, err := edge.EdgeStackRelatedEndpoints(edgeStack.EdgeGroups, endpoints, endpointGroups, edgeGroups) for _, endpointID := range relatedEndpoints { relation, err := handler.DataStore.EndpointRelation().EndpointRelation(endpointID) diff --git a/api/http/handler/edgestacks/edgestack_delete.go b/api/http/handler/edgestacks/edgestack_delete.go index cec2a3c82..641d2d66e 100644 --- a/api/http/handler/edgestacks/edgestack_delete.go +++ b/api/http/handler/edgestacks/edgestack_delete.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/internal/edge" ) func (handler *Handler) edgeStackDelete(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { @@ -42,7 +43,7 @@ func (handler *Handler) edgeStackDelete(w http.ResponseWriter, r *http.Request) return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge groups from database", err} } - relatedEndpoints, err := portainer.EdgeStackRelatedEndpoints(edgeStack.EdgeGroups, endpoints, endpointGroups, edgeGroups) + relatedEndpoints, err := edge.EdgeStackRelatedEndpoints(edgeStack.EdgeGroups, endpoints, endpointGroups, edgeGroups) for _, endpointID := range relatedEndpoints { relation, err := handler.DataStore.EndpointRelation().EndpointRelation(endpointID) diff --git a/api/http/handler/edgestacks/edgestack_update.go b/api/http/handler/edgestacks/edgestack_update.go index 3d7ac1ddc..39edc92e1 100644 --- a/api/http/handler/edgestacks/edgestack_update.go +++ b/api/http/handler/edgestacks/edgestack_update.go @@ -9,6 +9,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/internal/edge" ) type updateEdgeStackPayload struct { @@ -63,12 +64,12 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request) return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge groups from database", err} } - oldRelated, err := portainer.EdgeStackRelatedEndpoints(stack.EdgeGroups, endpoints, endpointGroups, edgeGroups) + oldRelated, err := edge.EdgeStackRelatedEndpoints(stack.EdgeGroups, endpoints, endpointGroups, edgeGroups) if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge stack related endpoints from database", err} } - newRelated, err := portainer.EdgeStackRelatedEndpoints(payload.EdgeGroups, endpoints, endpointGroups, edgeGroups) + newRelated, err := edge.EdgeStackRelatedEndpoints(payload.EdgeGroups, endpoints, endpointGroups, edgeGroups) if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge stack related endpoints from database", err} } diff --git a/api/http/handler/endpointgroups/endpointgroup_update.go b/api/http/handler/endpointgroups/endpointgroup_update.go index e7d00d41c..c03215518 100644 --- a/api/http/handler/endpointgroups/endpointgroup_update.go +++ b/api/http/handler/endpointgroups/endpointgroup_update.go @@ -8,6 +8,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/internal/tag" ) type endpointGroupUpdatePayload struct { @@ -52,14 +53,14 @@ func (handler *Handler) endpointGroupUpdate(w http.ResponseWriter, r *http.Reque tagsChanged := false if payload.TagIDs != nil { - payloadTagSet := portainer.TagSet(payload.TagIDs) - endpointGroupTagSet := portainer.TagSet((endpointGroup.TagIDs)) - union := portainer.TagUnion(payloadTagSet, endpointGroupTagSet) - intersection := portainer.TagIntersection(payloadTagSet, endpointGroupTagSet) + payloadTagSet := tag.Set(payload.TagIDs) + endpointGroupTagSet := tag.Set((endpointGroup.TagIDs)) + union := tag.Union(payloadTagSet, endpointGroupTagSet) + intersection := tag.Intersection(payloadTagSet, endpointGroupTagSet) tagsChanged = len(union) > len(intersection) if tagsChanged { - removeTags := portainer.TagDifference(endpointGroupTagSet, payloadTagSet) + removeTags := tag.Difference(endpointGroupTagSet, payloadTagSet) for tagID := range removeTags { tag, err := handler.DataStore.Tag().Tag(tagID) diff --git a/api/http/handler/endpointgroups/endpoints.go b/api/http/handler/endpointgroups/endpoints.go index 3ff3096cb..e854ca635 100644 --- a/api/http/handler/endpointgroups/endpoints.go +++ b/api/http/handler/endpointgroups/endpoints.go @@ -1,6 +1,9 @@ package endpointgroups -import portainer "github.com/portainer/portainer/api" +import ( + portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/internal/edge" +) func (handler *Handler) updateEndpointRelations(endpoint *portainer.Endpoint, endpointGroup *portainer.EndpointGroup) error { if endpoint.Type != portainer.EdgeAgentEnvironment { @@ -31,7 +34,7 @@ func (handler *Handler) updateEndpointRelations(endpoint *portainer.Endpoint, en return err } - endpointStacks := portainer.EndpointRelatedEdgeStacks(endpoint, endpointGroup, edgeGroups, edgeStacks) + endpointStacks := edge.EndpointRelatedEdgeStacks(endpoint, endpointGroup, edgeGroups, edgeStacks) stacksSet := map[portainer.EdgeStackID]bool{} for _, edgeStackID := range endpointStacks { stacksSet[edgeStackID] = true diff --git a/api/http/handler/endpointgroups/handler.go b/api/http/handler/endpointgroups/handler.go index 9730e875d..8a6586cb9 100644 --- a/api/http/handler/endpointgroups/handler.go +++ b/api/http/handler/endpointgroups/handler.go @@ -7,13 +7,14 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/security" + "github.com/portainer/portainer/api/internal/authorization" ) // Handler is the HTTP handler used to handle endpoint group operations. type Handler struct { *mux.Router DataStore portainer.DataStore - AuthorizationService *portainer.AuthorizationService + AuthorizationService *authorization.Service } // NewHandler creates a handler to manage endpoint group operations. diff --git a/api/http/handler/endpoints/endpoint_create.go b/api/http/handler/endpoints/endpoint_create.go index 677ce6222..a2b90c1cd 100644 --- a/api/http/handler/endpoints/endpoint_create.go +++ b/api/http/handler/endpoints/endpoint_create.go @@ -15,6 +15,7 @@ import ( "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/crypto" "github.com/portainer/portainer/api/http/client" + "github.com/portainer/portainer/api/internal/edge" ) type endpointCreatePayload struct { @@ -167,7 +168,7 @@ func (handler *Handler) endpointCreate(w http.ResponseWriter, r *http.Request) * } if endpoint.Type == portainer.EdgeAgentEnvironment { - relatedEdgeStacks := portainer.EndpointRelatedEdgeStacks(endpoint, endpointGroup, edgeGroups, edgeStacks) + relatedEdgeStacks := edge.EndpointRelatedEdgeStacks(endpoint, endpointGroup, edgeGroups, edgeStacks) for _, stackID := range relatedEdgeStacks { relationObject.EdgeStacks[stackID] = true } diff --git a/api/http/handler/endpoints/endpoint_update.go b/api/http/handler/endpoints/endpoint_update.go index 766c09207..bf73d9320 100644 --- a/api/http/handler/endpoints/endpoint_update.go +++ b/api/http/handler/endpoints/endpoint_update.go @@ -10,6 +10,8 @@ import ( "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/client" + "github.com/portainer/portainer/api/internal/edge" + "github.com/portainer/portainer/api/internal/tag" ) type endpointUpdatePayload struct { @@ -79,14 +81,14 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) * tagsChanged := false if payload.TagIDs != nil { - payloadTagSet := portainer.TagSet(payload.TagIDs) - endpointTagSet := portainer.TagSet((endpoint.TagIDs)) - union := portainer.TagUnion(payloadTagSet, endpointTagSet) - intersection := portainer.TagIntersection(payloadTagSet, endpointTagSet) + payloadTagSet := tag.Set(payload.TagIDs) + endpointTagSet := tag.Set((endpoint.TagIDs)) + union := tag.Union(payloadTagSet, endpointTagSet) + intersection := tag.Intersection(payloadTagSet, endpointTagSet) tagsChanged = len(union) > len(intersection) if tagsChanged { - removeTags := portainer.TagDifference(endpointTagSet, payloadTagSet) + removeTags := tag.Difference(endpointTagSet, payloadTagSet) for tagID := range removeTags { tag, err := handler.DataStore.Tag().Tag(tagID) @@ -248,7 +250,7 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) * edgeStackSet := map[portainer.EdgeStackID]bool{} - endpointEdgeStacks := portainer.EndpointRelatedEdgeStacks(endpoint, endpointGroup, edgeGroups, edgeStacks) + endpointEdgeStacks := edge.EndpointRelatedEdgeStacks(endpoint, endpointGroup, edgeGroups, edgeStacks) for _, edgeStackID := range endpointEdgeStacks { edgeStackSet[edgeStackID] = true } diff --git a/api/http/handler/endpoints/handler.go b/api/http/handler/endpoints/handler.go index 0fb6e5b06..1d9ca9f03 100644 --- a/api/http/handler/endpoints/handler.go +++ b/api/http/handler/endpoints/handler.go @@ -5,6 +5,7 @@ import ( "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/proxy" "github.com/portainer/portainer/api/http/security" + "github.com/portainer/portainer/api/internal/authorization" "net/http" @@ -23,7 +24,7 @@ type Handler struct { *mux.Router requestBouncer *security.RequestBouncer DataStore portainer.DataStore - AuthorizationService *portainer.AuthorizationService + AuthorizationService *authorization.Service FileService portainer.FileService JobService portainer.JobService ProxyManager *proxy.Manager diff --git a/api/http/handler/extensions/handler.go b/api/http/handler/extensions/handler.go index cba6b354c..b33c23881 100644 --- a/api/http/handler/extensions/handler.go +++ b/api/http/handler/extensions/handler.go @@ -9,6 +9,7 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/security" + "github.com/portainer/portainer/api/internal/authorization" ) // Handler is the HTTP handler used to handle extension operations. @@ -16,7 +17,7 @@ type Handler struct { *mux.Router DataStore portainer.DataStore ExtensionManager portainer.ExtensionManager - AuthorizationService *portainer.AuthorizationService + AuthorizationService *authorization.Service } // NewHandler creates a handler to manage extension operations. diff --git a/api/http/handler/settings/handler.go b/api/http/handler/settings/handler.go index 9bbe1cf52..8534b1867 100644 --- a/api/http/handler/settings/handler.go +++ b/api/http/handler/settings/handler.go @@ -1,6 +1,7 @@ package settings import ( + "github.com/portainer/portainer/api/internal/authorization" "net/http" "github.com/gorilla/mux" @@ -17,7 +18,7 @@ func hideFields(settings *portainer.Settings) { // Handler is the HTTP handler used to handle settings operations. type Handler struct { *mux.Router - AuthorizationService *portainer.AuthorizationService + AuthorizationService *authorization.Service DataStore portainer.DataStore FileService portainer.FileService JobScheduler portainer.JobScheduler diff --git a/api/http/handler/stacks/handler.go b/api/http/handler/stacks/handler.go index 6722feaa8..f0a6fc0bf 100644 --- a/api/http/handler/stacks/handler.go +++ b/api/http/handler/stacks/handler.go @@ -8,6 +8,7 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/security" + "github.com/portainer/portainer/api/internal/authorization" ) // Handler is the HTTP handler used to handle stack operations. @@ -58,7 +59,7 @@ func (handler *Handler) userCanAccessStack(securityContext *security.RestrictedR userTeamIDs = append(userTeamIDs, membership.TeamID) } - if resourceControl != nil && portainer.UserCanAccessResource(securityContext.UserID, userTeamIDs, resourceControl) { + if resourceControl != nil && authorization.UserCanAccessResource(securityContext.UserID, userTeamIDs, resourceControl) { return true, nil } diff --git a/api/http/handler/stacks/stack_create.go b/api/http/handler/stacks/stack_create.go index 7f3244520..c57464c2c 100644 --- a/api/http/handler/stacks/stack_create.go +++ b/api/http/handler/stacks/stack_create.go @@ -12,6 +12,7 @@ import ( "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/security" + "github.com/portainer/portainer/api/internal/authorization" ) func (handler *Handler) cleanUp(stack *portainer.Stack, doCleanUp *bool) error { @@ -133,7 +134,7 @@ func (handler *Handler) isValidStackFile(stackFileContent []byte) (bool, error) } func (handler *Handler) decorateStackResponse(w http.ResponseWriter, stack *portainer.Stack, userID portainer.UserID) *httperror.HandlerError { - resourceControl := portainer.NewPrivateResourceControl(stack.Name, portainer.StackResourceControl, userID) + resourceControl := authorization.NewPrivateResourceControl(stack.Name, portainer.StackResourceControl, userID) err := handler.DataStore.ResourceControl().CreateResourceControl(resourceControl) if err != nil { diff --git a/api/http/handler/stacks/stack_list.go b/api/http/handler/stacks/stack_list.go index eab7d7ca1..047740397 100644 --- a/api/http/handler/stacks/stack_list.go +++ b/api/http/handler/stacks/stack_list.go @@ -8,6 +8,7 @@ import ( "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/security" + "github.com/portainer/portainer/api/internal/authorization" ) type stackListOperationFilters struct { @@ -39,7 +40,7 @@ func (handler *Handler) stackList(w http.ResponseWriter, r *http.Request) *httpe return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve info from request context", err} } - stacks = portainer.DecorateStacks(stacks, resourceControls) + stacks = authorization.DecorateStacks(stacks, resourceControls) if !securityContext.IsAdmin { rbacExtensionEnabled := true @@ -60,7 +61,7 @@ func (handler *Handler) stackList(w http.ResponseWriter, r *http.Request) *httpe userTeamIDs = append(userTeamIDs, membership.TeamID) } - stacks = portainer.FilterAuthorizedStacks(stacks, user, userTeamIDs, rbacExtensionEnabled) + stacks = authorization.FilterAuthorizedStacks(stacks, user, userTeamIDs, rbacExtensionEnabled) } return response.JSON(w, stacks) diff --git a/api/http/handler/tags/tag_delete.go b/api/http/handler/tags/tag_delete.go index 52dac6a9c..ce6a3840b 100644 --- a/api/http/handler/tags/tag_delete.go +++ b/api/http/handler/tags/tag_delete.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/internal/edge" ) // DELETE request on /api/tags/:id @@ -111,7 +112,7 @@ func (handler *Handler) updateEndpointRelations(endpoint portainer.Endpoint, edg return err } - endpointStacks := portainer.EndpointRelatedEdgeStacks(&endpoint, endpointGroup, edgeGroups, edgeStacks) + endpointStacks := edge.EndpointRelatedEdgeStacks(&endpoint, endpointGroup, edgeGroups, edgeStacks) stacksSet := map[portainer.EdgeStackID]bool{} for _, edgeStackID := range endpointStacks { stacksSet[edgeStackID] = true diff --git a/api/http/handler/teammemberships/handler.go b/api/http/handler/teammemberships/handler.go index e9faca62d..8628234e8 100644 --- a/api/http/handler/teammemberships/handler.go +++ b/api/http/handler/teammemberships/handler.go @@ -4,6 +4,7 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/security" + "github.com/portainer/portainer/api/internal/authorization" "net/http" @@ -14,7 +15,7 @@ import ( type Handler struct { *mux.Router DataStore portainer.DataStore - AuthorizationService *portainer.AuthorizationService + AuthorizationService *authorization.Service } // NewHandler creates a handler to manage team membership operations. diff --git a/api/http/handler/teams/handler.go b/api/http/handler/teams/handler.go index ce87e0513..d0a49e537 100644 --- a/api/http/handler/teams/handler.go +++ b/api/http/handler/teams/handler.go @@ -1,6 +1,7 @@ package teams import ( + "github.com/portainer/portainer/api/internal/authorization" "net/http" "github.com/gorilla/mux" @@ -13,7 +14,7 @@ import ( type Handler struct { *mux.Router DataStore portainer.DataStore - AuthorizationService *portainer.AuthorizationService + AuthorizationService *authorization.Service } // NewHandler creates a handler to manage team operations. diff --git a/api/http/handler/users/admin_init.go b/api/http/handler/users/admin_init.go index 67010b660..b76299199 100644 --- a/api/http/handler/users/admin_init.go +++ b/api/http/handler/users/admin_init.go @@ -8,6 +8,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/internal/authorization" ) type adminInitPayload struct { @@ -45,7 +46,7 @@ func (handler *Handler) adminInit(w http.ResponseWriter, r *http.Request) *httpe user := &portainer.User{ Username: payload.Username, Role: portainer.AdministratorRole, - PortainerAuthorizations: portainer.DefaultPortainerAuthorizations(), + PortainerAuthorizations: authorization.DefaultPortainerAuthorizations(), } user.Password, err = handler.CryptoService.Hash(payload.Password) diff --git a/api/http/handler/users/handler.go b/api/http/handler/users/handler.go index cbdc9eaf7..3413d4bb9 100644 --- a/api/http/handler/users/handler.go +++ b/api/http/handler/users/handler.go @@ -4,6 +4,7 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/security" + "github.com/portainer/portainer/api/internal/authorization" "net/http" @@ -19,7 +20,7 @@ type Handler struct { *mux.Router DataStore portainer.DataStore CryptoService portainer.CryptoService - AuthorizationService *portainer.AuthorizationService + AuthorizationService *authorization.Service } // NewHandler creates a handler to manage user operations. diff --git a/api/http/handler/users/user_create.go b/api/http/handler/users/user_create.go index 43bcab0e7..d39d1a9a7 100644 --- a/api/http/handler/users/user_create.go +++ b/api/http/handler/users/user_create.go @@ -9,6 +9,7 @@ import ( "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/security" + "github.com/portainer/portainer/api/internal/authorization" ) type userCreatePayload struct { @@ -60,7 +61,7 @@ func (handler *Handler) userCreate(w http.ResponseWriter, r *http.Request) *http user = &portainer.User{ Username: payload.Username, Role: portainer.UserRole(payload.Role), - PortainerAuthorizations: portainer.DefaultPortainerAuthorizations(), + PortainerAuthorizations: authorization.DefaultPortainerAuthorizations(), } settings, err := handler.DataStore.Settings().Settings() diff --git a/api/http/proxy/factory/docker/access_control.go b/api/http/proxy/factory/docker/access_control.go index d3429f3b6..00b2c9693 100644 --- a/api/http/proxy/factory/docker/access_control.go +++ b/api/http/proxy/factory/docker/access_control.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/portainer/portainer/api/http/proxy/factory/responseutils" + "github.com/portainer/portainer/api/internal/authorization" "github.com/portainer/portainer/api" ) @@ -30,7 +31,7 @@ type ( func (transport *Transport) newResourceControlFromPortainerLabels(labelsObject map[string]interface{}, resourceID string, resourceType portainer.ResourceControlType) (*portainer.ResourceControl, error) { if labelsObject[resourceLabelForPortainerPublicResourceControl] != nil { - resourceControl := portainer.NewPublicResourceControl(resourceID, resourceType) + resourceControl := authorization.NewPublicResourceControl(resourceID, resourceType) err := transport.dataStore.ResourceControl().CreateResourceControl(resourceControl) if err != nil { @@ -76,7 +77,7 @@ func (transport *Transport) newResourceControlFromPortainerLabels(labelsObject m userIDs = append(userIDs, user.ID) } - resourceControl := portainer.NewRestrictedResourceControl(resourceID, resourceType, userIDs, teamIDs) + resourceControl := authorization.NewRestrictedResourceControl(resourceID, resourceType, userIDs, teamIDs) err := transport.dataStore.ResourceControl().CreateResourceControl(resourceControl) if err != nil { @@ -90,7 +91,7 @@ func (transport *Transport) newResourceControlFromPortainerLabels(labelsObject m } func (transport *Transport) createPrivateResourceControl(resourceIdentifier string, resourceType portainer.ResourceControlType, userID portainer.UserID) (*portainer.ResourceControl, error) { - resourceControl := portainer.NewPrivateResourceControl(resourceIdentifier, resourceType, userID) + resourceControl := authorization.NewPrivateResourceControl(resourceIdentifier, resourceType, userID) err := transport.dataStore.ResourceControl().CreateResourceControl(resourceControl) if err != nil { @@ -158,7 +159,7 @@ func (transport *Transport) applyAccessControlOnResource(parameters *resourceOpe return responseutils.RewriteResponse(response, responseObject, http.StatusOK) } - if executor.operationContext.isAdmin || executor.operationContext.endpointResourceAccess || (resourceControl != nil && portainer.UserCanAccessResource(executor.operationContext.userID, executor.operationContext.userTeamIDs, resourceControl)) { + if executor.operationContext.isAdmin || executor.operationContext.endpointResourceAccess || (resourceControl != nil && authorization.UserCanAccessResource(executor.operationContext.userID, executor.operationContext.userTeamIDs, resourceControl)) { responseObject = decorateObject(responseObject, resourceControl) return responseutils.RewriteResponse(response, responseObject, http.StatusOK) } @@ -246,7 +247,7 @@ func (transport *Transport) filterResourceList(parameters *resourceOperationPara continue } - if context.isAdmin || context.endpointResourceAccess || portainer.UserCanAccessResource(context.userID, context.userTeamIDs, resourceControl) { + if context.isAdmin || context.endpointResourceAccess || authorization.UserCanAccessResource(context.userID, context.userTeamIDs, resourceControl) { resourceObject = decorateObject(resourceObject, resourceControl) filteredResourceData = append(filteredResourceData, resourceObject) } @@ -256,7 +257,7 @@ func (transport *Transport) filterResourceList(parameters *resourceOperationPara } func (transport *Transport) findResourceControl(resourceIdentifier string, resourceType portainer.ResourceControlType, resourceLabelsObject map[string]interface{}, resourceControls []portainer.ResourceControl) (*portainer.ResourceControl, error) { - resourceControl := portainer.GetResourceControlByResourceIDAndType(resourceIdentifier, resourceType, resourceControls) + resourceControl := authorization.GetResourceControlByResourceIDAndType(resourceIdentifier, resourceType, resourceControls) if resourceControl != nil { return resourceControl, nil } @@ -264,7 +265,7 @@ func (transport *Transport) findResourceControl(resourceIdentifier string, resou if resourceLabelsObject != nil { if resourceLabelsObject[resourceLabelForDockerServiceID] != nil { inheritedServiceIdentifier := resourceLabelsObject[resourceLabelForDockerServiceID].(string) - resourceControl = portainer.GetResourceControlByResourceIDAndType(inheritedServiceIdentifier, portainer.ServiceResourceControl, resourceControls) + resourceControl = authorization.GetResourceControlByResourceIDAndType(inheritedServiceIdentifier, portainer.ServiceResourceControl, resourceControls) if resourceControl != nil { return resourceControl, nil @@ -273,7 +274,7 @@ func (transport *Transport) findResourceControl(resourceIdentifier string, resou if resourceLabelsObject[resourceLabelForDockerSwarmStackName] != nil { inheritedSwarmStackIdentifier := resourceLabelsObject[resourceLabelForDockerSwarmStackName].(string) - resourceControl = portainer.GetResourceControlByResourceIDAndType(inheritedSwarmStackIdentifier, portainer.StackResourceControl, resourceControls) + resourceControl = authorization.GetResourceControlByResourceIDAndType(inheritedSwarmStackIdentifier, portainer.StackResourceControl, resourceControls) if resourceControl != nil { return resourceControl, nil @@ -282,7 +283,7 @@ func (transport *Transport) findResourceControl(resourceIdentifier string, resou if resourceLabelsObject[resourceLabelForDockerComposeStackName] != nil { inheritedComposeStackIdentifier := resourceLabelsObject[resourceLabelForDockerComposeStackName].(string) - resourceControl = portainer.GetResourceControlByResourceIDAndType(inheritedComposeStackIdentifier, portainer.StackResourceControl, resourceControls) + resourceControl = authorization.GetResourceControlByResourceIDAndType(inheritedComposeStackIdentifier, portainer.StackResourceControl, resourceControls) if resourceControl != nil { return resourceControl, nil diff --git a/api/http/proxy/factory/docker/configs.go b/api/http/proxy/factory/docker/configs.go index 540bbdef9..0fee44bbc 100644 --- a/api/http/proxy/factory/docker/configs.go +++ b/api/http/proxy/factory/docker/configs.go @@ -8,6 +8,7 @@ import ( portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/proxy/factory/responseutils" + "github.com/portainer/portainer/api/internal/authorization" ) const ( @@ -22,7 +23,7 @@ func getInheritedResourceControlFromConfigLabels(dockerClient *client.Client, co swarmStackName := config.Spec.Labels[resourceLabelForDockerSwarmStackName] if swarmStackName != "" { - return portainer.GetResourceControlByResourceIDAndType(swarmStackName, portainer.StackResourceControl, resourceControls), nil + return authorization.GetResourceControlByResourceIDAndType(swarmStackName, portainer.StackResourceControl, resourceControls), nil } return nil, nil diff --git a/api/http/proxy/factory/docker/containers.go b/api/http/proxy/factory/docker/containers.go index c0587d9c8..dfda0b04a 100644 --- a/api/http/proxy/factory/docker/containers.go +++ b/api/http/proxy/factory/docker/containers.go @@ -7,6 +7,7 @@ import ( "github.com/docker/docker/client" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/proxy/factory/responseutils" + "github.com/portainer/portainer/api/internal/authorization" ) const ( @@ -21,7 +22,7 @@ func getInheritedResourceControlFromContainerLabels(dockerClient *client.Client, serviceName := container.Config.Labels[resourceLabelForDockerServiceID] if serviceName != "" { - serviceResourceControl := portainer.GetResourceControlByResourceIDAndType(serviceName, portainer.ServiceResourceControl, resourceControls) + serviceResourceControl := authorization.GetResourceControlByResourceIDAndType(serviceName, portainer.ServiceResourceControl, resourceControls) if serviceResourceControl != nil { return serviceResourceControl, nil } @@ -29,12 +30,12 @@ func getInheritedResourceControlFromContainerLabels(dockerClient *client.Client, swarmStackName := container.Config.Labels[resourceLabelForDockerSwarmStackName] if swarmStackName != "" { - return portainer.GetResourceControlByResourceIDAndType(swarmStackName, portainer.StackResourceControl, resourceControls), nil + return authorization.GetResourceControlByResourceIDAndType(swarmStackName, portainer.StackResourceControl, resourceControls), nil } composeStackName := container.Config.Labels[resourceLabelForDockerComposeStackName] if composeStackName != "" { - return portainer.GetResourceControlByResourceIDAndType(composeStackName, portainer.StackResourceControl, resourceControls), nil + return authorization.GetResourceControlByResourceIDAndType(composeStackName, portainer.StackResourceControl, resourceControls), nil } return nil, nil diff --git a/api/http/proxy/factory/docker/networks.go b/api/http/proxy/factory/docker/networks.go index c50b716e2..4c3c06d48 100644 --- a/api/http/proxy/factory/docker/networks.go +++ b/api/http/proxy/factory/docker/networks.go @@ -10,6 +10,7 @@ import ( "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/proxy/factory/responseutils" + "github.com/portainer/portainer/api/internal/authorization" ) const ( @@ -25,7 +26,7 @@ func getInheritedResourceControlFromNetworkLabels(dockerClient *client.Client, n swarmStackName := network.Labels[resourceLabelForDockerSwarmStackName] if swarmStackName != "" { - return portainer.GetResourceControlByResourceIDAndType(swarmStackName, portainer.StackResourceControl, resourceControls), nil + return authorization.GetResourceControlByResourceIDAndType(swarmStackName, portainer.StackResourceControl, resourceControls), nil } return nil, nil @@ -85,7 +86,7 @@ func findSystemNetworkResourceControl(networkObject map[string]interface{}) *por networkName := networkObject[networkObjectName].(string) if networkName == "bridge" || networkName == "host" || networkName == "none" { - return portainer.NewSystemResourceControl(networkID, portainer.NetworkResourceControl) + return authorization.NewSystemResourceControl(networkID, portainer.NetworkResourceControl) } return nil diff --git a/api/http/proxy/factory/docker/secrets.go b/api/http/proxy/factory/docker/secrets.go index 522597bdb..b57627d8d 100644 --- a/api/http/proxy/factory/docker/secrets.go +++ b/api/http/proxy/factory/docker/secrets.go @@ -8,6 +8,7 @@ import ( portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/proxy/factory/responseutils" + "github.com/portainer/portainer/api/internal/authorization" ) const ( @@ -22,7 +23,7 @@ func getInheritedResourceControlFromSecretLabels(dockerClient *client.Client, se swarmStackName := secret.Spec.Labels[resourceLabelForDockerSwarmStackName] if swarmStackName != "" { - return portainer.GetResourceControlByResourceIDAndType(swarmStackName, portainer.StackResourceControl, resourceControls), nil + return authorization.GetResourceControlByResourceIDAndType(swarmStackName, portainer.StackResourceControl, resourceControls), nil } return nil, nil diff --git a/api/http/proxy/factory/docker/services.go b/api/http/proxy/factory/docker/services.go index eb1aa6e80..8863ea3fd 100644 --- a/api/http/proxy/factory/docker/services.go +++ b/api/http/proxy/factory/docker/services.go @@ -9,6 +9,7 @@ import ( "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/proxy/factory/responseutils" + "github.com/portainer/portainer/api/internal/authorization" ) const ( @@ -23,7 +24,7 @@ func getInheritedResourceControlFromServiceLabels(dockerClient *client.Client, s swarmStackName := service.Spec.Labels[resourceLabelForDockerSwarmStackName] if swarmStackName != "" { - return portainer.GetResourceControlByResourceIDAndType(swarmStackName, portainer.StackResourceControl, resourceControls), nil + return authorization.GetResourceControlByResourceIDAndType(swarmStackName, portainer.StackResourceControl, resourceControls), nil } return nil, nil diff --git a/api/http/proxy/factory/docker/transport.go b/api/http/proxy/factory/docker/transport.go index 2456cc860..4c12fb93b 100644 --- a/api/http/proxy/factory/docker/transport.go +++ b/api/http/proxy/factory/docker/transport.go @@ -10,12 +10,12 @@ import ( "regexp" "strings" - "github.com/portainer/portainer/api/docker" - "github.com/docker/docker/client" - portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/docker" "github.com/portainer/portainer/api/http/proxy/factory/responseutils" "github.com/portainer/portainer/api/http/security" + "github.com/portainer/portainer/api/internal/authorization" ) var apiVersionRe = regexp.MustCompile(`(/v[0-9]\.[0-9]*)?`) @@ -462,7 +462,7 @@ func (transport *Transport) restrictedResourceOperation(request *http.Request, r return nil, err } - resourceControl := portainer.GetResourceControlByResourceIDAndType(resourceID, resourceType, resourceControls) + resourceControl := authorization.GetResourceControlByResourceIDAndType(resourceID, resourceType, resourceControls) if resourceControl == nil { agentTargetHeader := request.Header.Get(portainer.PortainerAgentTargetHeader) @@ -473,12 +473,12 @@ func (transport *Transport) restrictedResourceOperation(request *http.Request, r return nil, err } - if inheritedResourceControl == nil || !portainer.UserCanAccessResource(tokenData.ID, userTeamIDs, inheritedResourceControl) { + if inheritedResourceControl == nil || !authorization.UserCanAccessResource(tokenData.ID, userTeamIDs, inheritedResourceControl) { return responseutils.WriteAccessDeniedResponse() } } - if resourceControl != nil && !portainer.UserCanAccessResource(tokenData.ID, userTeamIDs, resourceControl) { + if resourceControl != nil && !authorization.UserCanAccessResource(tokenData.ID, userTeamIDs, resourceControl) { return responseutils.WriteAccessDeniedResponse() } } diff --git a/api/http/proxy/factory/docker/volumes.go b/api/http/proxy/factory/docker/volumes.go index 5727d0157..a4b5002ef 100644 --- a/api/http/proxy/factory/docker/volumes.go +++ b/api/http/proxy/factory/docker/volumes.go @@ -7,9 +7,10 @@ import ( "github.com/docker/docker/client" - portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/proxy/factory/responseutils" "github.com/portainer/portainer/api/http/security" + "github.com/portainer/portainer/api/internal/authorization" ) const ( @@ -24,7 +25,7 @@ func getInheritedResourceControlFromVolumeLabels(dockerClient *client.Client, vo swarmStackName := volume.Labels[resourceLabelForDockerSwarmStackName] if swarmStackName != "" { - return portainer.GetResourceControlByResourceIDAndType(swarmStackName, portainer.StackResourceControl, resourceControls), nil + return authorization.GetResourceControlByResourceIDAndType(swarmStackName, portainer.StackResourceControl, resourceControls), nil } return nil, nil diff --git a/api/http/server.go b/api/http/server.go index 1834c4100..11987901d 100644 --- a/api/http/server.go +++ b/api/http/server.go @@ -38,6 +38,7 @@ import ( "github.com/portainer/portainer/api/http/handler/websocket" "github.com/portainer/portainer/api/http/proxy" "github.com/portainer/portainer/api/http/security" + "github.com/portainer/portainer/api/internal/authorization" "net/http" "path/filepath" @@ -73,7 +74,7 @@ type Server struct { func (server *Server) Start() error { proxyManager := proxy.NewManager(server.DataStore, server.SignatureService, server.ReverseTunnelService, server.DockerClientFactory) - authorizationService := portainer.NewAuthorizationService(server.DataStore) + authorizationService := authorization.NewService(server.DataStore) rbacExtensionURL := proxyManager.GetExtensionURL(portainer.RBACExtension) requestBouncer := security.NewRequestBouncer(server.DataStore, server.JWTService, rbacExtensionURL) diff --git a/api/access_control.go b/api/internal/authorization/access_control.go similarity index 66% rename from api/access_control.go rename to api/internal/authorization/access_control.go index 7c767ce03..9879799ac 100644 --- a/api/access_control.go +++ b/api/internal/authorization/access_control.go @@ -1,19 +1,21 @@ -package portainer +package authorization + +import "github.com/portainer/portainer/api" // NewPrivateResourceControl will create a new private resource control associated to the resource specified by the // identifier and type parameters. It automatically assigns it to the user specified by the userID parameter. -func NewPrivateResourceControl(resourceIdentifier string, resourceType ResourceControlType, userID UserID) *ResourceControl { - return &ResourceControl{ +func NewPrivateResourceControl(resourceIdentifier string, resourceType portainer.ResourceControlType, userID portainer.UserID) *portainer.ResourceControl { + return &portainer.ResourceControl{ Type: resourceType, ResourceID: resourceIdentifier, SubResourceIDs: []string{}, - UserAccesses: []UserResourceAccess{ + UserAccesses: []portainer.UserResourceAccess{ { UserID: userID, - AccessLevel: ReadWriteAccessLevel, + AccessLevel: portainer.ReadWriteAccessLevel, }, }, - TeamAccesses: []TeamResourceAccess{}, + TeamAccesses: []portainer.TeamResourceAccess{}, AdministratorsOnly: false, Public: false, System: false, @@ -22,13 +24,13 @@ func NewPrivateResourceControl(resourceIdentifier string, resourceType ResourceC // NewSystemResourceControl will create a new public resource control with the System flag set to true. // These kind of resource control are not persisted and are created on the fly by the Portainer API. -func NewSystemResourceControl(resourceIdentifier string, resourceType ResourceControlType) *ResourceControl { - return &ResourceControl{ +func NewSystemResourceControl(resourceIdentifier string, resourceType portainer.ResourceControlType) *portainer.ResourceControl { + return &portainer.ResourceControl{ Type: resourceType, ResourceID: resourceIdentifier, SubResourceIDs: []string{}, - UserAccesses: []UserResourceAccess{}, - TeamAccesses: []TeamResourceAccess{}, + UserAccesses: []portainer.UserResourceAccess{}, + TeamAccesses: []portainer.TeamResourceAccess{}, AdministratorsOnly: false, Public: true, System: true, @@ -36,13 +38,13 @@ func NewSystemResourceControl(resourceIdentifier string, resourceType ResourceCo } // NewPublicResourceControl will create a new public resource control. -func NewPublicResourceControl(resourceIdentifier string, resourceType ResourceControlType) *ResourceControl { - return &ResourceControl{ +func NewPublicResourceControl(resourceIdentifier string, resourceType portainer.ResourceControlType) *portainer.ResourceControl { + return &portainer.ResourceControl{ Type: resourceType, ResourceID: resourceIdentifier, SubResourceIDs: []string{}, - UserAccesses: []UserResourceAccess{}, - TeamAccesses: []TeamResourceAccess{}, + UserAccesses: []portainer.UserResourceAccess{}, + TeamAccesses: []portainer.TeamResourceAccess{}, AdministratorsOnly: false, Public: true, System: false, @@ -50,29 +52,29 @@ func NewPublicResourceControl(resourceIdentifier string, resourceType ResourceCo } // NewRestrictedResourceControl will create a new resource control with user and team accesses restrictions. -func NewRestrictedResourceControl(resourceIdentifier string, resourceType ResourceControlType, userIDs []UserID, teamIDs []TeamID) *ResourceControl { - userAccesses := make([]UserResourceAccess, 0) - teamAccesses := make([]TeamResourceAccess, 0) +func NewRestrictedResourceControl(resourceIdentifier string, resourceType portainer.ResourceControlType, userIDs []portainer.UserID, teamIDs []portainer.TeamID) *portainer.ResourceControl { + userAccesses := make([]portainer.UserResourceAccess, 0) + teamAccesses := make([]portainer.TeamResourceAccess, 0) for _, id := range userIDs { - access := UserResourceAccess{ + access := portainer.UserResourceAccess{ UserID: id, - AccessLevel: ReadWriteAccessLevel, + AccessLevel: portainer.ReadWriteAccessLevel, } userAccesses = append(userAccesses, access) } for _, id := range teamIDs { - access := TeamResourceAccess{ + access := portainer.TeamResourceAccess{ TeamID: id, - AccessLevel: ReadWriteAccessLevel, + AccessLevel: portainer.ReadWriteAccessLevel, } teamAccesses = append(teamAccesses, access) } - return &ResourceControl{ + return &portainer.ResourceControl{ Type: resourceType, ResourceID: resourceIdentifier, SubResourceIDs: []string{}, @@ -86,10 +88,10 @@ func NewRestrictedResourceControl(resourceIdentifier string, resourceType Resour // DecorateStacks will iterate through a list of stacks, check for an associated resource control for each // stack and decorate the stack element if a resource control is found. -func DecorateStacks(stacks []Stack, resourceControls []ResourceControl) []Stack { +func DecorateStacks(stacks []portainer.Stack, resourceControls []portainer.ResourceControl) []portainer.Stack { for idx, stack := range stacks { - resourceControl := GetResourceControlByResourceIDAndType(stack.Name, StackResourceControl, resourceControls) + resourceControl := GetResourceControlByResourceIDAndType(stack.Name, portainer.StackResourceControl, resourceControls) if resourceControl != nil { stacks[idx].ResourceControl = resourceControl } @@ -99,11 +101,11 @@ func DecorateStacks(stacks []Stack, resourceControls []ResourceControl) []Stack } // FilterAuthorizedStacks returns a list of decorated stacks filtered through resource control access checks. -func FilterAuthorizedStacks(stacks []Stack, user *User, userTeamIDs []TeamID, rbacEnabled bool) []Stack { - authorizedStacks := make([]Stack, 0) +func FilterAuthorizedStacks(stacks []portainer.Stack, user *portainer.User, userTeamIDs []portainer.TeamID, rbacEnabled bool) []portainer.Stack { + authorizedStacks := make([]portainer.Stack, 0) for _, stack := range stacks { - _, ok := user.EndpointAuthorizations[stack.EndpointID][EndpointResourcesAccess] + _, ok := user.EndpointAuthorizations[stack.EndpointID][portainer.EndpointResourcesAccess] if rbacEnabled && ok { authorizedStacks = append(authorizedStacks, stack) continue @@ -119,7 +121,7 @@ func FilterAuthorizedStacks(stacks []Stack, user *User, userTeamIDs []TeamID, rb // UserCanAccessResource will valide that a user has permissions defined in the specified resource control // based on its identifier and the team(s) he is part of. -func UserCanAccessResource(userID UserID, userTeamIDs []TeamID, resourceControl *ResourceControl) bool { +func UserCanAccessResource(userID portainer.UserID, userTeamIDs []portainer.TeamID, resourceControl *portainer.ResourceControl) bool { for _, authorizedUserAccess := range resourceControl.UserAccesses { if userID == authorizedUserAccess.UserID { return true @@ -139,7 +141,7 @@ func UserCanAccessResource(userID UserID, userTeamIDs []TeamID, resourceControl // GetResourceControlByResourceIDAndType retrieves the first matching resource control in a set of resource controls // based on the specified id and resource type parameters. -func GetResourceControlByResourceIDAndType(resourceID string, resourceType ResourceControlType, resourceControls []ResourceControl) *ResourceControl { +func GetResourceControlByResourceIDAndType(resourceID string, resourceType portainer.ResourceControlType, resourceControls []portainer.ResourceControl) *portainer.ResourceControl { for _, resourceControl := range resourceControls { if resourceID == resourceControl.ResourceID && resourceType == resourceControl.Type { return &resourceControl diff --git a/api/internal/authorization/authorizations.go b/api/internal/authorization/authorizations.go new file mode 100644 index 000000000..682f50a56 --- /dev/null +++ b/api/internal/authorization/authorizations.go @@ -0,0 +1,776 @@ +package authorization + +import "github.com/portainer/portainer/api" + +// Service represents a service used to +// update authorizations associated to a user or team. +type Service struct { + dataStore portainer.DataStore +} + +// NewService returns a point to a new Service instance. +func NewService(dataStore portainer.DataStore) *Service { + return &Service{ + dataStore: dataStore, + } +} + +// DefaultEndpointAuthorizationsForEndpointAdministratorRole returns the default endpoint authorizations +// associated to the endpoint administrator role. +func DefaultEndpointAuthorizationsForEndpointAdministratorRole() portainer.Authorizations { + return map[portainer.Authorization]bool{ + portainer.OperationDockerContainerArchiveInfo: true, + portainer.OperationDockerContainerList: true, + portainer.OperationDockerContainerExport: true, + portainer.OperationDockerContainerChanges: true, + portainer.OperationDockerContainerInspect: true, + portainer.OperationDockerContainerTop: true, + portainer.OperationDockerContainerLogs: true, + portainer.OperationDockerContainerStats: true, + portainer.OperationDockerContainerAttachWebsocket: true, + portainer.OperationDockerContainerArchive: true, + portainer.OperationDockerContainerCreate: true, + portainer.OperationDockerContainerPrune: true, + portainer.OperationDockerContainerKill: true, + portainer.OperationDockerContainerPause: true, + portainer.OperationDockerContainerUnpause: true, + portainer.OperationDockerContainerRestart: true, + portainer.OperationDockerContainerStart: true, + portainer.OperationDockerContainerStop: true, + portainer.OperationDockerContainerWait: true, + portainer.OperationDockerContainerResize: true, + portainer.OperationDockerContainerAttach: true, + portainer.OperationDockerContainerExec: true, + portainer.OperationDockerContainerRename: true, + portainer.OperationDockerContainerUpdate: true, + portainer.OperationDockerContainerPutContainerArchive: true, + portainer.OperationDockerContainerDelete: true, + portainer.OperationDockerImageList: true, + portainer.OperationDockerImageSearch: true, + portainer.OperationDockerImageGetAll: true, + portainer.OperationDockerImageGet: true, + portainer.OperationDockerImageHistory: true, + portainer.OperationDockerImageInspect: true, + portainer.OperationDockerImageLoad: true, + portainer.OperationDockerImageCreate: true, + portainer.OperationDockerImagePrune: true, + portainer.OperationDockerImagePush: true, + portainer.OperationDockerImageTag: true, + portainer.OperationDockerImageDelete: true, + portainer.OperationDockerImageCommit: true, + portainer.OperationDockerImageBuild: true, + portainer.OperationDockerNetworkList: true, + portainer.OperationDockerNetworkInspect: true, + portainer.OperationDockerNetworkCreate: true, + portainer.OperationDockerNetworkConnect: true, + portainer.OperationDockerNetworkDisconnect: true, + portainer.OperationDockerNetworkPrune: true, + portainer.OperationDockerNetworkDelete: true, + portainer.OperationDockerVolumeList: true, + portainer.OperationDockerVolumeInspect: true, + portainer.OperationDockerVolumeCreate: true, + portainer.OperationDockerVolumePrune: true, + portainer.OperationDockerVolumeDelete: true, + portainer.OperationDockerExecInspect: true, + portainer.OperationDockerExecStart: true, + portainer.OperationDockerExecResize: true, + portainer.OperationDockerSwarmInspect: true, + portainer.OperationDockerSwarmUnlockKey: true, + portainer.OperationDockerSwarmInit: true, + portainer.OperationDockerSwarmJoin: true, + portainer.OperationDockerSwarmLeave: true, + portainer.OperationDockerSwarmUpdate: true, + portainer.OperationDockerSwarmUnlock: true, + portainer.OperationDockerNodeList: true, + portainer.OperationDockerNodeInspect: true, + portainer.OperationDockerNodeUpdate: true, + portainer.OperationDockerNodeDelete: true, + portainer.OperationDockerServiceList: true, + portainer.OperationDockerServiceInspect: true, + portainer.OperationDockerServiceLogs: true, + portainer.OperationDockerServiceCreate: true, + portainer.OperationDockerServiceUpdate: true, + portainer.OperationDockerServiceDelete: true, + portainer.OperationDockerSecretList: true, + portainer.OperationDockerSecretInspect: true, + portainer.OperationDockerSecretCreate: true, + portainer.OperationDockerSecretUpdate: true, + portainer.OperationDockerSecretDelete: true, + portainer.OperationDockerConfigList: true, + portainer.OperationDockerConfigInspect: true, + portainer.OperationDockerConfigCreate: true, + portainer.OperationDockerConfigUpdate: true, + portainer.OperationDockerConfigDelete: true, + portainer.OperationDockerTaskList: true, + portainer.OperationDockerTaskInspect: true, + portainer.OperationDockerTaskLogs: true, + portainer.OperationDockerPluginList: true, + portainer.OperationDockerPluginPrivileges: true, + portainer.OperationDockerPluginInspect: true, + portainer.OperationDockerPluginPull: true, + portainer.OperationDockerPluginCreate: true, + portainer.OperationDockerPluginEnable: true, + portainer.OperationDockerPluginDisable: true, + portainer.OperationDockerPluginPush: true, + portainer.OperationDockerPluginUpgrade: true, + portainer.OperationDockerPluginSet: true, + portainer.OperationDockerPluginDelete: true, + portainer.OperationDockerSessionStart: true, + portainer.OperationDockerDistributionInspect: true, + portainer.OperationDockerBuildPrune: true, + portainer.OperationDockerBuildCancel: true, + portainer.OperationDockerPing: true, + portainer.OperationDockerInfo: true, + portainer.OperationDockerVersion: true, + portainer.OperationDockerEvents: true, + portainer.OperationDockerSystem: true, + portainer.OperationDockerUndefined: true, + portainer.OperationDockerAgentPing: true, + portainer.OperationDockerAgentList: true, + portainer.OperationDockerAgentHostInfo: true, + portainer.OperationDockerAgentBrowseDelete: true, + portainer.OperationDockerAgentBrowseGet: true, + portainer.OperationDockerAgentBrowseList: true, + portainer.OperationDockerAgentBrowsePut: true, + portainer.OperationDockerAgentBrowseRename: true, + portainer.OperationDockerAgentUndefined: true, + portainer.OperationPortainerResourceControlCreate: true, + portainer.OperationPortainerResourceControlUpdate: true, + portainer.OperationPortainerStackList: true, + portainer.OperationPortainerStackInspect: true, + portainer.OperationPortainerStackFile: true, + portainer.OperationPortainerStackCreate: true, + portainer.OperationPortainerStackMigrate: true, + portainer.OperationPortainerStackUpdate: true, + portainer.OperationPortainerStackDelete: true, + portainer.OperationPortainerWebsocketExec: true, + portainer.OperationPortainerWebhookList: true, + portainer.OperationPortainerWebhookCreate: true, + portainer.OperationPortainerWebhookDelete: true, + portainer.OperationIntegrationStoridgeAdmin: true, + portainer.EndpointResourcesAccess: true, + } +} + +// DefaultEndpointAuthorizationsForHelpDeskRole returns the default endpoint authorizations +// associated to the helpdesk role. +func DefaultEndpointAuthorizationsForHelpDeskRole(volumeBrowsingAuthorizations bool) portainer.Authorizations { + authorizations := map[portainer.Authorization]bool{ + portainer.OperationDockerContainerArchiveInfo: true, + portainer.OperationDockerContainerList: true, + portainer.OperationDockerContainerChanges: true, + portainer.OperationDockerContainerInspect: true, + portainer.OperationDockerContainerTop: true, + portainer.OperationDockerContainerLogs: true, + portainer.OperationDockerContainerStats: true, + portainer.OperationDockerImageList: true, + portainer.OperationDockerImageSearch: true, + portainer.OperationDockerImageGetAll: true, + portainer.OperationDockerImageGet: true, + portainer.OperationDockerImageHistory: true, + portainer.OperationDockerImageInspect: true, + portainer.OperationDockerNetworkList: true, + portainer.OperationDockerNetworkInspect: true, + portainer.OperationDockerVolumeList: true, + portainer.OperationDockerVolumeInspect: true, + portainer.OperationDockerSwarmInspect: true, + portainer.OperationDockerNodeList: true, + portainer.OperationDockerNodeInspect: true, + portainer.OperationDockerServiceList: true, + portainer.OperationDockerServiceInspect: true, + portainer.OperationDockerServiceLogs: true, + portainer.OperationDockerSecretList: true, + portainer.OperationDockerSecretInspect: true, + portainer.OperationDockerConfigList: true, + portainer.OperationDockerConfigInspect: true, + portainer.OperationDockerTaskList: true, + portainer.OperationDockerTaskInspect: true, + portainer.OperationDockerTaskLogs: true, + portainer.OperationDockerPluginList: true, + portainer.OperationDockerDistributionInspect: true, + portainer.OperationDockerPing: true, + portainer.OperationDockerInfo: true, + portainer.OperationDockerVersion: true, + portainer.OperationDockerEvents: true, + portainer.OperationDockerSystem: true, + portainer.OperationDockerAgentPing: true, + portainer.OperationDockerAgentList: true, + portainer.OperationDockerAgentHostInfo: true, + portainer.OperationPortainerStackList: true, + portainer.OperationPortainerStackInspect: true, + portainer.OperationPortainerStackFile: true, + portainer.OperationPortainerWebhookList: true, + portainer.EndpointResourcesAccess: true, + } + + if volumeBrowsingAuthorizations { + authorizations[portainer.OperationDockerAgentBrowseGet] = true + authorizations[portainer.OperationDockerAgentBrowseList] = true + } + + return authorizations +} + +// DefaultEndpointAuthorizationsForStandardUserRole returns the default endpoint authorizations +// associated to the standard user role. +func DefaultEndpointAuthorizationsForStandardUserRole(volumeBrowsingAuthorizations bool) portainer.Authorizations { + authorizations := map[portainer.Authorization]bool{ + portainer.OperationDockerContainerArchiveInfo: true, + portainer.OperationDockerContainerList: true, + portainer.OperationDockerContainerExport: true, + portainer.OperationDockerContainerChanges: true, + portainer.OperationDockerContainerInspect: true, + portainer.OperationDockerContainerTop: true, + portainer.OperationDockerContainerLogs: true, + portainer.OperationDockerContainerStats: true, + portainer.OperationDockerContainerAttachWebsocket: true, + portainer.OperationDockerContainerArchive: true, + portainer.OperationDockerContainerCreate: true, + portainer.OperationDockerContainerKill: true, + portainer.OperationDockerContainerPause: true, + portainer.OperationDockerContainerUnpause: true, + portainer.OperationDockerContainerRestart: true, + portainer.OperationDockerContainerStart: true, + portainer.OperationDockerContainerStop: true, + portainer.OperationDockerContainerWait: true, + portainer.OperationDockerContainerResize: true, + portainer.OperationDockerContainerAttach: true, + portainer.OperationDockerContainerExec: true, + portainer.OperationDockerContainerRename: true, + portainer.OperationDockerContainerUpdate: true, + portainer.OperationDockerContainerPutContainerArchive: true, + portainer.OperationDockerContainerDelete: true, + portainer.OperationDockerImageList: true, + portainer.OperationDockerImageSearch: true, + portainer.OperationDockerImageGetAll: true, + portainer.OperationDockerImageGet: true, + portainer.OperationDockerImageHistory: true, + portainer.OperationDockerImageInspect: true, + portainer.OperationDockerImageLoad: true, + portainer.OperationDockerImageCreate: true, + portainer.OperationDockerImagePush: true, + portainer.OperationDockerImageTag: true, + portainer.OperationDockerImageDelete: true, + portainer.OperationDockerImageCommit: true, + portainer.OperationDockerImageBuild: true, + portainer.OperationDockerNetworkList: true, + portainer.OperationDockerNetworkInspect: true, + portainer.OperationDockerNetworkCreate: true, + portainer.OperationDockerNetworkConnect: true, + portainer.OperationDockerNetworkDisconnect: true, + portainer.OperationDockerNetworkDelete: true, + portainer.OperationDockerVolumeList: true, + portainer.OperationDockerVolumeInspect: true, + portainer.OperationDockerVolumeCreate: true, + portainer.OperationDockerVolumeDelete: true, + portainer.OperationDockerExecInspect: true, + portainer.OperationDockerExecStart: true, + portainer.OperationDockerExecResize: true, + portainer.OperationDockerSwarmInspect: true, + portainer.OperationDockerSwarmUnlockKey: true, + portainer.OperationDockerSwarmInit: true, + portainer.OperationDockerSwarmJoin: true, + portainer.OperationDockerSwarmLeave: true, + portainer.OperationDockerSwarmUpdate: true, + portainer.OperationDockerSwarmUnlock: true, + portainer.OperationDockerNodeList: true, + portainer.OperationDockerNodeInspect: true, + portainer.OperationDockerNodeUpdate: true, + portainer.OperationDockerNodeDelete: true, + portainer.OperationDockerServiceList: true, + portainer.OperationDockerServiceInspect: true, + portainer.OperationDockerServiceLogs: true, + portainer.OperationDockerServiceCreate: true, + portainer.OperationDockerServiceUpdate: true, + portainer.OperationDockerServiceDelete: true, + portainer.OperationDockerSecretList: true, + portainer.OperationDockerSecretInspect: true, + portainer.OperationDockerSecretCreate: true, + portainer.OperationDockerSecretUpdate: true, + portainer.OperationDockerSecretDelete: true, + portainer.OperationDockerConfigList: true, + portainer.OperationDockerConfigInspect: true, + portainer.OperationDockerConfigCreate: true, + portainer.OperationDockerConfigUpdate: true, + portainer.OperationDockerConfigDelete: true, + portainer.OperationDockerTaskList: true, + portainer.OperationDockerTaskInspect: true, + portainer.OperationDockerTaskLogs: true, + portainer.OperationDockerPluginList: true, + portainer.OperationDockerPluginPrivileges: true, + portainer.OperationDockerPluginInspect: true, + portainer.OperationDockerPluginPull: true, + portainer.OperationDockerPluginCreate: true, + portainer.OperationDockerPluginEnable: true, + portainer.OperationDockerPluginDisable: true, + portainer.OperationDockerPluginPush: true, + portainer.OperationDockerPluginUpgrade: true, + portainer.OperationDockerPluginSet: true, + portainer.OperationDockerPluginDelete: true, + portainer.OperationDockerSessionStart: true, + portainer.OperationDockerDistributionInspect: true, + portainer.OperationDockerBuildPrune: true, + portainer.OperationDockerBuildCancel: true, + portainer.OperationDockerPing: true, + portainer.OperationDockerInfo: true, + portainer.OperationDockerVersion: true, + portainer.OperationDockerEvents: true, + portainer.OperationDockerSystem: true, + portainer.OperationDockerUndefined: true, + portainer.OperationDockerAgentPing: true, + portainer.OperationDockerAgentList: true, + portainer.OperationDockerAgentHostInfo: true, + portainer.OperationDockerAgentUndefined: true, + portainer.OperationPortainerResourceControlUpdate: true, + portainer.OperationPortainerStackList: true, + portainer.OperationPortainerStackInspect: true, + portainer.OperationPortainerStackFile: true, + portainer.OperationPortainerStackCreate: true, + portainer.OperationPortainerStackMigrate: true, + portainer.OperationPortainerStackUpdate: true, + portainer.OperationPortainerStackDelete: true, + portainer.OperationPortainerWebsocketExec: true, + portainer.OperationPortainerWebhookList: true, + portainer.OperationPortainerWebhookCreate: true, + } + + if volumeBrowsingAuthorizations { + authorizations[portainer.OperationDockerAgentBrowseGet] = true + authorizations[portainer.OperationDockerAgentBrowseList] = true + authorizations[portainer.OperationDockerAgentBrowseDelete] = true + authorizations[portainer.OperationDockerAgentBrowsePut] = true + authorizations[portainer.OperationDockerAgentBrowseRename] = true + } + + return authorizations +} + +// DefaultEndpointAuthorizationsForReadOnlyUserRole returns the default endpoint authorizations +// associated to the readonly user role. +func DefaultEndpointAuthorizationsForReadOnlyUserRole(volumeBrowsingAuthorizations bool) portainer.Authorizations { + authorizations := map[portainer.Authorization]bool{ + portainer.OperationDockerContainerArchiveInfo: true, + portainer.OperationDockerContainerList: true, + portainer.OperationDockerContainerChanges: true, + portainer.OperationDockerContainerInspect: true, + portainer.OperationDockerContainerTop: true, + portainer.OperationDockerContainerLogs: true, + portainer.OperationDockerContainerStats: true, + portainer.OperationDockerImageList: true, + portainer.OperationDockerImageSearch: true, + portainer.OperationDockerImageGetAll: true, + portainer.OperationDockerImageGet: true, + portainer.OperationDockerImageHistory: true, + portainer.OperationDockerImageInspect: true, + portainer.OperationDockerNetworkList: true, + portainer.OperationDockerNetworkInspect: true, + portainer.OperationDockerVolumeList: true, + portainer.OperationDockerVolumeInspect: true, + portainer.OperationDockerSwarmInspect: true, + portainer.OperationDockerNodeList: true, + portainer.OperationDockerNodeInspect: true, + portainer.OperationDockerServiceList: true, + portainer.OperationDockerServiceInspect: true, + portainer.OperationDockerServiceLogs: true, + portainer.OperationDockerSecretList: true, + portainer.OperationDockerSecretInspect: true, + portainer.OperationDockerConfigList: true, + portainer.OperationDockerConfigInspect: true, + portainer.OperationDockerTaskList: true, + portainer.OperationDockerTaskInspect: true, + portainer.OperationDockerTaskLogs: true, + portainer.OperationDockerPluginList: true, + portainer.OperationDockerDistributionInspect: true, + portainer.OperationDockerPing: true, + portainer.OperationDockerInfo: true, + portainer.OperationDockerVersion: true, + portainer.OperationDockerEvents: true, + portainer.OperationDockerSystem: true, + portainer.OperationDockerAgentPing: true, + portainer.OperationDockerAgentList: true, + portainer.OperationDockerAgentHostInfo: true, + portainer.OperationPortainerStackList: true, + portainer.OperationPortainerStackInspect: true, + portainer.OperationPortainerStackFile: true, + portainer.OperationPortainerWebhookList: true, + } + + if volumeBrowsingAuthorizations { + authorizations[portainer.OperationDockerAgentBrowseGet] = true + authorizations[portainer.OperationDockerAgentBrowseList] = true + } + + return authorizations +} + +// DefaultPortainerAuthorizations returns the default Portainer authorizations used by non-admin users. +func DefaultPortainerAuthorizations() portainer.Authorizations { + return map[portainer.Authorization]bool{ + portainer.OperationPortainerDockerHubInspect: true, + portainer.OperationPortainerEndpointGroupList: true, + portainer.OperationPortainerEndpointList: true, + portainer.OperationPortainerEndpointInspect: true, + portainer.OperationPortainerEndpointExtensionAdd: true, + portainer.OperationPortainerEndpointExtensionRemove: true, + portainer.OperationPortainerExtensionList: true, + portainer.OperationPortainerMOTD: true, + portainer.OperationPortainerRegistryList: true, + portainer.OperationPortainerRegistryInspect: true, + portainer.OperationPortainerTeamList: true, + portainer.OperationPortainerTemplateList: true, + portainer.OperationPortainerTemplateInspect: true, + portainer.OperationPortainerUserList: true, + portainer.OperationPortainerUserInspect: true, + portainer.OperationPortainerUserMemberships: true, + } +} + +// UpdateVolumeBrowsingAuthorizations will update all the volume browsing authorizations for each role (except endpoint administrator) +// based on the specified removeAuthorizations parameter. If removeAuthorizations is set to true, all +// the authorizations will be dropped for the each role. If removeAuthorizations is set to false, the authorizations +// will be reset based for each role. +func (service Service) UpdateVolumeBrowsingAuthorizations(remove bool) error { + roles, err := service.dataStore.Role().Roles() + if err != nil { + return err + } + + for _, role := range roles { + // all roles except endpoint administrator + if role.ID != portainer.RoleID(1) { + updateRoleVolumeBrowsingAuthorizations(&role, remove) + + err := service.dataStore.Role().UpdateRole(role.ID, &role) + if err != nil { + return err + } + } + } + + return nil +} + +func updateRoleVolumeBrowsingAuthorizations(role *portainer.Role, removeAuthorizations bool) { + if !removeAuthorizations { + delete(role.Authorizations, portainer.OperationDockerAgentBrowseDelete) + delete(role.Authorizations, portainer.OperationDockerAgentBrowseGet) + delete(role.Authorizations, portainer.OperationDockerAgentBrowseList) + delete(role.Authorizations, portainer.OperationDockerAgentBrowsePut) + delete(role.Authorizations, portainer.OperationDockerAgentBrowseRename) + return + } + + role.Authorizations[portainer.OperationDockerAgentBrowseGet] = true + role.Authorizations[portainer.OperationDockerAgentBrowseList] = true + + // Standard-user + if role.ID == portainer.RoleID(3) { + role.Authorizations[portainer.OperationDockerAgentBrowseDelete] = true + role.Authorizations[portainer.OperationDockerAgentBrowsePut] = true + role.Authorizations[portainer.OperationDockerAgentBrowseRename] = true + } +} + +// RemoveTeamAccessPolicies will remove all existing access policies associated to the specified team +func (service *Service) RemoveTeamAccessPolicies(teamID portainer.TeamID) error { + endpoints, err := service.dataStore.Endpoint().Endpoints() + if err != nil { + return err + } + + for _, endpoint := range endpoints { + for policyTeamID := range endpoint.TeamAccessPolicies { + if policyTeamID == teamID { + delete(endpoint.TeamAccessPolicies, policyTeamID) + + err := service.dataStore.Endpoint().UpdateEndpoint(endpoint.ID, &endpoint) + if err != nil { + return err + } + + break + } + } + } + + endpointGroups, err := service.dataStore.EndpointGroup().EndpointGroups() + if err != nil { + return err + } + + for _, endpointGroup := range endpointGroups { + for policyTeamID := range endpointGroup.TeamAccessPolicies { + if policyTeamID == teamID { + delete(endpointGroup.TeamAccessPolicies, policyTeamID) + + err := service.dataStore.EndpointGroup().UpdateEndpointGroup(endpointGroup.ID, &endpointGroup) + if err != nil { + return err + } + + break + } + } + } + + registries, err := service.dataStore.Registry().Registries() + if err != nil { + return err + } + + for _, registry := range registries { + for policyTeamID := range registry.TeamAccessPolicies { + if policyTeamID == teamID { + delete(registry.TeamAccessPolicies, policyTeamID) + + err := service.dataStore.Registry().UpdateRegistry(registry.ID, ®istry) + if err != nil { + return err + } + + break + } + } + } + + return service.UpdateUsersAuthorizations() +} + +// RemoveUserAccessPolicies will remove all existing access policies associated to the specified user +func (service *Service) RemoveUserAccessPolicies(userID portainer.UserID) error { + endpoints, err := service.dataStore.Endpoint().Endpoints() + if err != nil { + return err + } + + for _, endpoint := range endpoints { + for policyUserID := range endpoint.UserAccessPolicies { + if policyUserID == userID { + delete(endpoint.UserAccessPolicies, policyUserID) + + err := service.dataStore.Endpoint().UpdateEndpoint(endpoint.ID, &endpoint) + if err != nil { + return err + } + + break + } + } + } + + endpointGroups, err := service.dataStore.EndpointGroup().EndpointGroups() + if err != nil { + return err + } + + for _, endpointGroup := range endpointGroups { + for policyUserID := range endpointGroup.UserAccessPolicies { + if policyUserID == userID { + delete(endpointGroup.UserAccessPolicies, policyUserID) + + err := service.dataStore.EndpointGroup().UpdateEndpointGroup(endpointGroup.ID, &endpointGroup) + if err != nil { + return err + } + + break + } + } + } + + registries, err := service.dataStore.Registry().Registries() + if err != nil { + return err + } + + for _, registry := range registries { + for policyUserID := range registry.UserAccessPolicies { + if policyUserID == userID { + delete(registry.UserAccessPolicies, policyUserID) + + err := service.dataStore.Registry().UpdateRegistry(registry.ID, ®istry) + if err != nil { + return err + } + + break + } + } + } + + return nil +} + +// UpdateUsersAuthorizations will trigger an update of the authorizations for all the users. +func (service *Service) UpdateUsersAuthorizations() error { + users, err := service.dataStore.User().Users() + if err != nil { + return err + } + + for _, user := range users { + err := service.updateUserAuthorizations(user.ID) + if err != nil { + return err + } + } + + return nil +} + +func (service *Service) updateUserAuthorizations(userID portainer.UserID) error { + user, err := service.dataStore.User().User(userID) + if err != nil { + return err + } + + endpointAuthorizations, err := service.getAuthorizations(user) + if err != nil { + return err + } + + user.EndpointAuthorizations = endpointAuthorizations + + return service.dataStore.User().UpdateUser(userID, user) +} + +func (service *Service) getAuthorizations(user *portainer.User) (portainer.EndpointAuthorizations, error) { + endpointAuthorizations := portainer.EndpointAuthorizations{} + if user.Role == portainer.AdministratorRole { + return endpointAuthorizations, nil + } + + userMemberships, err := service.dataStore.TeamMembership().TeamMembershipsByUserID(user.ID) + if err != nil { + return endpointAuthorizations, err + } + + endpoints, err := service.dataStore.Endpoint().Endpoints() + if err != nil { + return endpointAuthorizations, err + } + + endpointGroups, err := service.dataStore.EndpointGroup().EndpointGroups() + if err != nil { + return endpointAuthorizations, err + } + + roles, err := service.dataStore.Role().Roles() + if err != nil { + return endpointAuthorizations, err + } + + endpointAuthorizations = getUserEndpointAuthorizations(user, endpoints, endpointGroups, roles, userMemberships) + + return endpointAuthorizations, nil +} + +func getUserEndpointAuthorizations(user *portainer.User, endpoints []portainer.Endpoint, endpointGroups []portainer.EndpointGroup, roles []portainer.Role, userMemberships []portainer.TeamMembership) portainer.EndpointAuthorizations { + endpointAuthorizations := make(portainer.EndpointAuthorizations) + + groupUserAccessPolicies := map[portainer.EndpointGroupID]portainer.UserAccessPolicies{} + groupTeamAccessPolicies := map[portainer.EndpointGroupID]portainer.TeamAccessPolicies{} + for _, endpointGroup := range endpointGroups { + groupUserAccessPolicies[endpointGroup.ID] = endpointGroup.UserAccessPolicies + groupTeamAccessPolicies[endpointGroup.ID] = endpointGroup.TeamAccessPolicies + } + + for _, endpoint := range endpoints { + authorizations := getAuthorizationsFromUserEndpointPolicy(user, &endpoint, roles) + if len(authorizations) > 0 { + endpointAuthorizations[endpoint.ID] = authorizations + continue + } + + authorizations = getAuthorizationsFromUserEndpointGroupPolicy(user, &endpoint, roles, groupUserAccessPolicies) + if len(authorizations) > 0 { + endpointAuthorizations[endpoint.ID] = authorizations + continue + } + + authorizations = getAuthorizationsFromTeamEndpointPolicies(userMemberships, &endpoint, roles) + if len(authorizations) > 0 { + endpointAuthorizations[endpoint.ID] = authorizations + continue + } + + authorizations = getAuthorizationsFromTeamEndpointGroupPolicies(userMemberships, &endpoint, roles, groupTeamAccessPolicies) + if len(authorizations) > 0 { + endpointAuthorizations[endpoint.ID] = authorizations + } + } + + return endpointAuthorizations +} + +func getAuthorizationsFromUserEndpointPolicy(user *portainer.User, endpoint *portainer.Endpoint, roles []portainer.Role) portainer.Authorizations { + policyRoles := make([]portainer.RoleID, 0) + + policy, ok := endpoint.UserAccessPolicies[user.ID] + if ok { + policyRoles = append(policyRoles, policy.RoleID) + } + + return getAuthorizationsFromRoles(policyRoles, roles) +} + +func getAuthorizationsFromUserEndpointGroupPolicy(user *portainer.User, endpoint *portainer.Endpoint, roles []portainer.Role, groupAccessPolicies map[portainer.EndpointGroupID]portainer.UserAccessPolicies) portainer.Authorizations { + policyRoles := make([]portainer.RoleID, 0) + + policy, ok := groupAccessPolicies[endpoint.GroupID][user.ID] + if ok { + policyRoles = append(policyRoles, policy.RoleID) + } + + return getAuthorizationsFromRoles(policyRoles, roles) +} + +func getAuthorizationsFromTeamEndpointPolicies(memberships []portainer.TeamMembership, endpoint *portainer.Endpoint, roles []portainer.Role) portainer.Authorizations { + policyRoles := make([]portainer.RoleID, 0) + + for _, membership := range memberships { + policy, ok := endpoint.TeamAccessPolicies[membership.TeamID] + if ok { + policyRoles = append(policyRoles, policy.RoleID) + } + } + + return getAuthorizationsFromRoles(policyRoles, roles) +} + +func getAuthorizationsFromTeamEndpointGroupPolicies(memberships []portainer.TeamMembership, endpoint *portainer.Endpoint, roles []portainer.Role, groupAccessPolicies map[portainer.EndpointGroupID]portainer.TeamAccessPolicies) portainer.Authorizations { + policyRoles := make([]portainer.RoleID, 0) + + for _, membership := range memberships { + policy, ok := groupAccessPolicies[endpoint.GroupID][membership.TeamID] + if ok { + policyRoles = append(policyRoles, policy.RoleID) + } + } + + return getAuthorizationsFromRoles(policyRoles, roles) +} + +func getAuthorizationsFromRoles(roleIdentifiers []portainer.RoleID, roles []portainer.Role) portainer.Authorizations { + var associatedRoles []portainer.Role + + for _, id := range roleIdentifiers { + for _, role := range roles { + if role.ID == id { + associatedRoles = append(associatedRoles, role) + break + } + } + } + + var authorizations portainer.Authorizations + highestPriority := 0 + for _, role := range associatedRoles { + if role.Priority > highestPriority { + highestPriority = role.Priority + authorizations = role.Authorizations + } + } + + return authorizations +} diff --git a/api/internal/edge/edgegroup.go b/api/internal/edge/edgegroup.go new file mode 100644 index 000000000..968d5cff2 --- /dev/null +++ b/api/internal/edge/edgegroup.go @@ -0,0 +1,59 @@ +package edge + +import ( + "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/internal/tag" +) + +// EdgeGroupRelatedEndpoints returns a list of endpoints related to this Edge group +func EdgeGroupRelatedEndpoints(edgeGroup *portainer.EdgeGroup, endpoints []portainer.Endpoint, endpointGroups []portainer.EndpointGroup) []portainer.EndpointID { + if !edgeGroup.Dynamic { + return edgeGroup.Endpoints + } + + endpointIDs := []portainer.EndpointID{} + for _, endpoint := range endpoints { + if endpoint.Type != portainer.EdgeAgentEnvironment { + continue + } + + var endpointGroup portainer.EndpointGroup + for _, group := range endpointGroups { + if endpoint.GroupID == group.ID { + endpointGroup = group + break + } + } + + if edgeGroupRelatedToEndpoint(edgeGroup, &endpoint, &endpointGroup) { + endpointIDs = append(endpointIDs, endpoint.ID) + } + } + + return endpointIDs +} + +// edgeGroupRelatedToEndpoint returns true is edgeGroup is associated with endpoint +func edgeGroupRelatedToEndpoint(edgeGroup *portainer.EdgeGroup, endpoint *portainer.Endpoint, endpointGroup *portainer.EndpointGroup) bool { + if !edgeGroup.Dynamic { + for _, endpointID := range edgeGroup.Endpoints { + if endpoint.ID == endpointID { + return true + } + } + return false + } + + endpointTags := tag.Set(endpoint.TagIDs) + if endpointGroup.TagIDs != nil { + endpointTags = tag.Union(endpointTags, tag.Set(endpointGroup.TagIDs)) + } + edgeGroupTags := tag.Set(edgeGroup.TagIDs) + + if edgeGroup.PartialMatch { + intersection := tag.Intersection(endpointTags, edgeGroupTags) + return len(intersection) != 0 + } + + return tag.Contains(edgeGroupTags, endpointTags) +} diff --git a/api/edgestack.go b/api/internal/edge/edgestack.go similarity index 56% rename from api/edgestack.go rename to api/internal/edge/edgestack.go index 7a3019c5d..6f4094e9d 100644 --- a/api/edgestack.go +++ b/api/internal/edge/edgestack.go @@ -1,13 +1,16 @@ -package portainer +package edge -import "errors" +import ( + "errors" + "github.com/portainer/portainer/api" +) // EdgeStackRelatedEndpoints returns a list of endpoints related to this Edge stack -func EdgeStackRelatedEndpoints(edgeGroupIDs []EdgeGroupID, endpoints []Endpoint, endpointGroups []EndpointGroup, edgeGroups []EdgeGroup) ([]EndpointID, error) { - edgeStackEndpoints := []EndpointID{} +func EdgeStackRelatedEndpoints(edgeGroupIDs []portainer.EdgeGroupID, endpoints []portainer.Endpoint, endpointGroups []portainer.EndpointGroup, edgeGroups []portainer.EdgeGroup) ([]portainer.EndpointID, error) { + edgeStackEndpoints := []portainer.EndpointID{} for _, edgeGroupID := range edgeGroupIDs { - var edgeGroup *EdgeGroup + var edgeGroup *portainer.EdgeGroup for _, group := range edgeGroups { if group.ID == edgeGroupID { diff --git a/api/endpoint.go b/api/internal/edge/endpoint.go similarity index 58% rename from api/endpoint.go rename to api/internal/edge/endpoint.go index 661818da3..99d12bf60 100644 --- a/api/endpoint.go +++ b/api/internal/edge/endpoint.go @@ -1,8 +1,10 @@ -package portainer +package edge + +import "github.com/portainer/portainer/api" // EndpointRelatedEdgeStacks returns a list of Edge stacks related to this Endpoint -func EndpointRelatedEdgeStacks(endpoint *Endpoint, endpointGroup *EndpointGroup, edgeGroups []EdgeGroup, edgeStacks []EdgeStack) []EdgeStackID { - relatedEdgeGroupsSet := map[EdgeGroupID]bool{} +func EndpointRelatedEdgeStacks(endpoint *portainer.Endpoint, endpointGroup *portainer.EndpointGroup, edgeGroups []portainer.EdgeGroup, edgeStacks []portainer.EdgeStack) []portainer.EdgeStackID { + relatedEdgeGroupsSet := map[portainer.EdgeGroupID]bool{} for _, edgeGroup := range edgeGroups { if edgeGroupRelatedToEndpoint(&edgeGroup, endpoint, endpointGroup) { @@ -10,7 +12,7 @@ func EndpointRelatedEdgeStacks(endpoint *Endpoint, endpointGroup *EndpointGroup, } } - relatedEdgeStacks := []EdgeStackID{} + relatedEdgeStacks := []portainer.EdgeStackID{} for _, edgeStack := range edgeStacks { for _, edgeGroupID := range edgeStack.EdgeGroups { if relatedEdgeGroupsSet[edgeGroupID] { diff --git a/api/tag.go b/api/internal/tag/tag.go similarity index 54% rename from api/tag.go rename to api/internal/tag/tag.go index f93c6b547..8dd5ad58b 100644 --- a/api/tag.go +++ b/api/internal/tag/tag.go @@ -1,18 +1,20 @@ -package portainer +package tag -type tagSet map[TagID]bool +import "github.com/portainer/portainer/api" -// TagSet converts an array of ids to a set -func TagSet(tagIDs []TagID) tagSet { - set := map[TagID]bool{} +type tagSet map[portainer.TagID]bool + +// Set converts an array of ids to a set +func Set(tagIDs []portainer.TagID) tagSet { + set := map[portainer.TagID]bool{} for _, tagID := range tagIDs { set[tagID] = true } return set } -// TagIntersection returns a set intersection of the provided sets -func TagIntersection(sets ...tagSet) tagSet { +// Intersection returns a set intersection of the provided sets +func Intersection(sets ...tagSet) tagSet { intersection := tagSet{} if len(sets) == 0 { return intersection @@ -35,8 +37,8 @@ func TagIntersection(sets ...tagSet) tagSet { return intersection } -// TagUnion returns a set union of provided sets -func TagUnion(sets ...tagSet) tagSet { +// Union returns a set union of provided sets +func Union(sets ...tagSet) tagSet { union := tagSet{} for _, set := range sets { for tag := range set { @@ -46,8 +48,8 @@ func TagUnion(sets ...tagSet) tagSet { return union } -// TagContains return true if setA contains setB -func TagContains(setA tagSet, setB tagSet) bool { +// Contains return true if setA contains setB +func Contains(setA tagSet, setB tagSet) bool { containedTags := 0 for tag := range setB { if setA[tag] { @@ -57,8 +59,8 @@ func TagContains(setA tagSet, setB tagSet) bool { return containedTags == len(setA) } -// TagDifference returns the set difference tagsA - tagsB -func TagDifference(setA tagSet, setB tagSet) tagSet { +// Difference returns the set difference tagsA - tagsB +func Difference(setA tagSet, setB tagSet) tagSet { set := tagSet{} for tag := range setA {