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 @@