diff --git a/api/http/handler/resourcecontrols/resourcecontrol_create.go b/api/http/handler/resourcecontrols/resourcecontrol_create.go index e57476ed5..cf943b678 100644 --- a/api/http/handler/resourcecontrols/resourcecontrol_create.go +++ b/api/http/handler/resourcecontrols/resourcecontrol_create.go @@ -78,8 +78,6 @@ func (handler *Handler) resourceControlCreate(w http.ResponseWriter, r *http.Req switch payload.Type { case "container": resourceControlType = portainer.ContainerResourceControl - case "container-group": - resourceControlType = portainer.ContainerGroupResourceControl case "service": resourceControlType = portainer.ServiceResourceControl case "volume": diff --git a/api/http/proxy/factory/azure.go b/api/http/proxy/factory/azure.go index 84c9c6495..27b8a26f8 100644 --- a/api/http/proxy/factory/azure.go +++ b/api/http/proxy/factory/azure.go @@ -8,13 +8,13 @@ import ( "github.com/portainer/portainer/api/http/proxy/factory/azure" ) -func newAzureProxy(endpoint *portainer.Endpoint, dataStore portainer.DataStore) (http.Handler, error) { +func newAzureProxy(endpoint *portainer.Endpoint) (http.Handler, error) { remoteURL, err := url.Parse(azureAPIBaseURL) if err != nil { return nil, err } proxy := newSingleHostReverseProxyWithHostHeader(remoteURL) - proxy.Transport = azure.NewTransport(&endpoint.AzureCredentials, dataStore, endpoint) + proxy.Transport = azure.NewTransport(&endpoint.AzureCredentials) return proxy, nil } diff --git a/api/http/proxy/factory/azure/access_control.go b/api/http/proxy/factory/azure/access_control.go deleted file mode 100644 index 9974bfca1..000000000 --- a/api/http/proxy/factory/azure/access_control.go +++ /dev/null @@ -1,149 +0,0 @@ -package azure - -import ( - "log" - "net/http" - - portainer "github.com/portainer/portainer/api" - "github.com/portainer/portainer/api/http/security" - "github.com/portainer/portainer/api/internal/authorization" -) - -func (transport *Transport) createAzureRequestContext(request *http.Request) (*azureRequestContext, error) { - var err error - - tokenData, err := security.RetrieveTokenData(request) - if err != nil { - return nil, err - } - - resourceControls, err := transport.dataStore.ResourceControl().ResourceControls() - if err != nil { - return nil, err - } - - context := &azureRequestContext{ - isAdmin: true, - userID: tokenData.ID, - resourceControls: resourceControls, - } - - if tokenData.Role != portainer.AdministratorRole { - context.isAdmin = false - - teamMemberships, err := transport.dataStore.TeamMembership().TeamMembershipsByUserID(tokenData.ID) - if err != nil { - return nil, err - } - - userTeamIDs := make([]portainer.TeamID, 0) - for _, membership := range teamMemberships { - userTeamIDs = append(userTeamIDs, membership.TeamID) - } - context.userTeamIDs = userTeamIDs - } - - return context, nil -} - -func decorateObject(object map[string]interface{}, resourceControl *portainer.ResourceControl) map[string]interface{} { - if object["Portainer"] == nil { - object["Portainer"] = make(map[string]interface{}) - } - - portainerMetadata := object["Portainer"].(map[string]interface{}) - portainerMetadata["ResourceControl"] = resourceControl - return object -} - -func (transport *Transport) createPrivateResourceControl( - resourceIdentifier string, - resourceType portainer.ResourceControlType, - userID portainer.UserID) (*portainer.ResourceControl, error) { - - resourceControl := authorization.NewPrivateResourceControl(resourceIdentifier, resourceType, userID) - - err := transport.dataStore.ResourceControl().CreateResourceControl(resourceControl) - if err != nil { - log.Printf("[ERROR] [http,proxy,azure,transport] [message: unable to persist resource control] [resource: %s] [err: %s]", resourceIdentifier, err) - return nil, err - } - - return resourceControl, nil -} - -func (transport *Transport) userCanDeleteContainerGroup(request *http.Request, context *azureRequestContext) bool { - if context.isAdmin { - return true - } - resourceIdentifier := request.URL.Path - resourceControl := transport.findResourceControl(resourceIdentifier, context) - return authorization.UserCanAccessResource(context.userID, context.userTeamIDs, resourceControl) -} - -func (transport *Transport) decorateContainerGroups(containerGroups []interface{}, context *azureRequestContext) []interface{} { - decoratedContainerGroups := make([]interface{}, 0) - - for _, containerGroup := range containerGroups { - containerGroup = transport.decorateContainerGroup(containerGroup.(map[string]interface{}), context) - decoratedContainerGroups = append(decoratedContainerGroups, containerGroup) - } - - return decoratedContainerGroups -} - -func (transport *Transport) decorateContainerGroup(containerGroup map[string]interface{}, context *azureRequestContext) map[string]interface{} { - containerGroupId, ok := containerGroup["id"].(string) - if ok { - resourceControl := transport.findResourceControl(containerGroupId, context) - if resourceControl != nil { - containerGroup = decorateObject(containerGroup, resourceControl) - } - } else { - log.Printf("[WARN] [http,proxy,azure,decorate] [message: unable to find resource id property in container group]") - } - - return containerGroup -} - -func (transport *Transport) filterContainerGroups(containerGroups []interface{}, context *azureRequestContext) []interface{} { - filteredContainerGroups := make([]interface{}, 0) - - for _, containerGroup := range containerGroups { - userCanAccessResource := false - containerGroup := containerGroup.(map[string]interface{}) - portainerObject, ok := containerGroup["Portainer"].(map[string]interface{}) - if ok { - resourceControl, ok := portainerObject["ResourceControl"].(*portainer.ResourceControl) - if ok { - userCanAccessResource = authorization.UserCanAccessResource(context.userID, context.userTeamIDs, resourceControl) - } - } - - if context.isAdmin || userCanAccessResource { - filteredContainerGroups = append(filteredContainerGroups, containerGroup) - } - } - - return filteredContainerGroups -} - -func (transport *Transport) removeResourceControl(containerGroup map[string]interface{}, context *azureRequestContext) error { - containerGroupID, ok := containerGroup["id"].(string) - if ok { - resourceControl := transport.findResourceControl(containerGroupID, context) - if resourceControl != nil { - err := transport.dataStore.ResourceControl().DeleteResourceControl(resourceControl.ID) - return err - } - } else { - log.Printf("[WARN] [http,proxy,azure] [message: missign ID in container group]") - } - - return nil -} - -func (transport *Transport) findResourceControl(containerGroupId string, context *azureRequestContext) *portainer.ResourceControl { - resourceControl := authorization.GetResourceControlByResourceIDAndType(containerGroupId, portainer.ContainerGroupResourceControl, context.resourceControls) - return resourceControl -} diff --git a/api/http/proxy/factory/azure/containergroup.go b/api/http/proxy/factory/azure/containergroup.go deleted file mode 100644 index 4fc0042b9..000000000 --- a/api/http/proxy/factory/azure/containergroup.go +++ /dev/null @@ -1,109 +0,0 @@ -package azure - -import ( - "errors" - "net/http" - - portainer "github.com/portainer/portainer/api" - "github.com/portainer/portainer/api/http/proxy/factory/responseutils" -) - -// proxy for /subscriptions/*/resourceGroups/*/providers/Microsoft.ContainerInstance/containerGroups/* -func (transport *Transport) proxyContainerGroupRequest(request *http.Request) (*http.Response, error) { - switch request.Method { - case http.MethodPut: - return transport.proxyContainerGroupPutRequest(request) - case http.MethodGet: - return transport.proxyContainerGroupGetRequest(request) - case http.MethodDelete: - return transport.proxyContainerGroupDeleteRequest(request) - default: - return http.DefaultTransport.RoundTrip(request) - } -} - -func (transport *Transport) proxyContainerGroupPutRequest(request *http.Request) (*http.Response, error) { - response, err := http.DefaultTransport.RoundTrip(request) - if err != nil { - return response, err - } - - responseObject, err := responseutils.GetResponseAsJSONOBject(response) - if err != nil { - return response, err - } - - containerGroupID, ok := responseObject["id"].(string) - if !ok { - return response, errors.New("Missing container group ID") - } - - context, err := transport.createAzureRequestContext(request) - if err != nil { - return response, err - } - - resourceControl, err := transport.createPrivateResourceControl(containerGroupID, portainer.ContainerGroupResourceControl, context.userID) - if err != nil { - return response, err - } - - responseObject = decorateObject(responseObject, resourceControl) - - err = responseutils.RewriteResponse(response, responseObject, http.StatusOK) - if err != nil { - return response, err - } - - return response, nil -} - -func (transport *Transport) proxyContainerGroupGetRequest(request *http.Request) (*http.Response, error) { - response, err := http.DefaultTransport.RoundTrip(request) - if err != nil { - return response, err - } - - responseObject, err := responseutils.GetResponseAsJSONOBject(response) - if err != nil { - return nil, err - } - - context, err := transport.createAzureRequestContext(request) - if err != nil { - return nil, err - } - - responseObject = transport.decorateContainerGroup(responseObject, context) - - responseutils.RewriteResponse(response, responseObject, http.StatusOK) - - return response, nil -} - -func (transport *Transport) proxyContainerGroupDeleteRequest(request *http.Request) (*http.Response, error) { - context, err := transport.createAzureRequestContext(request) - if err != nil { - return nil, err - } - - if !transport.userCanDeleteContainerGroup(request, context) { - return responseutils.WriteAccessDeniedResponse() - } - - response, err := http.DefaultTransport.RoundTrip(request) - if err != nil { - return response, err - } - - responseObject, err := responseutils.GetResponseAsJSONOBject(response) - if err != nil { - return nil, err - } - - transport.removeResourceControl(responseObject, context) - - responseutils.RewriteResponse(response, responseObject, http.StatusOK) - - return response, nil -} diff --git a/api/http/proxy/factory/azure/containergroups.go b/api/http/proxy/factory/azure/containergroups.go deleted file mode 100644 index b13d0c924..000000000 --- a/api/http/proxy/factory/azure/containergroups.go +++ /dev/null @@ -1,48 +0,0 @@ -package azure - -import ( - "fmt" - "net/http" - - "github.com/portainer/portainer/api/http/proxy/factory/responseutils" -) - -// proxy for /subscriptions/*/providers/Microsoft.ContainerInstance/containerGroups -func (transport *Transport) proxyContainerGroupsRequest(request *http.Request) (*http.Response, error) { - switch request.Method { - case http.MethodGet: - return transport.proxyContainerGroupsGetRequest(request) - default: - return http.DefaultTransport.RoundTrip(request) - } -} - -func (transport *Transport) proxyContainerGroupsGetRequest(request *http.Request) (*http.Response, error) { - response, err := http.DefaultTransport.RoundTrip(request) - if err != nil { - return nil, err - } - - responseObject, err := responseutils.GetResponseAsJSONOBject(response) - if err != nil { - return nil, err - } - - value, ok := responseObject["value"].([]interface{}) - if ok { - context, err := transport.createAzureRequestContext(request) - if err != nil { - return response, err - } - - decoratedValue := transport.decorateContainerGroups(value, context) - filteredValue := transport.filterContainerGroups(decoratedValue, context) - responseObject["value"] = filteredValue - - responseutils.RewriteResponse(response, responseObject, http.StatusOK) - } else { - return nil, fmt.Errorf("The container groups response has no value property") - } - - return response, nil -} \ No newline at end of file diff --git a/api/http/proxy/factory/azure/transport.go b/api/http/proxy/factory/azure/transport.go index d6ed7de7a..0c8505c8b 100644 --- a/api/http/proxy/factory/azure/transport.go +++ b/api/http/proxy/factory/azure/transport.go @@ -5,7 +5,7 @@ import ( "strconv" "sync" "time" - "path" + "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/client" ) @@ -21,50 +21,26 @@ type ( client *client.HTTPClient token *azureAPIToken mutex sync.Mutex - dataStore portainer.DataStore - endpoint *portainer.Endpoint - } - - azureRequestContext struct { - isAdmin bool - userID portainer.UserID - userTeamIDs []portainer.TeamID - resourceControls []portainer.ResourceControl } ) // NewTransport returns a pointer to a new instance of Transport that implements the HTTP Transport // interface for proxying requests to the Azure API. -func NewTransport(credentials *portainer.AzureCredentials, dataStore portainer.DataStore, endpoint *portainer.Endpoint) *Transport { +func NewTransport(credentials *portainer.AzureCredentials) *Transport { return &Transport{ credentials: credentials, client: client.NewHTTPClient(), - dataStore: dataStore, - endpoint: endpoint, } } // RoundTrip is the implementation of the the http.RoundTripper interface func (transport *Transport) RoundTrip(request *http.Request) (*http.Response, error) { - return transport.proxyAzureRequest(request) -} - -func (transport *Transport) proxyAzureRequest(request *http.Request) (*http.Response, error) { - requestPath := request.URL.Path - err := transport.retrieveAuthenticationToken() if err != nil { return nil, err } request.Header.Set("Authorization", "Bearer "+transport.token.value) - - if match, _ := path.Match(portainer.AzurePathContainerGroups, requestPath); match { - return transport.proxyContainerGroupsRequest(request) - } else if match, _ := path.Match(portainer.AzurePathContainerGroup, requestPath); match { - return transport.proxyContainerGroupRequest(request) - } - return http.DefaultTransport.RoundTrip(request) } diff --git a/api/http/proxy/factory/factory.go b/api/http/proxy/factory/factory.go index 1e6f83d40..dd4e62440 100644 --- a/api/http/proxy/factory/factory.go +++ b/api/http/proxy/factory/factory.go @@ -55,7 +55,7 @@ func (factory *ProxyFactory) NewLegacyExtensionProxy(extensionAPIURL string) (ht func (factory *ProxyFactory) NewEndpointProxy(endpoint *portainer.Endpoint) (http.Handler, error) { switch endpoint.Type { case portainer.AzureEnvironment: - return newAzureProxy(endpoint, factory.dataStore) + return newAzureProxy(endpoint) case portainer.EdgeAgentOnKubernetesEnvironment, portainer.AgentOnKubernetesEnvironment, portainer.KubernetesLocalEnvironment: return factory.newKubernetesProxy(endpoint) } diff --git a/api/http/proxy/factory/responseutils/response.go b/api/http/proxy/factory/responseutils/response.go index a32cd3252..9f5870810 100644 --- a/api/http/proxy/factory/responseutils/response.go +++ b/api/http/proxy/factory/responseutils/response.go @@ -2,7 +2,6 @@ package responseutils import ( "bytes" - "compress/gzip" "encoding/json" "errors" "io/ioutil" @@ -49,21 +48,13 @@ func getResponseBodyAsGenericJSON(response *http.Response) (interface{}, error) return nil, errors.New("unable to parse response: empty response body") } - reader := response.Body - - if response.Header.Get("Content-Encoding") == "gzip" { - response.Header.Del("Content-Encoding") - gzipReader, err := gzip.NewReader(response.Body) - if err != nil { - return nil, err - } - reader = gzipReader + var data interface{} + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return nil, err } - defer reader.Close() - - var data interface{} - body, err := ioutil.ReadAll(reader) + err = response.Body.Close() if err != nil { return nil, err } diff --git a/api/internal/authorization/access_control.go b/api/internal/authorization/access_control.go index 5004088db..4f533bffa 100644 --- a/api/internal/authorization/access_control.go +++ b/api/internal/authorization/access_control.go @@ -160,13 +160,9 @@ func FilterAuthorizedCustomTemplates(customTemplates []portainer.CustomTemplate, return authorizedTemplates } -// UserCanAccessResource will valid that a user has permissions defined in the specified resource control +// 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 portainer.UserID, userTeamIDs []portainer.TeamID, resourceControl *portainer.ResourceControl) bool { - if resourceControl == nil { - return false - } - for _, authorizedUserAccess := range resourceControl.UserAccesses { if userID == authorizedUserAccess.UserID { return true diff --git a/api/portainer.go b/api/portainer.go index bd0fc0015..ea0711372 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -1497,8 +1497,6 @@ const ( ConfigResourceControl // CustomTemplateResourceControl represents a resource control associated to a custom template CustomTemplateResourceControl - // ContainerGroupResourceControl represents a resource control associated to an Azure container group - ContainerGroupResourceControl ) const ( @@ -1775,8 +1773,3 @@ const ( EndpointResourcesAccess Authorization = "EndpointResourcesAccess" ) - -const ( - AzurePathContainerGroups = "/subscriptions/*/providers/Microsoft.ContainerInstance/containerGroups" - AzurePathContainerGroup = "/subscriptions/*/resourceGroups/*/providers/Microsoft.ContainerInstance/containerGroups/*" -) diff --git a/app/azure/components/datatables/containergroups-datatable/containerGroupsDatatable.html b/app/azure/components/datatables/containergroups-datatable/containerGroupsDatatable.html index 62fa95acf..da68fd7a0 100644 --- a/app/azure/components/datatables/containergroups-datatable/containerGroupsDatatable.html +++ b/app/azure/components/datatables/containergroups-datatable/containerGroupsDatatable.html @@ -49,13 +49,6 @@ Published Ports - - - Ownership - - - - @@ -77,12 +70,6 @@ - - - - - {{ item.ResourceControl.Ownership ? item.ResourceControl.Ownership : item.ResourceControl.Ownership = $ctrl.RCO.ADMINISTRATORS }} - - Loading... diff --git a/app/azure/components/datatables/containergroups-datatable/containerGroupsDatatable.js b/app/azure/components/datatables/containergroups-datatable/containerGroupsDatatable.js index 22f77137c..8d91518a9 100644 --- a/app/azure/components/datatables/containergroups-datatable/containerGroupsDatatable.js +++ b/app/azure/components/datatables/containergroups-datatable/containerGroupsDatatable.js @@ -2,7 +2,7 @@ angular.module('portainer.azure').component('containergroupsDatatable', { templateUrl: './containerGroupsDatatable.html', controller: 'GenericDatatableController', bindings: { - titleText: '@', + title: '@', titleIcon: '@', dataset: '<', tableKey: '@', diff --git a/app/azure/models/container_group.js b/app/azure/models/container_group.js index 1c14f2d0d..af2871c92 100644 --- a/app/azure/models/container_group.js +++ b/app/azure/models/container_group.js @@ -1,6 +1,3 @@ -import { AccessControlFormData } from 'Portainer/components/accessControlForm/porAccessControlFormModel'; -import { ResourceControlViewModel } from 'Portainer/models/resourceControl/resourceControl'; - export function ContainerGroupDefaultModel() { this.Location = ''; this.OSType = 'Linux'; @@ -16,7 +13,6 @@ export function ContainerGroupDefaultModel() { ]; this.CPU = 1; this.Memory = 1; - this.AccessControlData = new AccessControlFormData(); } export function ContainerGroupViewModel(data) { @@ -34,10 +30,6 @@ export function ContainerGroupViewModel(data) { this.AllocatePublicIP = data.properties.ipAddress.type === 'Public'; this.CPU = container.properties.resources.requests.cpu; this.Memory = container.properties.resources.requests.memoryInGB; - - if (data.Portainer && data.Portainer.ResourceControl) { - this.ResourceControl = new ResourceControlViewModel(data.Portainer.ResourceControl); - } } export function CreateContainerGroupRequest(model) { diff --git a/app/azure/views/containerinstances/container-instance-details/containerInstanceDetails.html b/app/azure/views/containerinstances/container-instance-details/containerInstanceDetails.html index 1b564bd9b..37940a149 100644 --- a/app/azure/views/containerinstances/container-instance-details/containerInstanceDetails.html +++ b/app/azure/views/containerinstances/container-instance-details/containerInstanceDetails.html @@ -131,8 +131,4 @@ - - - - diff --git a/app/azure/views/containerinstances/create/createContainerInstanceController.js b/app/azure/views/containerinstances/create/createContainerInstanceController.js index 7d73a4477..cc42380d5 100644 --- a/app/azure/views/containerinstances/create/createContainerInstanceController.js +++ b/app/azure/views/containerinstances/create/createContainerInstanceController.js @@ -6,9 +6,7 @@ angular.module('portainer.azure').controller('AzureCreateContainerInstanceContro '$state', 'AzureService', 'Notifications', - 'Authentication', - 'ResourceControlService', - function ($q, $scope, $state, AzureService, Notifications, Authentication, ResourceControlService) { + function ($q, $scope, $state, AzureService, Notifications) { var allResourceGroups = []; var allProviders = []; @@ -44,12 +42,12 @@ angular.module('portainer.azure').controller('AzureCreateContainerInstanceContro $scope.state.actionInProgress = true; AzureService.createContainerGroup(model, subscriptionId, resourceGroupName) - .then(applyResourceControl) - .then(() => { + .then(function success() { Notifications.success('Container successfully created', model.Name); $state.go('azure.containerinstances'); }) .catch(function error(err) { + err = err.data ? err.data.error : err; Notifications.error('Failure', err, 'Unable to create container'); }) .finally(function final() { @@ -57,14 +55,6 @@ angular.module('portainer.azure').controller('AzureCreateContainerInstanceContro }); }; - function applyResourceControl(newResourceGroup) { - const userId = Authentication.getUserDetails().ID; - const resourceControl = newResourceGroup.Portainer.ResourceControl; - const accessControlData = $scope.model.AccessControlData; - - return ResourceControlService.applyResourceControl(userId, accessControlData, resourceControl); - } - function validateForm(model) { if (!model.Ports || !model.Ports.length || model.Ports.every((port) => !port.host || !port.container)) { return 'At least one port binding is required'; @@ -83,7 +73,7 @@ angular.module('portainer.azure').controller('AzureCreateContainerInstanceContro } function initView() { - $scope.model = new ContainerGroupDefaultModel(); + var model = new ContainerGroupDefaultModel(); AzureService.subscriptions() .then(function success(data) { @@ -103,6 +93,8 @@ angular.module('portainer.azure').controller('AzureCreateContainerInstanceContro var containerInstancesProviders = data.containerInstancesProviders; allProviders = containerInstancesProviders; + $scope.model = model; + var selectedSubscription = $scope.state.selectedSubscription; updateResourceGroupsAndLocations(selectedSubscription, resourceGroups, containerInstancesProviders); }) diff --git a/app/azure/views/containerinstances/create/createcontainerinstance.html b/app/azure/views/containerinstances/create/createcontainerinstance.html index 1e1d76424..477fb845a 100644 --- a/app/azure/views/containerinstances/create/createcontainerinstance.html +++ b/app/azure/views/containerinstances/create/createcontainerinstance.html @@ -157,9 +157,6 @@ - - -
Actions diff --git a/app/portainer/models/resourceControl/resourceControlTypes.js b/app/portainer/models/resourceControl/resourceControlTypes.js index 137ee52a2..714b7dc02 100644 --- a/app/portainer/models/resourceControl/resourceControlTypes.js +++ b/app/portainer/models/resourceControl/resourceControlTypes.js @@ -7,7 +7,6 @@ export const ResourceControlTypeString = Object.freeze({ STACK: 'stack', VOLUME: 'volume', CUSTOM_TEMPLATE: 'custom-template', - CONTAINER_GROUP: 'container-group', }); /** @@ -22,5 +21,4 @@ export const ResourceControlTypeInt = Object.freeze({ STACK: 6, CONFIG: 7, CUSTOM_TEMPLATE: 8, - CONTAINER_GROUP: 9, }); diff --git a/app/portainer/services/notifications.js b/app/portainer/services/notifications.js index 2642dcfbf..fdfd85e85 100644 --- a/app/portainer/services/notifications.js +++ b/app/portainer/services/notifications.js @@ -26,8 +26,6 @@ angular.module('portainer.app').factory('Notifications', [ msg = e.data.message; } else if (e.data && e.data.content) { msg = e.data.content; - } else if (e.data && e.data.error) { - msg = e.data.error; } else if (e.message) { msg = e.message; } else if (e.err && e.err.data && e.err.data.length > 0 && e.err.data[0].message) {